前言:作为API函数的调用过程(系统服务表)的学习笔记

前面的一篇笔记中分析了进入0环之后的保存现场,然后这里继续来学习如何调用0环的函数的分析过程

其中还要分析如下两个点

1、如何根据系统服务号(eax中存储)找到要执行的内核函数

2、调用时参数是存储到3环的堆栈,如何传递给内核函数

系统服务表SystemServiceTable

系统服务表是如下图所示,其中分为两个两张系统服务表,一个是指向Ntoskrl.exe相关的内核函数,一个是指向Win32k.sys相关的内核函数

API 系统调用 对内 对外 架构图 api调用流程_系统服务

SystemServiceTable系统服务表的位置

通过_KTHREAD结构体能够找到相关的系统服务表的位置

API 系统调用 对内 对外 架构图 api调用流程_服务号_02

dt _KTHREAD

API 系统调用 对内 对外 架构图 api调用流程_系统服务_03

0环如何判断要调用的函数在哪个表

首先要知道的进入0环之前,eax会保存要调用0环函数的服务号,edx保存调用的地址,如下图所示

API 系统调用 对内 对外 架构图 api调用流程_Windows内核_04

然后在寻找要调用的函数在系统服务表中就需要用到三环传入的eax的服务号,其中要用到的就是低13位,在第13位(下标12位)则是通过0或者1来判断要找的是哪个系统服务表

API 系统调用 对内 对外 架构图 api调用流程_服务号_05

0环如何找到要执行的函数与参数个数

eax中的低12位用在寻找相关要执行的函数和参数的个数,如下图所示

API 系统调用 对内 对外 架构图 api调用流程_Windows内核_06

细节分析学习

比如ReadVirtualMemory传入的eax值为0x0BA,拆分结果为 -> 0000 1011 1010

右移8位的话此时的值就是 -> 0000,再接着跟0x30与运算

00 0000 & 11 0000 结果就是0

如果为0的话那么找到的系统服务表就是跟ntoskrnl.exe相关的

01 0000 & 11 0000 结果就是 0x10

而如果为1的话,那么的系统服务表就是跟gdi32.dll相关的

为什么这么运算呢?其实这个0x10很巧妙的,你可以看下系统服务表的大小正好是0x10,所以说如果是跟gdi32.dll相关的,那么整好跳过0x10个字节,正好就到了第二张系统服务表的位置了

寻找过程

.text:00406932 loc_406932:                             ; CODE XREF: _KiBBTUnexpectedRange+18↑j
.text:00406932                                         ; _KiSystemService+71↑j
.text:00406932                 mov     edi, eax        ; edi = 当前要调用的内核函数的服务号
.text:00406934                 shr     edi, 8          ; edi = 要调用的函数服务号 >> 8
.text:00406937                 and     edi, 30h        ; edi &= 0x30
.text:0040693A                 mov     ecx, edi        ; 上面运算完的结果赋值给ecx
.text:0040693C                 add     edi, [esi+0E0h] ; 获得对应的系统服务表的基址,_KTHREAD+E0
.text:00406942                 mov     ebx, eax        ; ebx = 当前要调用的内核函数的服务号
.text:00406944                 and     eax, 0FFFh      ; eax &= 0xfff
.text:00406949                 cmp     eax, [edi+8]    ; 判断当前要调用的服务号的值是否超过了当前指定系统服务表中的serviceLimit的数量
.text:0040694C                 jnb     _KiBBTUnexpectedRange
.text:00406952                 cmp     ecx, 10h        ; 判断是第一张系统服务表,还是第二张系统服务表
.text:00406955                 jnz     short loc_406972 ; 第二张不跳,第一张跳
.text:00406957                 mov     ecx, large fs:18h
.text:0040695E                 xor     ebx, ebx
.text:00406960
.text:00406960 ; __stdcall KiSystemServiceAccessTeb()
.text:00406960 _KiSystemServiceAccessTeb@0:            ; DATA XREF: KiPreprocessAccessViolation(x,x,x)+3D↓o
.text:00406960                 or      ebx, [ecx+0F70h]
.text:00406966                 jz      short loc_406972
.text:00406968                 push    edx
.text:00406969                 push    eax
.text:0040696A                 call    ds:_KeGdiFlushUserBatch
.text:00406970                 pop     eax
.text:00406971                 pop     edx
.text:00406972
.text:00406972 loc_406972:                             ; CODE XREF: _KiSystemService+184↑j
.text:00406972                                         ; _KiSystemService+195↑j
.text:00406972                 inc     large dword ptr fs:638h ; 找到_KPRCB的KeSystemCalls
.text:00406979                 mov     esi, edx        ; esi保存三环的参数地址
.text:0040697B                 mov     ebx, [edi+0Ch]  ; ebx保存系统服务表中的ArgmentTable字段
.text:0040697E                 xor     ecx, ecx
.text:00406980                 mov     cl, [eax+ebx]   ; 函数的服务号加上ArgmentTable的基址就相当于找到对应函数的参数个数
.text:00406983                 mov     edi, [edi]      ; edi保存系统服务表中的ServiceTable字段
.text:00406985                 mov     ebx, [edi+eax*4] ; ServiceTable+4*函数服务号,相当于找到函数地址表中对应的函数地址
.text:00406988                 sub     esp, ecx        ; ESP提升栈定,大小为对应的参数个数大小,这里的ecx是要执行的参数的字节大小
.text:0040698A                 shr     ecx, 2          ; 参数个数/4,计算如果一次拷贝4字节的话,那么要拷贝的次数
.text:0040698D                 mov     edi, esp        ; edi指向函数参数的位置
.text:0040698F                 test    byte ptr [ebp+72h], 2
.text:00406993                 jnz     short loc_40699B
.text:00406995                 test    byte ptr [ebp+6Ch], 1
.text:00406999                 jz      short _KiSystemServiceCopyArguments@0 ; KiSystemServiceCopyArguments()
.text:0040699B
.text:0040699B loc_40699B:                             ; CODE XREF: _KiSystemService+1C2↑j
.text:0040699B                 cmp     esi, ds:_MmUserProbeAddress
.text:004069A1                 jnb     loc_406B4F
.text:004069A7
.text:004069A7 ; __stdcall KiSystemServiceCopyArguments()
.text:004069A7 _KiSystemServiceCopyArguments@0:        ; CODE XREF: _KiSystemService+1C8↑j
.text:004069A7                                         ; DATA XREF: KiPreprocessAccessViolation(x,x,x):loc_4628DF↓o
.text:004069A7                 rep movsd               ; 将esi中保存的三环的函数参数地址依次拷贝到edi中(esi中准备保存的是0环的函数参数地址)
.text:004069A9                 call    ebx             ; 调用函数地址表中对应的函数