Windows系统调用中的现场保存
该结构体简单来说用于三环的寄存器保存,存储于零环,由操作系统维护,每个线程都有自己的 _Ktrap_frame 结构体(ethread+0x108处)。
2. _ETHREAD 结构体
该结构体保存了和线程相关的信息,位于0环(其并非三环的 TEB,线程环境块)
在_ETHREAD结构体第一个成员是 _KTHRAD,可以看出其大小0x200,里面保存了线程中的一些信息
kd > dt _ethread
ntdll!_ETHREAD
+ 0x000 Tcb : _KTHREAD
+ 0x200 CreateTime : _LARGE_INTEGER
····
kd > dt _kthread
ntdll!_KTHREAD
+ 0x000 Header : _DISPATCHER_HEADER
···
+0x128 TrapFrame : Ptr32 _KTRAP_FRAME
3. KPCR结构体(kernel processor control region 内核线程控制区)
有一篇文章,里面大体介绍了该结构体 --> [Windows内核分析]KPCR结构体介绍 (CPU控制区 Processor Control Region)
简单来说,KPCR结构体中保存着 关于CPU 的信息,一个核有一个自己私有的KPCR结构体,八核则每个核有自己的单独的KPCR结构体
我们需要用到最后一个成员中的中的CurrentThread来获取当前线程的_KTHREAD结构体。
kd > dt _kpcr
ntdll!_KPCR
+ 0x000 NtTib : _NT_TIB
+ 0x000 Used_ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
····
+0x120 PrcbData : _KPRCB
kd > dt _KPRCB
ntdll!_KPRCB
+ 0x000 MinorVersion : Uint2B
+ 0x002 MajorVersion : Uint2B
+ 0x004 CurrentThread : Ptr32 _KTHREAD
+ 0x008 NextThread : Ptr32 _KTHREAD
···
二、分析 nt!KiSystemService 内核函数
该函数位于ntkrnlpa.exe真正的内核中(并非ntdll.dll),一开始希望使用IDA反汇编查看其代码,但是无法显示。
我们只能采取"曲线救国"的方法,通过int 2eh中断号查找GDT表,从表中获取中断描述符,将中断描述符拼接拆分,获取 函数的EIP 地址。
之后,我们使用 Windbg 的U指令,来查看该其该函数在内存中的反汇编代码
反汇编代码可能有点长,我们在正式分析之前需要明确几个注意事项。
0)这些反汇编代码的目的就是将 _Ktrap_frame 结构体中的寄存器填满,然后将[KTHREAD+0x128]处旧的TRAPFRAME切换为这个新的KTRAPFRAME然后调用内核函数。
1)函数开始时ESP位于_Ktrap_framec+0x070 ErrCode处。ErroCode这里没有,因此可以看待汇编代码中第一行使用 push 0。
2)FS寄存器在0环时是指向自己的KPCR,而在3环时保存的是该线程的TEB结构体。(R3可以查看:利用C++实现模块隐藏)
因此在反汇编代码 83e8cff6 、83e8cffb 是手动将FS寄存器存储KPCR。
原理是以30h作为段选择子查找GDT这张表,找出段描述符,然后加载到fs段寄存器中。
3) 0x83e8d007处 KPCR:[124] 为当前线程的ETHREAD结构体(看一中的表,KTHREAD也即ETHREAD的头部),将其存储到寄存器esi中,之后要经常使用。
4)0x83e8d026处修改esp,将其指向 _Ktrap_frame 结构体第一个成员,0x83e8d036 处也将ebp修改为 _Ktrap_frame 结构体第一个成员。
5)因为 esi 指向ETHREAD, [esi+128h] 则表示当前线程的_Ktrap_frame,在进入内核时,其保存了一个旧的frame。
0x83e8d038将获取旧的_Ktrap_frame
0x83e8d03e将旧的Frame放入新的Frame的edx寄存器中
0x83e8d049将新的Frame挂靠在ETHREAD+0x128处,这样完成了新旧替换。
下图三个箭头依次对应上面三个阶段,这样通过FS寄存器就可以查找到该FRAME了,回去只需要找到这里完成替换即可。
6)Frame前部分成员和调试有关,因此0x83e8d045这里需要判断是否在调试状态,如果处于调试状态,则jmp进一个地址,将寄存器入栈再回来。
硬件调试,其实就是通过这个部分。对于反调试,这里可以做些文章...
7)还有部分涉及权限切换,0x83e8d030 比如有些API可以让3环也可以让0环权限调用,但有的不让(看保护模式调用门等知识),此时就会进行位运算判断"先前模式"的权限级别。
8)当这些工作全部完成之后,会执行 83e8d06d e9dd000000 jmp nt!KiFastCallEntry+0x8f (83e8d14f)
对,你没看错,就是快速调用时的函数,相当于中断多走了一步,处理了上面的一些信息,而快速调用提前就可以处理好直接调用 nt!KiFastCallEntry+0x8f函数。
三、nt!KiSystemService函数的反汇编解读
1 nt!KiSystemService: 2