系统调用-KiSystemService分析

admin 2023年11月25日16:01:25评论109 views字数 4939阅读16分27秒阅读模式

3环进0环,不论怎么调用,都绕不过两个函数:KiSystemServiceKiFastCallEntry

因此,我们只需要将这两个函数分析清除了,那么就知道,底层是如何调用的了

;执行之前,我们需要知道ESP指向哪
;INT 0x2E进来,EIP和CS从中断门进来,SS与ESP0从TSS寄存器来
;此时ESP0指向_KTrap_Frame的0x068(EIP的位置)
;又因为栈顶是越来越小的
;前面说了TSS寄存器其实操作系统用的并不多
;而用的最多的则是_KTrap_Frame
;_KTrap_Frame
/*
kd> dt _KTrap_Frame
ntdll!_KTRAP_FRAME
+0x000 DbgEbp : Uint4B
+0x004 DbgEip : Uint4B
+0x008 DbgArgMark : Uint4B
+0x00c DbgArgPointer : Uint4B
+0x010 TempSegCs : Uint4B
+0x014 TempEsp : Uint4B
+0x018 Dr0 : Uint4B
+0x01c Dr1 : Uint4B
+0x020 Dr2 : Uint4B
+0x024 Dr3 : Uint4B
+0x028 Dr6 : Uint4B
+0x02c Dr7 : Uint4B
+0x030 SegGs : Uint4B
+0x034 SegEs : Uint4B
+0x038 SegDs : Uint4B
+0x03c Edx : Uint4B
+0x040 Ecx : Uint4B
+0x044 Eax : Uint4B
+0x048 PreviousPreviousMode : Uint4B
+0x04c ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x050 SegFs : Uint4B
+0x054 Edi : Uint4B
+0x058 Esi : Uint4B
+0x05c Ebx : Uint4B
+0x060 Ebp : Uint4B
+0x064 ErrCode : Uint4B
+0x068 Eip : Uint4B
......略
*/
;在往上是0x064ErrCode,而KiSystemService,并没有得到这个ErrCod
;ErrCode是CPU提供的,非操作系统

arg_0 = dword ptr 4
01:push 0;,对齐操作,上面已经解释清楚了
02:push ebp;02-05:保存现场,将3环的EBP存储到_KTrap_Frame的结构体中
03:push ebx;02-05:保存现场,将3环的EBX存储到_KTrap_Frame
04:push esi;02-05:保存现场,将3环的EBI存储到_KTrap_Frame
05:push edi;02-05:保存现场,将3环的EDI存储到_KTrap_Frame
06:push fs;02-05:保存现场,将3环的FS存储到_KTrap_Frame
07:mov ebx, 30h;把0x30赋值给ebx,注意这里是段选择子
08:mov fs, ebx;在前面介绍过FS在3环为3B,在0环为30,
;0x30:00110 000:
;000:低2位代表RPL,第3位0:查GDT,1查LDT表,因此找GDT表
;00110:代表下标为6,查第6个:ffc093df`f0000001,该段为数据段
;ffc093df`f0000001,该段参考段寄存器的成员属性:得到Base=ffdff000
;也就是FS.Base=ffdff000
;一个核一个KPCR结构体
;查看核数量dd KeNumberProcessors
;查看KPCR地址:dd KiProcessorBlock L2 ->ffdff120
;通过上述查看到的地址-120,即:ffdff120-120=ffdff000,为KPCR的结构体的首地址
;也就是现在的FS的基址指向KPCR的首地址
09:push large dword ptr fs:0;将KPCR的四字节(dword)压入堆栈
;KPCR第一个成员:_NT_TIB,该结构体的第一个成员大小为4字节,_NT_TIB结构体的第一个成员大小为4字节,为ExceptionList,是异常链表
;KPCR+0x00 ==>_NT_TIB,_NT_TIB+0x00 ==>ExceptionList
10:mov large dword ptr fs:0, 0FFFFFFFFh;将老的ExceptionList保存期来之后,给新的ExceptionList赋值为-1
11:mov esi, large fs:124h;ESI = _KTHREAD
;KPCR偏移0x120是一个结构体:_KPRCB,该结构体_KPRCB偏移0x4,是成员_KTHREAD
;_KTHREAD是一个地址,指向是当前CPU所执行线程的_ETHREAD
;_ETHREAD结构体第一个成员是_KTHREAD,_KTHREAD也是一个结构体
12:push dword ptr [esi+140h];_KTHREAD偏移140是:PreviousMode,也叫先前模式
;什么是先前模式?先前模式记录的是调用这段代码之前,是0环还是3环的。
;将该处压入堆栈
13:sub esp, 48h;当前ESP指向_KTrap_Frame偏移的第0x48处
;因此,sub esp 0x48就是把esp提升到_Trap_Frame首地址处
14:mov ebx, [esp+68h+arg_0];arg_0的偏移为4,也就是把esp偏移0x6C的值赋值给EBX,该值为CS,3环CS是1B,0环是08
15:and ebx, 1;如果时3环(00011011)来的,那么低两位(RPL)即为11,与1做与运算
;与完的结果为1,则为3环,结果为0,则为0环。这是操作系统识别权限的方式
16:mov [esi+140h], bl;上述与完结果保存在bl中,再将之前的权限保存到PreviousMode先前模式中
17:mov ebp, esp;提升堆栈,都指向了_KTrap_frame结构体起始位置
18:mov ebx, [esi+134h];esi(_KTHREAD)偏移0x134的位置为_KTRAP_FRAME
;将_KTRAP_FRAME赋值给ebx
19:mov [ebp+3Ch], ebx;将这个_KTRAP_FRAME,放到ebp+3c的地方
20:mov [esi+134h], ebp;将ebp的指向的位置,修改为_KTHREAD中的_KTRAP_FRAME
;意味着更新了_KTRAP_FRAME的值
22:mov ebx, [ebp+60h];[ebp+60h]执行的是原来3环的ebp,把3环的EBP取出来,放到ebx
23:mov edi, [ebp+68h];同理
24:mov [ebp+0Ch], edx;不管怎么进入0环,都会用到两个寄存器eax,和edx,eax存放的是系统调用号,edx存放的是参数指针
;因此,这里是将参数指针赋值给_KTrap_Frame的DbgArgPointer
25:mov dword ptr [ebp+8], 0BADB0D00h
26:mov [ebp+0], ebx;备份,ebx存的是3环的ebp,将该值赋值给_KTrap_Frame的首地址(ebp)
27:mov [ebp+4], edi;备份,同理
28:test byte ptr [esi+2Ch], 0FFh;esi=_KTHREAD,_KTHREAD+0x2C = DeBugActive
;DeBugActive代表调试状态,如果当前线程为0,那么当前线程就不是调试状态
;test 相当于and,如果是0,zero位会变为1.
29:test byte ptr [esi+3], 0DFh;不知道啥意思
30:jmp loc_43E14F


mov edi, eax ; 取出系统调用号
shr edi, 8 ; 服务号右移8位 1111 1111 1111 1111 111X 1111 1111 1111
and edi, 10h ; 判断第13位(下标12)是否 0001 0000
mov ecx, edi ; 把edi的值赋值给ecx
add edi, [esi+0xE0] ; 如果下标12的地方为0,edi为0,esi(_KTHREAD)偏移0xE0的位置是:ServiceTable,那么找的就是第一张表。
; 如果下标12的地方为1,那么and之后,edi就为10,这个时候add就会加上10,那么刚好是第二张表
; 为什么呢?因为每张表有四个成员,每个成员4字节,4*4=16=0x10
mov ebx, eax; 把系统的调用号备份一份
and eax, 0FFFh ; 取函数表索引,也就是低12位,低12位说明调用的是哪个函数
cmp eax, [edi+8] ; 判断数组是否越界,因为eax代表函数的下标也就是第几个,[edi+8] =NumberOfService
;与服务函数的个数做笔记,看是否越界了
; edi上面说了,存储的就是系统服务表
; typedef struct _SYSTEM_SERVICE_TABLE
;{
;PVOID ServiceTableBase;//这个指向系统服务函数地址表
;PULONG ServiceCounterTableBase;//表里面的服务被访问了多少次
;ULONG NumberOfService;//服务函数的个数
;ULONG ParamTableBase;//参数表基址
;}SYSTEM_SERVICE_TABLE,*PSYSTEM_SERVICE_TABLE;
jnb _KiBBTUnexpectedRange;//没有越界忽略
cmp ecx, 10h; 与0x10比较,看ecx也就是edi需要访问哪个表
jnz short loc_43E18E;如果是0,那么就是要查第一张服务表,会跳转
mov ecx, [esi+_ETHREAD.Tcb.Teb]
xor esi, esi
or esi, [ecx+_TEB.GdiBatchCount]
jz short loc_43E18E
push edx
push eax
call ds:_KeGdiFlushUserBatch ; 第二张表与GUI相关,因此会调用该函数,该函数为GUI 的处理 刷新一下界面渲染
pop eax
pop edx

loc_43E18E: //第一张表

inc large dword ptr fs:_KPCR.PrcbData.KeSystemCalls
mov esi, edx ; edx存放的是3环的参数的指针
xor ecx, ecx
mov edx, [edi+0Ch] ; edi现在执行的是系统服务表的起始位置,[edi+0Ch]为函数参数表
mov edi, [edi] ; 函数表
mov cl, [eax+edx] ; 函数索引 获取参数的字节数,cl存放的是函数需要的参数个数
; 前面说了,函数索引与参数索引一样,一一对应的
mov edx, [edi+eax*4] ; edx = 函数地址,前面说了,函数地址的大小为4字节,而参数大小为1字节,所以这里需要*4
sub esp, ecx ; 提升堆栈,copy参数,现在的ecx其实就是cl
shr ecx, 2 ; 参数字节/4 等于 参数个数,右移2等于除以4,因为参数是1个字节,但是每次提升堆栈都是4个字节的提升,所以要除以4
mov edi, esp
cmp esi, ds:_MmUserProbeAddress ; 0x7FFFF000 _MmUserProbeAddress代表3环程序能访问的最大范围
; 这里是判断esi,3环的参数有没有越界
jnb loc_43E3E5; 如果越界,错误处理。

rep movsd ; 复制参数
test byte ptr [ebp+_KTRAP_FRAME.SegCs], 1
jz short loc_43E1D5
mov ecx, large fs:_KPCR.PrcbData.CurrentThread
mov edi, [esp]
mov [ecx+_ETHREAD.Tcb.SystemCallNumber], ebx
mov [ecx+_ETHREAD.Tcb.FirstArgument], edi

mov ebx, edx
test byte ptr ds:etwLogMask, 40h
setnz [ebp+_KTRAP_FRAME.Logging]
jnz loc_43E574


原文始发于微信公众号(loochSec):系统调用-KiSystemService分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年11月25日16:01:25
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   系统调用-KiSystemService分析https://cn-sec.com/archives/2238424.html

发表评论

匿名网友 填写信息