Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.htmlhtml
Windows系统调用中API从3环到0环(上)函数
若是对API在三环的部分不了解的,能够查看 Windows系统调用中的API三环部分(依据分析重写ReadProcessMemory函数
spa
这篇文章分为上下两篇,其中上篇初步讲解大致轮廓,下篇着重经过实验来探究其内部实现,最终分析两个函数(快速调用与系统中断),来实现经过系统中断直接调用内核函数。操作系统
1、结构体 _KUSER_SHARED_DATA指针
该结构体看名字可知是用于内核层与用户层来共享数据所使用的结构体。htm
kd > dt _KUSER_SHARED_DATA
ntdll!_KUSER_SHARED_DATA
+ 0x000 TickCountLowDeprecated : Uint4B
+ 0x004 TickCountMultiplier : Uint4B
+ 0x008 InterruptTime : _KSYSTEM_TIME
+ 0x014 SystemTime : _KSYSTEM_TIME
+ 0x020 TimeZoneBias : _KSYSTEM_TIME
+ 0x02c ImageNumberLow : Uint2B
+ 0x02e ImageNumberHigh : Uint2B
·······blog
1)在User层和KerNel层分别定义了一个_KUSER_SHARED_DATA结构区域,用于User层和Kernel层共享某些数据。索引
2)它们使用同一段页,只是映射位置不一样。虽然同一页,但User只读,Kernnel层可写。ip
3)它们使用固定的地址值映射,_KUSER_SHARED_DATA结构在User为:0x7ffe0000,在Kernel层为:0xffdf0000。内存
经过windbg能够查看其中两块内存彻底同样(实质查看的是同一物理页,挂在两块页表PTT中。
2、分析 0X7FFE0300 这个地址
在 <&ntdll.NtReadVirtualMemory> 中调用 ntdll.KiFastSystemCall 函数 ,实质就是调用 0X7FFE0300 这个地址。
77A162F8 > B8 15010000 mov eax,0x115 // 对应操做系统内核中某一函数的编号。
77A162FD BA 0003FE7F mov edx,0x7FFE0300 // 该地方是一个函数,该函数决定了什么方式进零环。
77A16302 FF12 call dword ptr ds:[edx] ; ntdll.KiFastSystemCall
1)_KUSER_SHARED_DATA 在用户层的位置为 0x7FFE0000,该地址为其+0x300位置
+0x300 SystemCall : Uint4B
2)该成员保存着系统调用的函数入口,若是当前CPU支持快速调用。
则存储着ntdll.dll!KiFastSystemCall()函数地址;
若是不支持快速调用,则存储着ntdll.dll!KiIntSystemCall()函数地址。
3)经过实验验证当前CPU是否支持快速调用:
当经过eax=1来执行cupid指令时,处理器特征信息被存放在ecx和edx寄存器中,
其中edx包含了SEP位(11位),该位指明了当前处理器是否支持sysenter/sysexit指令。
以下图,咱们执行cupid指令,获取edx 178BFBFF,拆分 11-8位 B 1011,故其11位为1,支持快速调用。
这也验证了咱们上一篇文章中的分析结果。(最后是快速调用并不是使用中断门)
3、从3环进0环须要哪些寄存器改变
4、ntdll.dll!KiIntSystemCall() 分析
咱们使用ida来分析ntdll.dll!KiIntSystemCall()
.text:77F070C0
.text : 77F070C0 public KiIntSystemCall
.text : 77F070C0 KiIntSystemCall proc near; DATA XREF : .text : off_77EF61B8↑o
.text : 77F070C0
.text : 77F070C0 arg_4 = byte ptr 8
.text : 77F070C0 // 以前调用该函数时 mov eax, 0x115,向eax传入一个函数号
.text : 77F070C0 lea edx, [esp + arg_4] // 当前参数的指针存储在 edx中
.text : 77F070C4 int 2Eh; // 经过中断门的形式进入到内核中
.text:77F070C4; DS:SI->counted CR - terminated command string
.text : 77F070C6 retn
.text : 77F070C6 KiIntSystemCall endp
其在触发 int 2eh中断前用到两个寄存器,一个是内核中调用函数的函数号,另一个就是传入参数的指针。
五. ntdll.dll!KiFastSystemCall()函数分析
当CPU支持快速调用,则使用这个函数。(咱们在上篇文章中已经用到了这个来重构WriteProcessMemory函数)
.text:77F070B0 public KiFastSystemCall
.text:77F070B0 KiFastSystemCall proc near ; DATA XREF: .text:off_77EF61B8↑o
.text:77F070B0 // 以前调用该函数时 mov eax, 0x115,向eax传入一个函数号
.text:77F070B0 mov edx, esp // 将当前堆栈放入edx,用它来存储参数
.text:77F070B2 sysenter
.text:77F070B2 KiFastSystemCall endp
触发sysenter指令后,也用到两个寄存器eax,edx,做用与使用中断同样。
为何叫快速调用?
中断门进入0环,须要的CS、EIP在IDT表中,须要查内存(SS与ESP由IDT表提供);
而CPU若是支持sysenter指令时,操做系统会提早将CS/SS/ESP/EIP的值存储在MSR寄存器中,
sysenter指令执行时,CPU会将MSR寄存器中的值直接写入寄存器中,没有读内存过程,本质时同样的。
下一篇,咱们探究其中的详细细节。