Windows系统调用学习笔记(三)—— 保存现场
Windows系统调用学习笔记(三)—— 保存现场
- 要点回顾
- 基本概念
- Trap Frame 结构
- 线程相关的结构体
- ETHREAD
- KTHREAD
- CPU相关的结构体
- KPCR
- _NT_TIB
- KPRCB
- 实验一:分析 KiSystemService
- 实验二:分析 KiFastCallEntry
- 总结
要点回顾
API进入0环后调用的函数:
- 中断门 – KiSystemService
- 快速调用 – KiFastCallEntry
上一篇留了几个练习:
- 进0环后,原来的寄存器存在哪里?
- 如何根据系统服务号(eax中存储)找到要执行的内核函数?
- 调用时参数是存储到3环的堆栈,如何传递给内核函数?
- 2种调用方式是如何返回到3环的?
本篇将对第一个练习进行说明
基本概念
Trap Frame 结构
描述:
- 无论是通过中断门进入0环,还是通过快速调用进入0环,进入0环前(3环)的所有寄存器都会存到这个结构体中
- 这个结构体本身处于0环,由windows操作系统进行维护
- 当程序通过中断门从3环进入0环时,ESP指向TrapFrame+0x64的位置
- 当程序通过快速调用从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
描述:
- 全称为CPU控制区(Processor Control Region)
- 每一个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环的SS
、ESP
、EFLAGS
、CS
、EIP
就已经被存储到 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的段描述符:
段描述符:ff
c093df
`f000
0001
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的段描述符:
段描述符:ff
c093df
`f000
0001
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 函数的共同代码部分; 下篇学习系统服务表时再进行分析
总结
- 当程序通过中断门从3环进入0环时,ESP指向TrapFrame+0x64的位置
- 当程序通过快速调用从3环进入0环时,ESP指向TrapFrame+0x78的位置
- 若通过中断门进入0环,在KiSystemService函数开始执行时,3环的
SS
、ESP
、EFLAGS
、CS
、EIP
就已经被存储到 TrapFrame 结构体中了 - TrapFrame 结构体的其它成员通过 KiSystemService 和 KiFastCallEntry 进行赋值
- 不管是 KiSystemService 还是 KiFastCallEntry,最终都要执行一部分相同的代码,分为两个函数是因为进入0环时,堆栈里的值不一样,走同一条函数会出问题
Windows系统调用学习笔记(三)—— 保存现场相关推荐
- Windows系统调用学习笔记(四)—— 系统服务表SSDT
Windows系统调用学习笔记(四)-- 系统服务表&SSDT 要点回顾 系统服务表 实验:分析 KiSystemService 与 KiFastCallEntry 共同代码 SSDT 实验: ...
- Windows系统调用学习笔记(二)—— 3环进0环
Windows系统调用学习笔记(二)-- 3环进0环 要点回顾 基本概念 _KUSER_SHARED_DATA 0x7FFE0300 实验:判断CPU是否支持快速调用 第一步:修改EAX=1 第二步: ...
- Windows系统调用学习笔记(一)—— API函数调用过程
Windows系统调用学习笔记(一)-- API函数调用过程 Windows API 实验1:分析ReadProcessMemory 第一步:定位函数 第二步:开始分析 总结 实验2:分析NtRead ...
- Windows异常学习笔记(一)—— CPU异常记录模拟异常记录
Windows异常学习笔记(一)-- CPU异常记录 基础知识 异常的分类 CPU异常 分析中断处理函数 _KiTrap00 分析 CommonDispatchException 总结 软件模拟异常 ...
- Windows APC学习笔记(二)—— 挂入过程执行过程
Windows APC学习笔记(二)-- 挂入过程&执行过程 基础知识 挂入过程 KeInitializeApc ApcStateIndex KiInsertQueueApc Alertabl ...
- 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 个重要概念 ...
- php shellcode,Windows Shellcode学习笔记
0x00 前言 Windows Shellcode学习笔记--通过VisualStudio生成shellcode,shellcode是一段机器码,常用作漏洞利用中的载荷(也就是payload). 在渗 ...
- J2EE学习笔记三:EJB基础概念和知识 收藏
J2EE学习笔记三:EJB基础概念和知识 收藏 EJB正是J2EE的旗舰技术,因此俺直接跳到这一章来了,前面的几章都是讲Servlet和JSP以及JDBC的,俺都懂一些.那么EJB和通常我们所说的Ja ...
- Windows异常学习笔记(五)—— 未处理异常
Windows异常学习笔记(五)-- 未处理异常 要点回顾 最后一道防线 实验一:理解最后一道防线 实验二:新线程的最后一道防线 总结 UnhandledExceptionFilter 实验三:理解U ...
最新文章
- 南洋理工75页最新「深度学习对话系统」大综述论文,最全面概述深度学习对话技术进展...
- 项目集跟进计划_项目延期,项目经理应该如何补救?
- SQL Server Extended Events (扩展事件)
- 兰州计算机速成班都学什么,兰州当众讲话速成班
- 如何在本地搭建svn本地版本库
- leetcode - 53. 最大子序和 152. 乘积最大子序列 - 两个算法之间的联系和区别
- python 根据索引取值_Python基础知识2
- 若依前后端分离部署到tomcat中详细教程
- linux获取文件名最后一位,获取出文件最后一位是1 或者0 若果都是1 代表是正确的 如果有0代表错误...
- Web前端开发规范 之html命名规范
- 2021热门手机制作表格的软件
- Leaflet--建设移动设备友好的互动地图
- C#.Net实现AutoCAD块属性提取
- vagrant下载速度慢的解决方法
- C/C++去除行末空格
- 实验11-1-7 藏头诗 (15 分)
- Elasticsearch 和 MongoDB 对比
- html ico 图片 无效,设置favicon.ico manifest.json无效
- markdown图片旋转
- 【Python爬虫】Scrapy 下载安装
热门文章
- Python语言学习之时间那些事:python和时间的使用方法之详细攻略
- ML之xgboost:利用xgboost算法(sklearn+3Split)训练mushroom蘑菇数据集(22+1,6513+1611)来预测蘑菇是否毒性(二分类预测)
- 成功解决fp = builtins.open(filename, quot;rbquot;) OSError: [Errno 22] Invalid argument: 'F:\\File_Pyt
- YOLO:将yolo的.weights文件转换为keras、tensorflow等模型所需的.h5文件的图文教程
- mongodb 1安装,开启
- linux与开发板串口通信
- EXT4.2--Ext Designer 使用
- 利用GDataXML解析XML文件
- 数据库字段设置为非空默认值
- Java实现类似C/C++中的__FILE__、__FUNC__、__