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

  • 要点回顾
  • 基本概念
    • Trap Frame 结构
    • 线程相关的结构体
      • ETHREAD
      • KTHREAD
    • CPU相关的结构体
      • KPCR
      • _NT_TIB
      • KPRCB
  • 实验一:分析 KiSystemService
  • 实验二:分析 KiFastCallEntry
    • 总结

要点回顾

API进入0环后调用的函数:

  1. 中断门 – KiSystemService
  2. 快速调用 – KiFastCallEntry

上一篇留了几个练习:

  1. 进0环后,原来的寄存器存在哪里?
  2. 如何根据系统服务号(eax中存储)找到要执行的内核函数?
  3. 调用时参数是存储到3环的堆栈,如何传递给内核函数?
  4. 2种调用方式是如何返回到3环的?

本篇将对第一个练习进行说明

基本概念

Trap Frame 结构

描述

  1. 无论是通过中断门进入0环,还是通过快速调用进入0环,进入0环前(3环)的所有寄存器都会存到这个结构体中
  2. 这个结构体本身处于0环,由windows操作系统进行维护
  3. 当程序通过中断门从3环进入0环时,ESP指向TrapFrame+0x64的位置
  4. 当程序通过快速调用从3环进入0环时,ESP指向TrapFrame+0x78的位置

结构体

在WinDbg中查看:

kd> dt _Ktrap_frame

注意
5. 在保护模式下,最后四个成员(0x7C~0x88)并没有被使用,因此无需考虑;只有在虚拟8086模式下,才会用到
6. 当中断门执行时,3环的SS、ESP、EFLAGS、CS、EIP会被存储到结构体的0x68~0x78中,而执行快速调用时不会

线程相关的结构体

ETHREAD

结构体

KTHREAD

结构体

kd> dt _KTHREAD

CPU相关的结构体

KPCR

描述

  1. 全称为CPU控制区(Processor Control Region)
  2. 每一个CPU都有一个CPU控制区,一核一个KPCR

结构体

查看CPU数量:

kd>dd KeNumBerProcessors

查看KPCR:

kd>dd KiProcessorBlock L2
ffdff120 00000000
若第二个成员有值,说明当前CPU有两个核

_NT_TIB

描述
_NT_TIB是KPCR结构体的成员之一

结构体

KPRCB

描述
KPRCB是KPCR结构体的成员之一

结构体

kd> dt _KPRCB

实验一:分析 KiSystemService

注意:当进入KiSystemService时,3环的SSESPEFLAGSCSEIP就已经被存储到 Trap Frame 结构体中

IDA反汇编

.text:004067D1                 push    0         ; nt!_KTRAP_FRAME;    +0x064 ErrCode          : Uint4B; 中断门产生权限切换时一般往堆栈中压入5个值,但有些情况会压入6个值,第六个值为Error Code; 具体细节可以参考Intel白皮书第三卷,章节名:Input and Exception Handling; 其中有一小节叫做 Error Code;; 当通过 INT 2E 进入0环时,并没有压入Error Code; 操作系统为了对齐,自己补了个0
.text:004067D3                 push    ebp          ; nt!_KTRAP_FRAME;    +0x060 Ebp              : Uint4B
.text:004067D4                 push    ebx          ;    +0x05c Ebx              : Uint4B
.text:004067D5                 push    esi          ;    +0x058 Esi              : Uint4B
.text:004067D6                 push    edi          ;    +0x054 Edi              : Uint4B
.text:004067D7                 push    fs           ;    +0x050 SegFs            : Uint4B
.text:004067D9                 mov     ebx, 30h     ; 为FS寄存器赋值,指向KPCR结构体
.text:004067DE                 mov     fs, ebx      ; 加载下标为6的段描述符到fs

在WinDbg中查看GDT表中下标为6的段描述符:

段描述符ffc093df`f0000001
fs.Base:0xffdff000(KPCR

.text:004067E0                 push    large dword ptr fs:0          ; 保存老的ExceptionList(异常列表); nt!_KPCR;    +0x000 NtTib            : _NT_TIB; nt!_NT_TIB;    +0x000 ExceptionList    : Ptr32 _EXCEPTION_REGISTRATION_RECORD;; nt!_KTRAP_FRAME;    +0x04c ExceptionList    : Ptr32 _EXCEPTION_REGISTRATION_RECORD
.text:004067E7                 mov     large dword ptr fs:0, 0FFFFFFFFh     ; 新的ExceptionList为空白
.text:004067F2                 mov     esi, large fs:124h           ; 得到当前正在执行的线程信息; nt!_KPCR;    +0x120 +0x4 CurrentThread    : Ptr32 _KTHREAD; 存储了当前正在跑的线程的信息,为 KTHREAD 结构体

在WinDbg中查看 KPCR + 0x120 + 0x4

.text:004067F9                 push    dword ptr [esi+140h]         ; 保存老的"先前模式"到堆栈; nt!_KTHREAD;    +0x140 PreviousMode     : Char
.text:004067FF                 sub     esp, 48h                 ; 执行前,ESP位于_KTRAP_FRAME + 0x48 = PreviousPreviousMode; 执行后,ESP 等于 _KTRAP_FRAME 结构指针
.text:00406802                 mov     ebx, [esp+68h+arg_0]       ; arg_0 = 4; 取出三环压入的参数CS(_KTRAP_FRAME + 0x6C)
.text:00406806                 and     ebx, 1                   ; 权限检查:0环最低位为0,3环最低位为1
.text:00406809                 mov     [esi+140h], bl          ; 新的"先前模式"; 将权限检查的结果存储到 PreviousMode 中; 目的是检测调用该代码前,程序处于几环
.text:0040680F                 mov     ebp, esp                 ; ebp = esp = _KTRAP_FRAME 结构指针
.text:00406811                 mov     ebx, [esi+134h]         ; _KTHREAD 的成员 TrapFrame
.text:00406817                 mov     [ebp+3Ch], ebx          ; 将 _KTHREAD 中的 TrapFrame 暂时存放在这个位置; 之后会将这个值重新取出来,赋值给 _KTHREAD 的 TrapFrame
.text:0040681A                 mov     [esi+134h], ebp         ; 将堆栈中形成的 _KTRAP_FRAME 结构指针赋值给 _KTHREAD 的 TrapFrame
.text:00406820                 cld
.text:00406821                 mov     ebx, [ebp+60h]          ; 将原来3环的ebp赋值给ebx
.text:00406824                 mov     edi, [ebp+68h]          ; 将原来3环的eip赋值给edi
.text:00406827                 mov     [ebp+0Ch], edx           ; edx存储的是3环参数的指针:;; _KiFasSystemCall函数:;      mov edx, esp;       sysenter;
.text:0040682A                 mov     dword ptr [ebp+8], 0BADB0D00h       ; 给操作系统用的标志
.text:00406831                 mov     [ebp+0], ebx                ; 原来3环的ebp存储到 _KTRAP_FRAME; + 0x000 DbgEbp 的位置
.text:00406834                 mov     [ebp+4], edi                ; 原来3环的eip存储到 _KTRAP_FRAME; + 0x004 DbgEbp 的位置
.text:00406837                 test    byte ptr [esi+2Ch], 0FFh    ; 判断 _KTHREAD + 0x02c DebugActive 是否为 -1; 若为 -1, 即当前线程 未 处于调试状态
.text:0040683B                 jnz     Dr_kss_a                 ; 若处于调试状态,则跳转; 功能是对TrapFrame的Dr0~Dr7进行赋值; 也就是TrapFrame+0x18 ~ TrapFrame+0x2c的位置进行赋值; 若未处于调试状态,则继续向下执行(loc_406841)

在WinDbg中查看KTHREAD + 0x02c

.text:00406841                 sti                               ; 关闭中断
.text:00406842                 jmp     loc_406932               ; KiSystemService 与 KiFastCallEntry 函数的共同代码片段; 下篇学习系统服务表时再进行分析

实验二:分析 KiFastCallEntry

IDA反汇编

.text:0040689F                 mov     ecx, 23h                      ; ECX = 0x23
.text:004068A4                 push    30h                          ; 通过堆栈的入栈和出栈加载fs
.text:004068A6                 pop     fs                           ; 加载fs段寄存器:GDT表下标为6的段描述符

在WinDbg中查看GDT表下标为6的段描述符:

段描述符ffc093df`f0000001
fs.Base:0xffdff000(KPCR

.text:004068A8                 mov     ds, ecx                       ; RPL=3;TI=0;INDEX=4; 加载GDT表下标为4的段描述符给ds
.text:004068AA                 mov     es, ecx                      ; 加载GDT表下标为4的段描述符给es

在WinDbg中查看GDT表下标为4的段描述符:

.text:004068AC                 mov     ecx, large fs:40h         ; ECX = TSS指针; nt!_KPCR;    +0x040 TSS              : Ptr32 _KTSS
.text:004068B3                 mov     esp, [ecx+4]                    ; nt!_KTSS;    +0x004 Esp0             : Uint4B
.text:004068B6                 push    23h                          ; nt!_KTRAP_FRAME;    +0x078 HardwareSegSs    : Uint4B
.text:004068B8                 push    edx                          ; edx保存的是3环参数的指针; nt!_KTRAP_FRAME;    +0x074 HardwareEsp      : Uint4B
.text:004068B9                 pushf                                ; 旧的标志寄存器入栈
.text:004068BA
.text:004068BA loc_4068BA:                             ; CODE XREF: _KiFastCallEntry2+23↑j
.text:004068BA                 push    2                            ; 新的标志寄存器的值入栈
.text:004068BC                 add     edx, 8
.text:004068BF                 popf                                 ; 新的标志寄存器的值出栈
                                                                 ; EFlags =  2,即第二位置1,其它位清零
.text:004068C0                 or      [esp+0Ch+var_B], 2
.text:004068C5                 push    1Bh                          ; nt!_KTRAP_FRAME;    +0x06c SegCs            : Uint4B
.text:004068C7                 push    dword ptr ds:0FFDF0304h      ;    +0x068 Eip              : Uint4B
.text:004068CD                 push    0                            ;    +0x064 ErrCode          : Uint4B
.text:004068CF                 push    ebp                          ;    +0x060 Ebp              : Uint4B
.text:004068D0                 push    ebx                          ;    +0x05c Ebx              : Uint4B
.text:004068D1                 push    esi                          ;    +0x058 Esi              : Uint4B
.text:004068D2                 push    edi                          ;    +0x054 Edi              : Uint4B
.text:004068D3                 mov     ebx, large fs:1Ch            ; nt!_KPCR;    +0x01c SelfPcr          : Ptr32 _KPCR; 指向当前 KPCR 结构体本身,目的是方便查找
.text:004068DA                 push    3Bh                          ; nt!_KTRAP_FRAME;    +0x050 SegFs            : Uint4B
.text:004068DC                 mov     esi, [ebx+124h]             ; nt!_KPCR;    +0x120 +0x4 CurrentThread    : Ptr32 _KTHREAD; 存储了当前正在跑的线程的信息,为 KTHREAD 结构体
.text:004068E2                 push    dword ptr [ebx]               ; ebx 保存的是KPCR结构体的地址; KPCR 第一个成员为结构体 NT_TIB 的指针; NT_TIB 的第一个值为 ExceptionList(异常列表); 这句话的目的是将异常列表保存到TrapFrame结构体中; nt!_KPCR;    +0x000 NtTib            : _NT_TIB; nt!_NT_TIB;    +0x000 ExceptionList    : Ptr32 _EXCEPTION_REGISTRATION_RECORD;; nt!_KTRAP_FRAME;    +0x04c ExceptionList    : Ptr32 _EXCEPTION_REGISTRATION_RECORD
.text:004068E4                 mov     dword ptr [ebx], 0FFFFFFFFh  ; 将 ExceptionList(异常列表)置为 -1
.text:004068EA                 mov     ebp, [esi+18h]              ; nt!_KTHREAD;    +0x018 InitialStack     : Ptr32 Void
.text:004068ED                 push    1                            ; nt!_KTRAP_FRAME;    +0x048 PreviousPreviousMode : Uint4B; 往栈中压入老的先前模式,1表示先前模式为3环
.text:004068EF                 sub     esp, 48h                      ; 执行前,ESP位于_KTRAP_FRAME + 0x48 = PreviousPreviousMode; 执行后,ESP 等于 _KTRAP_FRAME 结构指针
.text:004068F2                 sub     ebp, 29Ch
.text:004068F8                 mov     byte ptr [esi+140h], 1      ; 新的先前模式; nt!_KTHREAD;     +0x140 PreviousMode     : Char
.text:004068FF                 cmp     ebp, esp                     ; 检查 ebp 是否等于 esp,也就是 _KTRAP_FRAME 结构指针
.text:00406901                 jnz     loc_40686C                   ; 若不等于,则跳转,进行异常处理
.text:00406907                 and     dword ptr [ebp+2Ch], 0      ; 修改 TrapFrame 结构体中 Dr7 的值; nt!_KTRAP_FRAME;    +0x02c Dr7              : Uint4B
.text:0040690B                 test    byte ptr [esi+2Ch], 0FFh        ; 检测当前进程是否处于调试状态; nt!_KTHREAD;    +0x02c DebugActive      : UChar; 若不处于调试状态,则 DebugActive 的值为FF
.text:0040690F                 mov     [esi+134h], ebp             ; 将 TrapFrame 结构指针写入当前线程信息中; nt!_KTHREAD;    +0x134 TrapFrame        : Ptr32 _KTRAP_FRAME
.text:00406915                 jnz     Dr_FastCallDrSave            ; 若当前进程处于被调试状态,则跳转,为 Dr0 ~ Dr7 进行赋值; 若当前进程不处于被调试状态,则继续向下执行
.text:0040691B
.text:0040691B loc_40691B:                             ; CODE XREF: Dr_FastCallDrSave+10↑j
.text:0040691B                                         ; Dr_FastCallDrSave+7C↑j
.text:0040691B                 mov     ebx, [ebp+60h]              ; 从 TrapFrame 结构体中取出 Ebp 到 ebx 寄存器
.text:0040691E                 mov     edi, [ebp+68h]              ; 从 TrapFrame 结构体中取出 Eip 到 edi 寄存器
.text:00406921                 mov     [ebp+0Ch], edx              ; nt!_KTRAP_FRAME;    +0x000 DbgEbp           : Uint4B
.text:00406924                 mov     dword ptr [ebp+8], 0BADB0D00h;    +0x008 DbgArgMark       : Uint4B
.text:0040692B                 mov     [ebp+0], ebx                    ;    +0x000 DbgEbp           : Uint4B
.text:0040692E                 mov     [ebp+4], edi                    ;    +0x004 DbgEip           : Uint4B
.text:00406931                 sti                                  ; 关闭中断
.text:00406932
.text:00406932 loc_406932:                             ; CODE XREF: _KiBBTUnexpectedRange+18↑j
.text:00406932                                         ; _KiSystemService+71↑j; KiSystemService 与 KiFastCallEntry 函数的共同代码部分; 下篇学习系统服务表时再进行分析

总结

  1. 当程序通过中断门从3环进入0环时,ESP指向TrapFrame+0x64的位置
  2. 当程序通过快速调用从3环进入0环时,ESP指向TrapFrame+0x78的位置
  3. 若通过中断门进入0环,在KiSystemService函数开始执行时,3环的SSESPEFLAGSCSEIP就已经被存储到 TrapFrame 结构体中了
  4. TrapFrame 结构体的其它成员通过 KiSystemServiceKiFastCallEntry 进行赋值
  5. 不管是 KiSystemService 还是 KiFastCallEntry,最终都要执行一部分相同的代码,分为两个函数是因为进入0环时,堆栈里的值不一样,走同一条函数会出问题

Windows系统调用学习笔记(三)—— 保存现场相关推荐

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

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

  2. Windows系统调用学习笔记(二)—— 3环进0环

    Windows系统调用学习笔记(二)-- 3环进0环 要点回顾 基本概念 _KUSER_SHARED_DATA 0x7FFE0300 实验:判断CPU是否支持快速调用 第一步:修改EAX=1 第二步: ...

  3. Windows系统调用学习笔记(一)—— API函数调用过程

    Windows系统调用学习笔记(一)-- API函数调用过程 Windows API 实验1:分析ReadProcessMemory 第一步:定位函数 第二步:开始分析 总结 实验2:分析NtRead ...

  4. Windows异常学习笔记(一)—— CPU异常记录模拟异常记录

    Windows异常学习笔记(一)-- CPU异常记录 基础知识 异常的分类 CPU异常 分析中断处理函数 _KiTrap00 分析 CommonDispatchException 总结 软件模拟异常 ...

  5. Windows APC学习笔记(二)—— 挂入过程执行过程

    Windows APC学习笔记(二)-- 挂入过程&执行过程 基础知识 挂入过程 KeInitializeApc ApcStateIndex KiInsertQueueApc Alertabl ...

  6. K8S 学习笔记三 核心技术 Helm nfs prometheus grafana 高可用集群部署 容器部署流程

    K8S 学习笔记三 核心技术 2.13 Helm 2.13.1 Helm 引入 2.13.2 使用 Helm 可以解决哪些问题 2.13.3 Helm 概述 2.13.4 Helm 的 3 个重要概念 ...

  7. php shellcode,Windows Shellcode学习笔记

    0x00 前言 Windows Shellcode学习笔记--通过VisualStudio生成shellcode,shellcode是一段机器码,常用作漏洞利用中的载荷(也就是payload). 在渗 ...

  8. J2EE学习笔记三:EJB基础概念和知识 收藏

    J2EE学习笔记三:EJB基础概念和知识 收藏 EJB正是J2EE的旗舰技术,因此俺直接跳到这一章来了,前面的几章都是讲Servlet和JSP以及JDBC的,俺都懂一些.那么EJB和通常我们所说的Ja ...

  9. Windows异常学习笔记(五)—— 未处理异常

    Windows异常学习笔记(五)-- 未处理异常 要点回顾 最后一道防线 实验一:理解最后一道防线 实验二:新线程的最后一道防线 总结 UnhandledExceptionFilter 实验三:理解U ...

最新文章

  1. 南洋理工75页最新「深度学习对话系统」大综述论文,最全面概述深度学习对话技术进展...
  2. 项目集跟进计划_项目延期,项目经理应该如何补救?
  3. SQL Server Extended Events (扩展事件)
  4. 兰州计算机速成班都学什么,兰州当众讲话速成班
  5. 如何在本地搭建svn本地版本库
  6. leetcode - 53. 最大子序和 152. 乘积最大子序列 - 两个算法之间的联系和区别
  7. python 根据索引取值_Python基础知识2
  8. 若依前后端分离部署到tomcat中详细教程
  9. linux获取文件名最后一位,获取出文件最后一位是1 或者0 若果都是1 代表是正确的 如果有0代表错误...
  10. Web前端开发规范 之html命名规范
  11. 2021热门手机制作表格的软件
  12. Leaflet--建设移动设备友好的互动地图
  13. C#.Net实现AutoCAD块属性提取
  14. vagrant下载速度慢的解决方法
  15. C/C++去除行末空格
  16. 实验11-1-7 藏头诗 (15 分)
  17. Elasticsearch 和 MongoDB 对比
  18. html ico 图片 无效,设置favicon.ico manifest.json无效
  19. markdown图片旋转
  20. 【Python爬虫】Scrapy 下载安装

热门文章

  1. Python语言学习之时间那些事:python和时间的使用方法之详细攻略
  2. ML之xgboost:利用xgboost算法(sklearn+3Split)训练mushroom蘑菇数据集(22+1,6513+1611)来预测蘑菇是否毒性(二分类预测)
  3. 成功解决fp = builtins.open(filename, quot;rbquot;) OSError: [Errno 22] Invalid argument: 'F:\\File_Pyt
  4. YOLO:将yolo的.weights文件转换为keras、tensorflow等模型所需的.h5文件的图文教程
  5. mongodb 1安装,开启
  6. linux与开发板串口通信
  7. EXT4.2--Ext Designer 使用
  8. 利用GDataXML解析XML文件
  9. 数据库字段设置为非空默认值
  10. Java实现类似C/C++中的__FILE__、__FUNC__、__