模式切换的流程

内核提供的服务都通过NTDLL模块被应用程序使用,使用方法为通过调用一组系统dll中的API,间接的通过ntdll的存根函数来调用内核提供的系统服务
系统的服务例程运行在内核模式下,但是应用程序调用是在用户模式,如果想要使用则需要进行模式切换,这个切换工作在系统模块NTDLL中已经实现,依赖于硬件体系结构

进入R0

1.通过中断门进入R0

中断号为0x2e,通过“ int 0x2e”进入内核,在IDT 中查找2e 表项,IDTEntry 包含了一个段选择符和中断例程的段内偏移,所以,处理器还需要在GDT 中再查找一次表项,得到段选择符指定的段的虚拟基地址。段基地址加上中断例程偏移,最终得到中断例程的虚拟地址。

kd> dq idtr l40
kd> dq idtr l40
...
8003f540  00000000`00080000 00000000`00080000
8003f550  8053ee00`0008e9de 8053ee00`0008eae0
8003f560  8053ee00`0008ec80 8053ee00`0008f5c0
8003f570  8053ee00`0008e481 80548e00`00081780
//!idt -a 可以直接查看idt各项的地址
//8003f560 2e ,选择子为8,偏移为8053e481
kd> dq gdtr l20
8003f000  00000000`00000000 00cf9b00`0000ffff    //base 0
uf 8053e481 

在这里插入图片描述

通过图中可以形象的观察出通过中断进入内核的工作过程,尽管只需要一条指令,但是模式切换的过程中涉及多次的内存访问和权限检查以及查表,导致开销很大,从Pentium II 处理器开始,引入了新的切换方式,sysenter/syseixt

2.通过sysenter进入:

sysenter通过使用三个MSR寄存器指定跳转目标和栈位置
在这里插入图片描述

//查看
kd> rdmsr 174 //msr地址
msr[174] = 00000000`00000008

sysenter内部逻辑

  • IA32_SYSENTER_CS和IA32_SYSENTER_EIP装载cs和eip寄存器中
  • IA32_SYSENTER_CS+8和IA32_SYSENTER_ESP装载cs和esp寄存器中
  • 切换特权级0
  • 清除EFLAGS的VM标志
  • 执行eip
    注意: 代码段和栈段在GDT中需要相邻。即:(cs)GDTR+8x,(ss)GDTR+8x+8;代码段描述符需要指定基地址为0、段范围达4GB的特权级0的段,有可执行可读权限;栈描述符不同点是需要具有读写访问和向上拓展的权限。
    sysexit内部逻辑
  • IA32_SYSENTER_CS+16装载到CS寄存器
  • edx寄存器里的指针装载到eip寄存器
  • IA32_SYSENTER_CS+24装载ss寄存器
  • 将ecx 寄存器中的指针装载到esp 寄存器中
  • 切换特权级3
  • 执行eip

注意: sysexit要求IA32_SYSENTER_CS+16和IA32_SYSENTER_CS+24在GDT中的位置紧跟在sysenter中使用的项后
在这里插入图片描述

_KUSER_SHARED_DATA

ReadVirtualMemory 逆向

...
push    [ebp+lpBaseAddress] ; BaseAddress
push    [ebp+hProcess]  ; ProcessHandle
call    ds:__imp__NtReadVirtualMemory@20 ; NtReadVirtualMemory(x,x,x,x,x)
...
//在导入表查看NtReadVirtualMemory,可以看到是在NTDLL导入的NtReadVirtualMemory
7C801418		NtReadVirtualMemory	ntdll
//在NTDLL查找
mov     eax, 0BAh       ; NtReadVirtualMemory
mov     edx, 7FFE0300h
call    dword ptr [edx]
retn    14h
//eax放入一个编号,edx放入一个地址,然后call这个地址 (SystemCall地址)

用户空间和内核空间有一块共享区域,_KUSER_SHARED_DATA,R0和R3的线性地址映射在同一物理页上。大小为 4 KB

内核起始地址内核结束地址用户起始地址用户结束地址
32 系统0xFFDF00000xFFDF0FFF0x7FFE00000x7FFE0FFF
64 系统0xFFFFF780`000000000xFFFFF780`00000FFF0x7FFE00000x7FFE0FFF

通过挂载指定进程查看

.process 861e45e0  
kd> dt _KUSER_SHARED_DATA 7FFE0000
ntdll!_KUSER_SHARED_DATA
   +0x000 TickCountLow     : 0x612c
   +0x004 TickCountMultiplier : 0xfa00000
   +0x008 InterruptTime    : _KSYSTEM_TIME
   +0x014 SystemTime       : _KSYSTEM_TIME
   +0x020 TimeZoneBias     : _KSYSTEM_TIME
   +0x02c ImageNumberLow   : 0x14c
   +0x02e ImageNumberHigh  : 0x14c
   +0x030 NtSystemRoot     : [260] 0x43
   +0x238 MaxStackTraceDepth : 0
   +0x23c CryptoExponent   : 0
   +0x240 TimeZoneId       : 0
   +0x244 Reserved2        : [8] 0
   +0x264 NtProductType    : 1 ( NtProductWinNt )
   +0x268 ProductTypeIsValid : 0x1 ''
   +0x26c NtMajorVersion   : 5
   +0x270 NtMinorVersion   : 1
   +0x274 ProcessorFeatures : [64]  ""
   +0x2b4 Reserved1        : 0x7ffeffff
   +0x2b8 Reserved3        : 0x80000000
   +0x2bc TimeSlip         : 0
   +0x2c0 AlternativeArchitecture : 0 ( StandardDesign )
   +0x2c8 SystemExpirationDate : _LARGE_INTEGER 0x0
   +0x2d0 SuiteMask        : 0x110
   +0x2d4 KdDebuggerEnabled : 0x3 ''
   +0x2d5 NXSupportPolicy  : 0x2 ''
   +0x2d8 ActiveConsoleId  : 0
   +0x2dc DismountCount    : 0
   +0x2e0 ComPlusPackage   : 0xffffffff
   +0x2e4 LastSystemRITEventTickCount : 0x1ed87
   +0x2e8 NumberOfPhysicalPages : 0x3ff6c
   +0x2ec SafeBootMode     : 0 ''
   +0x2f0 TraceLogging     : 0
   +0x2f8 TestRetInstruction : 0xc3
   +0x300 SystemCall       : 0x7c92e4f0    //调用地址
   +0x304 SystemCallReturn : 0x7c92e4f4
   +0x308 SystemCallPad    : [3] 0
   +0x320 TickCount        : _KSYSTEM_TIME
   +0x320 TickCountQuad    : 0
   +0x330 Cookie           : 0x31c5ba42
//当前支持sysenter所以里面存的是kifastsystemcall地址,查看systemcall内容
kd> uf 0x7c92e4f0
ntdll!KiFastSystemCall:
7c92e4f0 8bd4            mov     edx,esp
7c92e4f2 0f34            sysenter
7c92e4f4 c3              ret

如果当前CPU不支持sysenter,则里面存放的是KiIntSystemCall函数的地址

_KiIntSystemCall@0 proc near

arg_4= byte ptr  8

lea     edx, [esp+arg_4]
int     2Eh             ; DOS 2+ internal - EXECUTE COMMAND
                        ; DS:SI -> counted CR-terminated command string
retn
_KiIntSystemCall@0 endp

总结一下,应用程序通过系统dll调用时,会通过ntdll找到系统存根函数,然后判断是否支持快速调用,通过KiIntSystemCall/KiFastSystemCall进入,如果是由int指令进入,则KiSystemService获得控制权(IDT中描述符指定);如果是由sysenter进入,则KiFastCallEntry获得控制权(IA32_SYSENTER_EIP指定)。

返回R3

从R0返回R3,则是由_KiSystemCallExit返回,在_KiSystemCallExit中,会判断是否支持快速调用,通过不同的方式进行返回

_KiSystemCallExit:
iretd ; 中断返回
_KiSystemCallExit2:
test dword ptr [esp+8], EFLAGS_TF ; 测试陷阱标志
jne short _KiSystemCallExit ; 若是陷阱,则转到_KiSystemCallExit
pop edx ; 弹出eip,此EIP是_KiFastCallEntry填充的
add esp, 4 ; 移除cs
and dword ptr [esp], NOT EFLAGS_INTERRUPT_MASK ; 禁止eflags中的中断标志
popfd
pop ecx ; 弹出esp
sti ; 恢复中断,因为sysexit指令不会恢复中断标志
sysexit ; 返回用户模式

判断是否支持快速调用可以通过指令 cupid来查看,当通过eax=1 来执行cpuid 指令时,处理器的特征信息被存放在ecx 和edx 寄存器中,其中edx 包含了一个SEP 位(SysEnter/SysExit Present,第11位),该位指明了当前处理器是否支持sysenter/sysexit 指令。

整体流程

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值