文章目录

  • 前言
  • 保存现场
    • _KTRAP_FRAME
    • KRPC
    • ExceptionList
    • _KTHREAD结构体
    • 先前模式
    • 抬高堆栈
    • 判断当前权限
    • 更新_KTRAP_FRAME
    • 保存三环的寄存器环境
    • 判断调试状态

前言

之前我们详细了解了API从三环进到零环的过程,API会通过两种方式进入到零环,如果通过中断门的方式进入零环,最终会进入到KiSystemService这个函数。接下来就来分析KiSystemService这个函数的内部实现细节。

用IDA打开ntkrnlpa.exe,找到KiSystemService函数

程序想要运行必须要有两样东西,分别是EIP和ESP。一旦进入零环,就需要把寄存器保存到一个结构体,这个结构体就是_KTRAP_FRAME,也就是零环的堆栈。

保存现场

_KTRAP_FRAME

_KTRAP_FRAME这个结构体由操作系统维护 数据如下所示:

kd> dt _KTRAP_FRAME
nt!_KTRAP_FRAME+0x000 DbgEbp           : Uint4B+0x004 DbgEip           : Uint4B+0x008 DbgArgMark       : Uint4B+0x00c DbgArgPointer    : Uint4B+0x010 TempSegCs        : Uint2B+0x012 Logging          : UChar+0x013 Reserved         : UChar+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+0x06c SegCs            : Uint4B+0x070 EFlags           : Uint4B+0x074 HardwareEsp      : Uint4B+0x078 HardwareSegSs    : Uint4B+0x07c V86Es            : Uint4B+0x080 V86Ds            : Uint4B+0x084 V86Fs            : Uint4B+0x088 V86Gs            : Uint4B

这个结构体的最后四个成员只有在虚拟8086模式下才会用到,保护模式下不用。

当API通过中断门进入到零环之前,ESP0指向+0x078 HardwareSegSs的这个位置。

接着,中断门提权后会将SS ESP EFlags CS和EIP压入堆栈。此时,ESP0指向+0x064 ErrCode的位置。

到这里,我们就知道了KiSystemService函数前几行汇编代码的含义:

.text:00465651                 push    0            ; 保存ErrCode到esp0
.text:00465653                 push    ebp             ; 保存ebp到esp0
.text:00465654                 push    ebx             ; 保存ebx到esp0
.text:00465655                 push    esi             ; 保存esi到esp0
.text:00465656                 push    edi             ; 保存edi到esp0
.text:00465657                 push    fs              ; 保存fs到esp0

KRPC

接下来继续分析后面的代码

.text:00465659                 mov     ebx, 30h
.text:0046565E                 mov     fs, bx

这里将0x30赋值给FS寄存器,通过拆分0x30段选择子可以得到GDT表下标为6的段描述符:FFC093DF`F0000001。这个段描述符的基址为FFDFF0000,指向当前的KPCR结构体。

KPCR结构体如下所示:

kd> dt _KPCR
nt!_KPCR+0x000 NtTib            : _NT_TIB+0x000 Used_ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD+0x004 Used_StackBase   : Ptr32 Void+0x008 Spare2           : Ptr32 Void+0x00c TssCopy          : Ptr32 Void+0x010 ContextSwitches  : Uint4B+0x014 SetMemberCopy    : Uint4B+0x018 Used_Self        : Ptr32 Void+0x01c SelfPcr          : Ptr32 _KPCR+0x020 Prcb             : Ptr32 _KPRCB+0x024 Irql             : UChar+0x028 IRR              : Uint4B+0x02c IrrActive        : Uint4B+0x030 IDR              : Uint4B+0x034 KdVersionBlock   : Ptr32 Void+0x038 IDT              : Ptr32 _KIDTENTRY+0x03c GDT              : Ptr32 _KGDTENTRY+0x040 TSS              : Ptr32 _KTSS+0x044 MajorVersion     : Uint2B+0x046 MinorVersion     : Uint2B+0x048 SetMember        : Uint4B+0x04c StallScaleFactor : Uint4B+0x050 SpareUnused      : UChar+0x051 Number           : UChar+0x052 Spare0           : UChar+0x053 SecondLevelCacheAssociativity : UChar+0x054 VdmAlert         : Uint4B+0x058 KernelReserved   : [14] Uint4B+0x090 SecondLevelCacheSize : Uint4B+0x094 HalReserved      : [16] Uint4B+0x0d4 InterruptMode    : Uint4B+0x0d8 Spare1           : UChar+0x0dc KernelReserved2  : [17] Uint4B+0x120 PrcbData         : _KPRCB

KPCR叫CPU控制区(Kernel Processor Control Region),每个CPU有一个。如果想查看当前的CPU数量,可以用下面这条指令。

kd> dd KeNumberProcessors
83fb096c  00000001 83f3cf33 00000001 00000001
83fb097c  00000000 00000000 00000020 1fc10000
83fb098c  00110006 00003c03 776c7058 776c6f58
83fb099c  776c6fc0 776c7008 776b5a8f 776b5a8d
83fb09ac  776b5a64 00000000 00ce6126 842095b0
83fb09bc  841734f2 83ec4d9c 00000000 00000191
83fb09cc  83ec53e4 00000000 00000000 00000000
83fb09dc  00000000 83f236af 00000000 026b2372

我当前的虚拟机只有一个核,所以数量是1。用下面这条指令可以查看每个核对应的KPCR分别是什么

kd> dd KiProcessorBlock l2
8055a320 ffdff120 00000000

这个地址显示的是ffdff120,也就是KPCR偏移0x120的位置。KPCR偏移0x120的位置是 _KPRCB,可以理解为扩展的KPCR。

所以下面这两句代码执行完之后,FS就指向KPCR

.text:00465659                 mov     ebx, 30h
.text:0046565E                 mov     fs, bx          ; 使FS指向KPCR

ExceptionList

接下来继续向下分析

.text:00465661                 push    dword ptr ds:0FFDFF000h
.text:00465667                 mov     dword ptr ds:0FFDFF000h, 0FFFFFFFFh

这里将0FFDFF000h压入堆栈,也就是KPCR的结构体的第一个成员

+0x000 NtTib            : _NT_TIB

这个NtTib是个子结构体,这个子结构体很大

kd> dt _NT_TIB
nt!_NT_TIB+0x000 ExceptionList    : Ptr32 _EXCEPTION_REGISTRATION_RECORD+0x004 StackBase        : Ptr32 Void+0x008 StackLimit       : Ptr32 Void+0x00c SubSystemTib     : Ptr32 Void+0x010 FiberData        : Ptr32 Void+0x010 Version          : Uint4B+0x014 ArbitraryUserPointer : Ptr32 Void+0x018 Self             : Ptr32 _NT_TIB

这个结构体的第一个成员是ExceptionList

.text:00465661                 push    dword ptr ds:0FFDFF000h ; 保存老的ExceptionList到esp0
.text:00465667                 mov     dword ptr ds:0FFDFF000h, 0FFFFFFFFh ; 将新的ExceptionList赋值为-1

这两行代码首先保存老的ExceptionList,并将新的ExceptionList赋值为-1,。继续往下分析

.text:00465671                 mov     esi, ds:0FFDFF124h

_KTHREAD结构体

这里将0FFDFF124h保存到esi。KPCR偏移0x120的位置是_KPRCB

+0x120 PrcbData         : _KPRCB

继续查看一下_KPRCB结构体0x4的位置

kd> dt _KPRCB
nt!_KPRCB+0x000 MinorVersion     : Uint2B+0x002 MajorVersion     : Uint2B+0x004 CurrentThread    : Ptr32 _KTHREAD

CurrentThread是当前CPU所执行线程的_ETHREAD结构体,继续查看一下_ETHREAD结构体。

kd> dt _ETHREAD
nt!_ETHREAD+0x000 Tcb              : _KTHREAD

_ETHREAD结构体的第一个成员是_KTHREAD,所以下面这行代码的具体含义是将当前线程的_KTHREAD结构体保存到esi

.text:00465671                 mov     esi, ds:0FFDFF124h ; 将当前线程的_KTHREAD结构体保存到esi

继续往下分析

.text:00465677                 push    dword ptr [esi+140h]

这里将esi+140压入堆栈,esi指向_KTHREAD结构体,查看一下_KTHREAD0x140的位置

kd> dt _KTHREAD
nt!_KTHREAD+0x140 PreviousMode

先前模式

0x140的位置保存的是先前模式,先前模式的作用就是记录当前调用的这段代码之前是被零环调用还是被三环调用

.text:00465677                 push    dword ptr [esi+140h] ; 保存老的先前模式到esp0

抬高堆栈

继续往下分析

.text:0046567D                 sub     esp, 48h

回顾一下当前的堆栈情况

kd> dt _KTRAP_FRAME
nt!_KTRAP_FRAME+0x000 DbgEbp           : Uint4B+0x004 DbgEip           : Uint4B+0x008 DbgArgMark       : Uint4B+0x00c DbgArgPointer    : Uint4B+0x010 TempSegCs        : Uint2B+0x012 Logging          : UChar+0x013 Reserved         : UChar+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+0x06c SegCs            : Uint4B+0x070 EFlags           : Uint4B+0x074 HardwareEsp      : Uint4B+0x078 HardwareSegSs    : Uint4B+0x07c V86Es            : Uint4B+0x080 V86Ds            : Uint4B+0x084 V86Fs            : Uint4B+0x088 V86Gs            : 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

而除去已经压入堆栈的值,这个结构体正好剩下0x48个字节,sub,esp 0x48将当前的esp0指向_KTRAP_FRAME0x00的位置

.text:0046567D                 sub     esp, 48h        ; 将esp0指向_KTRAP_FRAME头

判断当前权限

继续往下分析

.text:00465680                 mov     ebx, [esp+68h+arg_0]
.text:00465684                 and     ebx, 1

这里将esp+0x68+4的值保存到ebx,当前的esp指向``_KTRAP_FRAME`头部,esp+6C的位置就是CS

+0x06c SegCs            : Uint4B

取出CS之后,和1做与运算。

如果是3环的CS段选择子,那么最后两个二进制位是11。11和1进行与运算结果还是1。

如果是0环的CS段选择子,那么最后两个二进制位是00。00和1进行与运算结果还是00。

这里实际上是通过与运算的结果来判断当前代码是三环还是零环。

.text:00465687                 mov     [esi+140h], bl  ; 赋值新的先前模式

接下来将bl赋值给esi+140h的位置,也就是之前分析过的先前模式

.text:0046568D                 mov     ebp, esp        ; ebp=esp=_KTRAP_FRAME

接下来上面这行代码执行完成之后ebp和esp都指向_KTRAP_FRAME结构体,继续往下分析

更新_KTRAP_FRAME

.text:0046568F                 mov     ebx, [esi+134h]

这里将esi+0x134的位置保存到ebx,esi指向的是_ETHREAD,查看一下 +0x134的位置保存的内容

+0x134 TrapFrame        : Ptr32 _KTRAP_FRAME

+0x134的位置保存的_KTRAP_FRAME结构体指针,_KTRAP_FRAME这个结构体以线程为单位保存在_ETHREAD结构体里面,每个线程都有一份。

.text:0046568F                 mov     ebx, [esi+134h] ; 保存_KTRAP_FRAME结构体到ebx
.text:00465695                 mov     [ebp+3Ch], ebx  ; 将KTHREAD中的_KTRAP_FRAME保存到[ebp+0x3C]

所以这一句的含义实际上是先将_KTRAP_FRAME结构体保存到ebx,然后再存到[ebp+0x3C]的位置。继续往下分析

.text:00465698                 mov     [esi+134h], ebp ; 更新当前线程的_KTRAP_FRAME

这里将ebp保存到[esi+134h],此时ebp指向_KTRAP_FRAME头部位置,而[esi+134h]也是_KTRAP_FRAME头,所以这行代码执行完,就会更新当前线程的_KTRAP_FRAME结构体。继续往下分析

保存三环的寄存器环境

.text:0046569F                 mov     ebx, [ebp+60h]
.text:004656A2                 mov     edi, [ebp+68h]

当前的ebp=esp指向了_KTRAP_FRAME头的位置,

kd> dt _KTRAP_FRAME
nt!_KTRAP_FRAME+0x060 Ebp              : Uint4B+0x064 ErrCode          : Uint4B+0x068 Eip              : Uint4B

[ebp+0x60]的位置是三环的ebp,[ebp+0x68]的位置是三环的eip,

.text:0046569F                 mov     ebx, [ebp+60h]  ; 取出三环的ebp放到ebx
.text:004656A2                 mov     edi, [ebp+68h]  ; 取出三环的eip放到edi
.text:004656A5                 mov     [ebp+0Ch], edx
.text:004656A8                 mov     dword ptr [ebp+8], 0BADB0D00h
.text:004656AF                 mov     [ebp+0], ebx    ; 将三环的ebp备份到_KTRAP_FRAME DbgEbp的位置
.text:004656B2                 mov     [ebp+4], edi    ; 将三环的eip备份到_KTRAP_FRAME DbgEip的位置
.text:004656B5                 test    byte ptr [esi+2Ch], 0FFh

这里先将三环的ebp和eip保存到寄存器,然后再备份到_KTRAP_FRAME结构体DbgEbp和DbgEip的位置。然后继续看中间两行代码

.text:004656A5                 mov     [ebp+0Ch], edx
.text:004656A8                 mov     dword ptr [ebp+8], 0BADB0D00h

这里将edx存到ebp+0xC的位置

kd> dt _KTRAP_FRAME
nt!_KTRAP_FRAME+0x000 DbgEbp           : Uint4B+0x004 DbgEip           : Uint4B+0x008 DbgArgMark       : Uint4B+0x00c DbgArgPointer    : Uint4B

ebp+0xC的位置是DbgArgPointer,那么这个edx是什么呢?这里要回顾之前学习的内容。三环进零环的两种方式,不管是哪一种,都会用到eax和edx两个寄存器,其中eax保存的是服务号,而edx存储的是三环的参数开始的位置。

.text:004656A5                 mov     [ebp+0Ch], edx  ; 将三环的参数指针存到DbgArgPointer
.text:004656A8                 mov     dword ptr [ebp+8], 0BADB0D00h ; 将DbgArgMark赋值为0BADB0D00

判断调试状态

那么这行汇编代码的作用就是将三环的参数指针存到DbgArgPointer,继续往下分析

.text:004656B5                 test    byte ptr [esi+2Ch], 0FFh
.text:004656B9                 jnz     Dr_kss_a

当前的esi指向KTHRAD结构体, [esi+2Ch]是位置是DebugActive,这个字段是调试状态,如果当前的线程处于调试状态,那么这里面的值不为零

+0x02C DebugActive

这里将DebugActive和FF做与运算,根据运算的结果决定是否跳转,那么这两句汇编的含义就很明显了

.text:004656B5                 test    byte ptr [esi+2Ch], 0FFh ; 判断KTHREAD结构体的DebugActive是否为零
.text:004656B9                 jnz     Dr_kss_a        ; 如果处于调试状态 跳转

到此,整个KiSystemService函数保存现场的部分就已经完成了。

FFh
.text:004656B9 jnz Dr_kss_a


当前的esi指向KTHRAD结构体, [esi+2Ch]是位置是DebugActive,这个字段是调试状态,如果当前的线程处于调试状态,那么这里面的值不为零```assembly
+0x02C DebugActive

这里将DebugActive和FF做与运算,根据运算的结果决定是否跳转,那么这两句汇编的含义就很明显了

.text:004656B5                 test    byte ptr [esi+2Ch], 0FFh ; 判断KTHREAD结构体的DebugActive是否为零
.text:004656B9                 jnz     Dr_kss_a        ; 如果处于调试状态 跳转

到此,整个KiSystemService函数保存现场的部分就已经完成了。

系统调用002 KiSystemService函数逆向分析相关推荐

  1. 58 招财猫变形 RSA 与变形 BASE64 逆向分析

    0x0 概述: 58 招财猫登陆密码算法经过: libcom_wuba_uc_rsa.so 中 Java_com_wuba_uc_RsaCryptService_encrypt 函数加密,下面 简称 ...

  2. X64驱动开发和保护+X86X64游戏逆向分析课程

    老师教学范围和方式:木塔负责PC电脑端C语言基础和端游逆向分析部分内容,采用录制+部分直播课程教学,晚上还有专业老师讲解和指导.我要的是质量不是数量.老师备课,设计课件需要时间的. 学习周期:PC端3 ...

  3. Simens NX (原UG)内部代码逆向分析 / Inner code Reverse analysis of NX software

    [篇首语]这里的逆向分析不是机械中的反求工程(抄数),而是软件中的逆向工程,旨在研究软件内部代码实现机制.我在这里要说明一下,软件逆向不在软件使用的许可范围之内,仅可以作为研究软件实现功能的一种方法. ...

  4. (49)逆向分析KiSystemService/KiFastCallEntry调用内核函数部分(SST,SSDT,SSPT)

    一.回顾 前两篇博客,我逆向分析了 KiSystemService 和 KiFastCallEntry 填充_KTRAP_FRAME 结构体的代码,二者大同小异,主要的区别是 sysenter 只改了 ...

  5. (47)逆向分析 KiSystemService 函数填充 _KTRAP_FRAME 部分

    一.回顾 之前的课程,我们学习了API系统调用在3环部分做的事情,有两种方式进0环,分别是中断门和快速调用,分别调用两个不同的函数 KiSystemService 和 KiFastCallEntry. ...

  6. (48)逆向分析 KiFastCallEntry 函数填充 _KTRAP_FRAME 部分

    一.回顾 上一篇博客我逆向了 KiSystemService 函数填充 _KTRAP_FRAME 部分. 逆向分析 KiSystemService 函数填充 _KTRAP_FRAME 部分 里面涉及了 ...

  7. 使用 inline 内联函数对抗逆向分析

    在 ARM 汇编里函数调用是使用 bl 指令跳转到函数地址,攻击者逆向分析很容易找到调用过程,分析参数和返回值. 可以将一些敏感的函数,比如将加密函数写成 inline 内联,无论代码里调用了多少次函 ...

  8. 二进制函数_Go二进制文件逆向分析从基础到进阶——MetaInfo、函数符号和源码文件路径列表...

    书接前文,本文主要介绍 Go 二进制文件中 Meta Information 的解析,与函数符号和源码文件路径列表的提取.最后详细介绍一下 Moduledata 这个结构.传送门:Go二进制文件逆向分 ...

  9. Linux Security Module逆向分析实战

    Linux Security Module逆向分析实战 本文记录了对某发行版Linux中一个安全模块(LSM)的逆向过程,该LSM对系统中待运行的程序进行安全校验,数据流穿越内核态与用户态,涉及系统内 ...

最新文章

  1. 如何看待消息中间件的选型
  2. 让Windows下Git和TortoiseGit支持中文文件名/UTF-8
  3. 关于Django部分
  4. Android android-common 常用功能和工具集合
  5. Android 使用本地应用在线播放流媒体文件
  6. datatable移动一列的位置
  7. Ogre共享骨骼与两种骨骼驱动方法
  8. 计算机怎么更改用户头像像,Win10系统电脑账户头像怎么改成系统默认状态?
  9. kl散度的理解_以曲率的视角理解自然梯度优化
  10. s5pv210——I2C基础详解、I2C时序详解
  11. 你确信你了解时间吗?
  12. 中柏平板电脑安linux,中柏平板电脑重装系统
  13. wps打开服务器文件很慢,wps打开速度慢怎么办-wps打开速度慢解决方法 - 河东软件园...
  14. intel服务器最新主板芯片组,Intel C600芯片组:数据中心集群计算平台_Intel服务器主板_服务器评测与技术-中关村在线...
  15. oracle左连接应用场景,Oracle左连接left join 的实际操作与应用
  16. Linux 命令(208)—— ssh-keygen 命令
  17. java面向对象实验结论及心得_20162305 实验二 Java面向对象程序设计 实验报告
  18. 别管哪条公链了,你听过数字恐龙吗?
  19. Java中原生(native)函数的用法
  20. 智慧园区BIM智能化系统平台建设方案

热门文章

  1. DL之DNN优化技术:利用Dropout(简介、使用、应用)优化方法提高DNN模型的性能
  2. 奇奇怪怪的小姿势——关于UI的各种Position
  3. python 进程理论基础
  4. ionic 完美仿微信摇一摇
  5. 一款超炫的图片排列特效
  6. 【备忘】visual studio调试状态下显示lua调用栈
  7. flash player10.1 + FMS4中的p2p功能
  8. 漫谈C++重载运算符
  9. 字符设备驱动基本流程
  10. EOS 智能合约源代码解读 (3)asset.hpp