文章目录

  • 前言
  • SystemServiceTable 系统服务表
  • 系统服务表在哪
  • 判断调用的函数在哪个表
  • API函数的调用过程

前言

我们现在已经知道API怎么从三环进入零环,从三环进零环需要带两个寄存器,分别是eax和edx。eax保存的是系统的服务号,edx保存的是三环的esp,通过esp可以找到三环的参数。

这次要解决的问题是如何通过eax找到零环的函数,零环的函数是怎么被调用的,并且零环的函数执行的时候是怎么使用三环的参数的。

SystemServiceTable 系统服务表

在操作系统里,有一张非常重要的表,SystemServiceTable 系统服务表 。系统服务表是操作系统内核的一张表,结构如图:

总共有四个成员:

  1. ServiceTable 这个成员是个地址,通过这个地址可以找到一个函数地址表。从三环进零环的eax的服务号就是函数地址表的索引
  2. count 当前的系统服务表被调用了多少次
  3. ServiceLimit:保存的是函数地址表的大小,也就是服务函数的个数
  4. ArgmentTable:函数参数表,里面保存的是函数地址表对应的参数个数,以字节为单位。例如函数地址表下标为1的函数需要三个参数,那么函数参数表下标为1的内容就是12
  5. 系统服务表有两份,其中一份函数来自于Ntoskrl.exe内核模块的导出函数,里面保存常用的系统服务;另外一份来自Win32k.sys的导出函数,里面是与图形以及用户界面相关的系统服务。向三环提供的内核函数全在这两张表里。

系统服务表在哪

系统服务表位于_KTHREAD结构体0xE0的位置。

判断调用的函数在哪个表

问题在于系统服务表有两张,那么我们怎么判断需要调用的函数在哪个表呢

  • 要找两张表取决于eax系统服务号。这个系统服务号总共有32位,但是真正只使用了13位。
  • 系统服务号在使用的时候分为两部分,低12位表示的是函数地址表的索引,下标为12的位置的值,决定了使用哪张表
  • 如果第12位为0,则找第一张表,如果第12位为1,则找第二张表。

API函数的调用过程

接下来就来分析零环代码是怎么通过服务号找到零环函数,并且零环的函数是如何使用在三环的参数的。

用IDA打开ntkrnlpa.exe,找到KiFastCallEntry函数。KiFastCallEntry和KiSystemService前面的代码都是用于保存现场,大致相同。

我们从保存现场之后开始分析。

.text:0046579D                 mov     edi, eax        ; 取出三环传进来的系统调用号

首先将三环传递过来的系统调用号保存到edi里

.text:0046579F                 shr     edi, 8          ; 系统调用号右移8位
.text:004657A2                 and     edi, 30h        ; 判断调用号的第12是0还是1
.text:004657A5                 mov     ecx, edi        ; ecx存储的值是00或者0x10

然后将系统调用号右移8位,然后再和0x30做与运算。此时edi的结果只能有两种,要么是0x0,要么是0x10。如果是0的话,就说明调用号下标为12的位置是0,如果edi的结果是0x10,那久说明调用号下标为12的位置是1。

.text:004657A7                 add     edi, [esi+0E0h] ; edi指向KTHREAD--->ServiceTable

esi指向的是ETHREAD线程结构体,这个结构体+0xE的位置是ServiceTable系统服务表。用系统服务表直接加上edi的值。如果edi的值是0,加上ServiceTable的基址还是等于原来的值,这个时候就会找第一张系统服务表。

如果edi的值是0x10,再加上ServiceTable的基址指向的刚好是第二张表。因为系统服务表的大小正好是0x10,加上0x10就能找到第二张表。这个地方的算法非常巧妙。

.text:004657AD                 mov     ebx, eax        ; 把三环的系统服务号存到ebx备份
.text:004657AF                 and     eax, 0FFFh      ; 系统调用号 只保留后12位

接着把系统调用号备份到ebx,然后和0FFFh做与运算,保留后12位

.text:004657B4                 cmp     eax, [edi+8]
.text:004657B7                 jnb     _KiBBTUnexpectedRange

这里用eax和edi+0x8的位置做比较,此时的edx指向的是系统服务表,[edi+8]的位置是第三个成员ServiceLimit,是服务函数的个数。如果传入的调用号的低12位大于服务函数的个数,就跳转到_KiBBTUnexpectedRange。

这里是判断要找的函数有没有超过函数地址表的范围。如果没有越界,代码会继续往下走。

.text:004657BD                 cmp     ecx, 10h        ; 判断是否是查第二张系统服务表
.text:004657C0                 jnz     short loc_4657DC ; 如果是查第一张系统服务表则跳转

接着拿ecx和0x10进行比较,ecx的值来自于edi,存储的值是00或者0x10,如果ecx是10的话就说明要查第二张系统服务表。

如果是查第一张系统服务表就会跳转,查第二张则继续往下执行

.text:004657C2                 mov     ecx, ds:0FFDFF018h
.text:004657C8                 xor     ebx, ebx
.text:004657CA
.text:004657CA loc_4657CA:                             ; DATA XREF: _KiTrap0E+113↓o
.text:004657CA                 or      ebx, [ecx+0F70h]
.text:004657D0                 jz      short loc_4657DC
.text:004657D2                 push    edx
.text:004657D3                 push    eax
.text:004657D4                 call    ds:_KeGdiFlushUserBatch
.text:004657DA                 pop     eax
.text:004657DB                 pop     edx

最后会调用_KeGdiFlushUserBatch函数。继续往下分析,假设最后查的是第一张系统服务表

.text:004657DC loc_4657DC:                             ; CODE XREF: _KiFastCallEntry+B0↑j
.text:004657DC                 inc     dword ptr ds:0FFDFF638h ; _KPCRB->0x518 KeSystemCall增加1
.text:004657E2                 mov     esi, edx        ; edx存储的三环的参数指针

这里将edx保存到esi,edx存储的是三环的参数指针

.text:004657E4                 mov     ebx, [edi+0Ch]   ;ebx指向参数表起始位置

此时的edi指向系统服务表的起始位置,[edi+0Ch]存储的是ParamTableBase 参数表的基址,此时ebx指向参数表起始位置。

.text:004657E9                 mov     cl, [eax+ebx]   ; eax->函数地址表索引 ebx->参数表起始位置 cl->参数的个数

参数表的基址加上函数地址表的索引,再取内容得到的值就是要调用的函数的参数个数。

.text:004657EC                 mov     edi, [edi]      ; edi指向系统服务表 第一个成员是函数地址表

接下来取出函数地址表放到edi

.text:004657EE                 mov     ebx, [edi+eax*4] ; ebx->零环的函数地址

接下来用函数地址表edi加上索引eax乘以4,这行代码执行完成之后ebx存储的是零环的函数地址

.text:004657F1                 sub     esp, ecx        ; 提升堆栈 提升高度为CL

接着提升堆栈,提升的大小为CL,也就是参数的大小,为了容纳三环的参数

.text:004657F3                 shr     ecx, 2          ; 参数总长度/4=参数的个数
.text:00465804                 rep movsd               ; 开始拷贝参数

然后将ecx右移两位,右移两位相当于除以4,得到的结果是参数的个数。为什么要右移呢?原因很简单,因为后面的rep串操作指令的循环次数取决于ecx的值,而movsd一次复制4个字节。

.text:004657F8                 cmp     esi, ds:_MmUserProbeAddress ; 判断三环的参数的地址范围是否越界
.text:004657FE                 jnb     loc_4659AC      ; 越界跳转到错误处理模块

此时esi指向的是三环的函数指针,这里跟一个全局变量进行比较,这个全局变量是用户程序能访问地址的最大范围。

这里是为了判断三环的参数的地址范围是否越界,如果越界则跳转到错误处理的模块

.text:00465804                 rep movsd               ; 开始拷贝参数
.text:00465806                 call    ebx             ; 调用函数

最后将三环的参数赋值到零环,开始调用真正的内核函数

系统调用003 系统服务表相关推荐

  1. Windows系统调用学习笔记(四)—— 系统服务表SSDT

    Windows系统调用学习笔记(四)-- 系统服务表&SSDT 要点回顾 系统服务表 实验:分析 KiSystemService 与 KiFastCallEntry 共同代码 SSDT 实验: ...

  2. 4.API的调用过程(系统服务表)

    SystemServiceTable(系统服务表) typedef struct _KSYSTEM_SERVICE_TABLE {PULONG ServiceTableBase; //这个指向系统服务 ...

  3. 《SQL高级应用和数据仓库基础(MySQL版)》学习笔记 ·003【表的约束、表的CRUD操作(DDL语句)】

    文章目录 一.表的约束 1.约束类型 2.添加约束 二.表的CRUD操作(DDL语句) 1.C(Create):创建 2.R(Retrieve):查询 3.U(Update):更新 4.D(Delet ...

  4. 图解SQL面试题 (学习笔记) 003多表查询----2

    这里写目录标题 一级目录 二级目录 三级目录 如何比较日期数据 如何交换数据 滴滴面试题:如何找出最小N个数 一级目录 二级目录 三级目录 如何比较日期数据 [题目] 下面是某公司每天的营业额,表名为 ...

  5. 《学活Linux》第一讲——系统调用和VFS

    <学活LINUX> 第一讲 系统调用和VFS 自确定<学活LINUX>课程详情以来,已有两周时间.相信很多格友们已经准备好GDK8,等待课程的开始.2023年7月22日,上周六 ...

  6. SSDT表与ShadowSSDT表

    实际上内核中存在两个系统服务描述符表,一个是KeServiceDescriptorTable(由ntoskrnl.exe导出):,一个是KeServieDescriptorTableShadow(没有 ...

  7. linux系统调用理解之摘录(2)

    原文博客 http://blog.csdn.net/gatieme/article/details/50779184 Linux系统调用的实现机制分析 本文介绍了系统调用的一些细节. 首先,分析了系统 ...

  8. (52)系统调用阶段测试——基于 SSDT HOOK 的 FindWindowA 监视器

    一.项目说明 SSDT HOOK 内核函数我们已经会了,请看这两篇博客: SSDT HOOK 实现进程保护 补充内容:SSDT HOOK 模板 此次考试和 hook NtOpenProcess 或 N ...

  9. Windows系统调用学习笔记(三)—— 保存现场

    Windows系统调用学习笔记(三)-- 保存现场 要点回顾 基本概念 Trap Frame 结构 线程相关的结构体 ETHREAD KTHREAD CPU相关的结构体 KPCR _NT_TIB KP ...

最新文章

  1. Java案例:生成指定目录下某种类型文件的列表
  2. 消息称《绝地求生》开发商Krafton将启动IPO 腾讯是大股东
  3. 编程范式之rotate操作
  4. 【 Hibernate3-摘要 】 【 第1节 】:JPA常用注解
  5. python的优点有哪些-python语言有什么优势
  6. pytorch 向量转化为one-hot编码
  7. BZOJ2115 [WC2011]最大XOR和路径
  8. Matlab中的c2d函数用法
  9. C++--struct的用法
  10. chrome浏览器F12 Network中Timing参数含义
  11. python爬虫从入门到放弃(一)初识爬虫
  12. 动态水印跟踪去除_视频动态水印如何去除 视频中不定时出现的图片加文字广告如何尽量模糊处理...
  13. 硬件知识:独立显卡和集成显卡的区别
  14. Driver的prob的调用顺序
  15. 施坦威D4三角钢琴-e-instruments Session Keys Grand S Kontakt
  16. 边缘基础设施还差点什么?
  17. 微软面试题:五个囚犯抓绿豆
  18. vlive android app,v live app
  19. 新概念英语第二册课文电子版_新概念英语第二册音频+视频讲解:Lesson 24
  20. 沉没的蜀山---探索东方神话传说中的泰坦尼克

热门文章

  1. 成功解决raise XGBoostError(_LIB.XGBGetLastError()) xgboost.core.XGBoostError: b'[22:08:00] C:\\Users\\Ad
  2. 成功解决ValueError: Parameter values for parameter (n_estimators) need to be a sequence.
  3. ML之DT:基于DT算法对泰坦尼克号乘客数据集进行二分类(是否获救)预测
  4. CV:基于keras利用cv2自带两步检测法对《跑男第六季第五期》之如花片段(或调用摄像头)进行实时性别脸部表情检测
  5. Python中的argparse模块
  6. os_mem.c(全)
  7. MongoDB学习笔记【2】-- 试用
  8. 板邓:wordpress建站不得不知的安全防护(二)
  9. jQuery Event.stopPropagation() 函数详解
  10. 最长公共子序列 nyoj-36