X86
在C:WindwosSystem32中存放着系统所有的dll文件,这些dll文件导出相应的系统API接口函数供我们的应用程序调用。其中最重要的四个dll文件:kernel32.dll,user32.dll,gdi32.dll,以及最重要的ntdll.dll(kernel32.dll,user32.dll,gdi32.dll更底层的实现就在ntdll.dll中)。
例如我们调用kernel32.dll中的ReadProcessMemory()函数,此函数在内部只是单纯的调用了ntdll.dll中的NtReadVirtualMemory()函数而已。
其中ntdll.dll中的Nt系列函数都有对应的Zw系列函数,他只是同一个函数体导出的两个不同的名称,实际上是一个函数。例如我们上面提到的NtReadVirtualMemory()就有与其对应的ZwReadVirtualMemory()。此函数会继续调用call ds:[0x7FFE0300],此函数到底是什么呢?我们需要知道一个重要的数据结构:_KUSER_SHARED_DATA。
此结构在3环中对应的虚拟地址为0x7ffe0000,在0环中对应的虚拟地址为0xffdf0000。也就是此结构的物理内存映射了两块虚拟地址,但是两处虚拟地址的访问权限不同,3环0x7ffe0000只有读权限,而0环0xffdf0000处有读写权限。此结构的0x300处的SystemCall是一个函数指针,当我们call ds:[0x7FFE0300]的时候调用的就是此函数的地址。系统会检测当前处理器是否支持sysenter/sysexit快速系统调用指令,如果支持就将在内核中通过0xFFDF0000地址填充SystemCall字段为ntdll!KiFastSystemCall()函数的地址,如果不支持就填充ntdll!KiIntSystemCall()函数的地址。这两个函数通过不同的方式从用户态进入内核中从而调用对应的服务例程。
ntdll!KiIntSystemCall -----> nt!KiSystemService
ntdll!KiIntSystemCall函数先将参数列表地址保存到edx中,通过int 2Eh中断指令借助中断门进入到内核中。
函数进入到内核中需要更改CS,EIP,SS,ESP。其中CS,EIP通过中断门描述符得到,当得到cs后根据cs段选择子在GDT表中找到对应的段描述符得到段基地址。通过TR寄存器在GDT表中得到TSS段描述符,通过TSS得到SS和ESP的值。其中eip对应的函数就是ntoskrnl.exe中的nt!KiSystemService。在跳转到此函数之前,因为是发生权限改变的中断过程,所以会先将原来的SS,ESP压入堆栈,再将标志寄存器(EFLAGS),CS和EIP压入堆栈中。
在分析nt!KiSystemService()函数前我们要知道一个重要的数据结构:_KTRAP_FRAME,此结构在栈中存储了3环所有的环境寄存器等信息,当我们通过中断门进入0环时,就会填充其中0x68 - 0x78字段,0-0x64由nt!KiSystemService填充。
每一个CPU都会维护一个_KPCR结构用来保存CPU对应的状态,在应用层中fs指向线程环境块TEB,在内核层fs指向的是_KPCR。
nt!KiSystemService函数在保存相关的寄存器之后会跳转到nt!KiFastCallEntry中继续执行从而调用对应的系统服务。
nt!KiSystemService:
8053e481 6a00 push 0 //填充ErrCode
8053e483 55 push ebp //保存ebp
8053e484 53 push ebx //保存ebx
8053e485 56 push esi //保存esi
8053e486 57 push edi //保存edi
8053e487 0fa0 push fs //保存fs
8053e489 bb30000000 mov ebx,30h
8053e48e 668ee3 mov fs,bx //修改fs,修改后fs.base为0XFFDFF000,此地址保存的是当前CPU的_KPCR(cpu的一些状态信息)
8053e491 ff3500f0dfff push dword ptr ds:[0FFDFF000h] //将原来的ExceptionList保存
8053e497 c70500f0dfffffffffff mov dword ptr ds:[0FFDFF000h],0FFFFFFFFh //将ExceptionList清空
8053e4a1 8b3524f1dfff mov esi,dword ptr ds:[0FFDFF124h] //获得_KTHREAD(_KPCR的PrcbData的CurrentThread字段)
8053e4a7 ffb640010000 push dword ptr [esi+140h] //保存旧的先前模式(Previous Mode)
8053e4ad 83ec48 sub esp,48h //扩展堆栈
8053e4b0 8b5c246c mov ebx,dword ptr [esp+6Ch]
8053e4b4 83e301 and ebx,1 //得到ecx的值并获得cpl
8053e4b7 889e40010000 mov byte ptr [esi+140h],bl //保存新的先前模式(Previous Mode)
8053e4bd 8bec mov ebp,esp
8053e4bf 8b9e34010000 mov ebx,dword ptr [esi+134h]
8053e4c5 895d3c mov dword ptr [ebp+3Ch],ebx //保存_KTHREAD中的_Trap_Frame
8053e4c8 89ae34010000 mov dword ptr [esi+134h],ebp //设置新的_Trap_Frame(也就是我们正在填充的这个)
8053e4ce fc cld
8053e4cf 8b5d60 mov ebx,dword ptr [ebp+60h]
8053e4d2 8b7d68 mov edi,dword ptr [ebp+68h]
8053e4d5 89550c mov dword ptr [ebp+0Ch],edx //保存调用API的函数的参数的指针
8053e4d8 c74508000ddbba mov dword ptr [ebp+8],0BADB0D00h //保存一个特殊的标志
8053e4df 895d00 mov dword ptr [ebp],ebx
8053e4e2 897d04 mov dword ptr [ebp+4],edi
8053e4e5 f6462cff test byte ptr [esi+2Ch],0FFh //判断_KTHREAD的DebugActive字段是否为-1,如果是说明正在被调试,需要跳到Dr_kss_a处将调试相关的寄存器保存在_Trap_Frame中
8053e4e9 0f858dfeffff jne nt!Dr_kss_a (8053e37c)
8053e4ef fb sti
8053e4f0 e9d8000000 jmp nt!KiFastCallEntry+0x8d (8053e5cd) //跳到nt!KiFastCallEntry中继续执行调用对应的系统服务(Dr_kss_a处的代码最后也会跳到这个位置)
ntdll!KiFastSystemCall -----> nt!KiFastCallEntry
ntdll!KiFastSystemCall先将esp栈顶保存在edx中,然后通过快速系统调用指令sysenter进入内核
sysenter指令通过查询MSR寄存器获得cs,eip,ss,esp。系统会提前将cs,eip,esp的值填入MSR寄存器指定的位置。因为ntdll!KiIntSystemCall是通过int 0x2E中断方式进入内核中的,需要查内存,而ntdll!KiFastSystemCall通过sysenter指令是查的MSR寄存器,查寄存器肯定不查内存要快的多而且不需要对堆栈进行操作速度更快,所以称为快速系统调用。
cs,eip,esp都直接从MSR寄存器指定位置取出,而ss实际等于cs + 8。也就是sysenter要求内核的GDT表中的CS段描述符和SS段描述符相邻。
需要注意的是sysexit指令会令cs = IA32_SYSENTER_CS + 16,SS = IA32_SYSENTER_CS + 24,也就是与sysenter所需的段描述符相邻。而且需要知道无论是sysenter还是sysexit,其在修改cs和ss寄存器时,并没有复制对应的GDT表中的段描述符为其不可见部分,而是直接通过硬编码的方式写入固定值。这样可以避免内存访问和查表查内存,以及权限检查的操作,实现所谓的快速系统调用。
KiFastCallEntry在保存完相关的寄存器后会调用对应的系统服务,
nt!KiFastCallEntry
8053e540 b923000000 mov ecx,23h
8053e545 6a30 push 30h
8053e547 0fa1 pop fs //fs.base = 0xFFDFF000为_KPCR的基地址
8053e549 8ed9 mov ds,cx
8053e54b 8ec1 mov es,cx
8053e54d 8b0d40f0dfff mov ecx,dword ptr ds:[0FFDFF040h] //获得_KPCR.TSS
8053e553 8b6104 mov esp,dword ptr [ecx+4] //esp = _KPCR.TSS.ESP 切换到0环的堆栈
8053e556 6a23 push 23h //设置_KTRAP_FRAME.HardwareSegSs = 0x23
8053e558 52 push edx //保存esp
8053e559 9c pushfd //保存EFLAGS
8053e55a 6a02 push 2
8053e55c 83c208 add edx,8 //edx + 8定位到调用API时的参数地址
8053e55f 9d popfd
8053e560 804c240102 or byte ptr [esp+1],2 //设置保存的EFLAGS的IF位为1
8053e565 6a1b push 1Bh //设置_KTRAP_FRAME.SegCs = 0x1B
8053e567 ff350403dfff push dword ptr ds:[0FFDF0304h] //设置eip = _KUSER_SHARE_DATA.SysCallReturn(ntdll!KiFastSystemCallRet函数的地址)
8053e56d 6a00 push 0 //设置ErrCode
8053e56f 55 push ebp //保存ebp
8053e570 53 push ebx //保存ebx
8053e571 56 push esi //保存esi
8053e572 57 push edi //保存edi
8053e573 8b1d1cf0dfff mov ebx,dword ptr ds:[0FFDFF01Ch] //ebx = _KPCR.SelfPcr(ebx指向_KPCR)
8053e579 6a3b push 3Bh //设置_KTRAP_FRAME.SegFs = 0x3B
8053e57b 8bb324010000 mov esi,dword ptr [ebx+124h] //esi指向KTHREAD
8053e581 ff33 push dword ptr [ebx] //保存ExceptionList
8053e583 c703ffffffff mov dword ptr [ebx],0FFFFFFFFh //清空ExceptionList
8053e589 8b6e18 mov ebp,dword ptr [esi+18h] //ebp = KTHREAD.InitialStack
8053e58c 6a01 push 1 //保存旧的先前模式(Previous Mode)
8053e58e 83ec48 sub esp,48h
8053e591 81ed9c020000 sub ebp,29Ch
8053e597 c6864001000001 mov byte ptr [esi+140h],1 //设置新的先前模式为1
8053e59e 3bec cmp ebp,esp //如果ebp不等于esp就跳到异常处理
8053e5a0 759a jne nt!KiFastCallEntry2+0x47 (8053e53c)
8053e5a2 83652c00 and dword ptr [ebp+2Ch],0 //设置Dr7为0
8053e5a6 f6462cff test byte ptr [esi+2Ch],0FFh
8053e5aa 89ae34010000 mov dword ptr [esi+134h],ebp //KTHREAD.TrapFram = ebp也就是当前_KTRAPFRAM结构的地址
8053e5b0 0f854afeffff jne nt!Dr_FastCallDrSave (8053e400)//判断KTHREAD.DebugActive是否处于调试状态,如果被调试就跳转保存必要的调试寄存器
8053e5b6 8b5d60 mov ebx,dword ptr [ebp+60h]
8053e5b9 8b7d68 mov edi,dword ptr [ebp+68h]
8053e5bc 89550c mov dword ptr [ebp+0Ch],edx //保存调用API时的参数地址
8053e5bf c74508000ddbba mov dword ptr [ebp+8],0BADB0D00h
8053e5c6 895d00 mov dword ptr [ebp],ebx
8053e5c9 897d04 mov dword ptr [ebp+4],edi
8053e5cc fb sti
对比ntdll!KiFastSystemCall和ntdll!KiIntSystemCall。设置两个不同的系统调用内核入口函数nt!KiSystemService和nt!KiFastCallEntry,原因是因为其填充_Trap_Frame结构的方式不同。同时要注意nt!KiFastCallEntry函数会将_Trap_Frame的eip设置为ntdll!KiFastSystemCallRet函数,这样在执行sysexit时就会返回到ntdll!KiFastSystemCallRet函数中,ntdll!KiFastSystemCallRet函数执行一条ret指令在返回到ntdll!XXX函数中。而nt!KiSystemService函数不需要,因为中断方式返回用户会调用iretd指令,可以直接返回到ntdll!XXX函数中。
注意是nt!KiServiceExit()调用nt!KiSystemCallExit()或nt!KiSystemCallExit2(),其会通过检测系统是否支持sysenter快速系统调用来修改跳转代码决定跳到nt!KiSystemCallExit()还是nt!KiSystemCallExit2()。
8053e735 7506 jne nt!KiSystemCallExit2 (8053e73d) //如果不支持sysenter,则此指令会被系统修改为jne nt!KiSystemCallExit
8053e737 5a pop edx
8053e738 59 pop ecx
8053e739 9d popfd
8053e73a ffe2 jmp edx
nt!KiSystemCallExit:
8053e73c cf iretd
kd> u
nt!KiSystemCallExit2:
8053e73d f644240901 test byte ptr [esp+9],1 //测试EFLATGS的TF位是否为0,如果为0说明是在支持sysenter的系统上通过int 0x2e中断调用系统服务的
8053e742 75f8 jne nt!KiSystemCallExit (8053e73c) //对应需要跳到nt!KiSystemCallExit中执行iretd返回用户
8053e744 5a pop edx
8053e745 83c404 add esp,4
8053e748 80642401fd and byte ptr [esp+1],0FDh
8053e74d 9d popfd
8053e74e 59 pop ecx
8053e74f fb sti
内核中的Zw和Nt系列函数
在用户层我们知道ntdll中的每一个Nt系列函数都有与之对应的Zw系列函数,二者只是同一个函数体的不同的两个名称。而在内核层也存在与系统服务Nt系列函数对应的Zw系列函数,只不过并不是每一个Nt系列函数都有与之对应的Zw系列函数,Zw系列函数只是为了提供在内核中调用对应Nt系列函数的一种手段。例如nt!ZwCreateFile(),其通过调用nt!KiSystemService来调用对应的Nt系统服务函数。
X64
x64中3环是通过syscall指令进入到内核层,通过sysret指令返回到用户层。系统再nt!KiInitializeBootStructures函数中初始化两条指令的执行环境。
fffff800`04116291 48b80000000010002300 mov rax,23001000000000h
fffff800`0411629b b9810000c0 mov ecx,0C0000081h //MSR_STAR
fffff800`041162a0 488bd0 mov rdx,rax
fffff800`041162a3 48c1ea20 shr rdx,20h
fffff800`041162a7 0f30 wrmsr
fffff800`041162a9 488d05d070dbff lea rax,[nt!KiSystemCall32 (fffff800`03ecd380)]
fffff800`041162b0 b9830000c0 mov ecx,0C0000083h //MSR_CSTAR
fffff800`041162b5 488bd0 mov rdx,rax
fffff800`041162b8 48c1ea20 shr rdx,20h
fffff800`041162bc 0f30 wrmsr
fffff800`041162be 488d057b73dbff lea rax,[nt!KiSystemCall64 (fffff800`03ecd640)]
fffff800`041162c5 b9820000c0 mov ecx,0C0000082h //MSR_LSTAR
fffff800`041162ca 488bd0 mov rdx,rax
fffff800`041162cd 48c1ea20 shr rdx,20h
fffff800`041162d1 0f30 wrmsr
fffff800`041162d3 b800470000 mov eax,4700h
fffff800`041162d8 b9840000c0 mov ecx,0C0000084h //MSR_SFMASK
fffff800`041162dd 488bd0 mov rdx,rax
fffff800`041162e0 48c1ea20 shr rdx,20h
fffff800`041162e4 0f30 wrmsr
如果我们程序在兼容性(comatibility)下运行也就是在64位系统上运行32位程序,则如果执行syscall指令其就会将MSR_CSTAR的值nt!KiSystemCall32()写入rip寄存器。但是只有在AMD处理器中才会发生,所以为了在Inter处理器中也能运行32位程序,一般需要操作系统实现的时候先从32位模式切换到64位模式然后在执行syscall指令,这样就会将MSR_LSTAR的值nt!KiSystemCall64()写入rip。例如我们在64位系统上运行32位程序调用32位的ntdll!NtWriteFile(),此函数调用fs:0C0h切换到64位模式,然后再执行syscall指令。
正常的64位调用是先将rcx保存到r10中,然后直接执行syscall指令
执行syscall指令会将syscall指令的下一条地址保存到RCX中,这也是为什么要事先将rcx的值先保存的原因。然后将EFLAGS的值保存到R11中,然后用MSR_SFMSK值填充EFLAGS的值,其从MSR_STAR的47:32位中获得cs和ss的值,但是其并不从GDT表中复制cs和ss的不可见部分,而是采用硬编码固定值的方式(这样利于实现快速系统调用)。接着用MSR_LSTAR的值nt!KiSystemCall64()函数设置rip的值,跳转到nt!KiSystemCall64()。
和x86一样,x64也会在进入内核中先保存环境到栈中的_KTRAP_FRAME结构中,注意x64和x86的_KTRAP_FRAME结构不同。
kd> dt _KTRAP_FRAME
nt!_KTRAP_FRAME
+0x000 P1Home : Uint8B
+0x008 P2Home : Uint8B
+0x010 P3Home : Uint8B
+0x018 P4Home : Uint8B
+0x020 P5 : Uint8B
+0x028 PreviousMode : Char
+0x029 PreviousIrql : UChar
+0x02a FaultIndicator : UChar
+0x02b ExceptionActive : UChar
+0x02c MxCsr : Uint4B
+0x030 Rax : Uint8B
+0x038 Rcx : Uint8B
+0x040 Rdx : Uint8B
+0x048 R8 : Uint8B
+0x050 R9 : Uint8B
+0x058 R10 : Uint8B
+0x060 R11 : Uint8B
+0x068 GsBase : Uint8B
+0x068 GsSwap : Uint8B
+0x070 Xmm0 : _M128A
+0x080 Xmm1 : _M128A
+0x090 Xmm2 : _M128A
+0x0a0 Xmm3 : _M128A
+0x0b0 Xmm4 : _M128A
+0x0c0 Xmm5 : _M128A
+0x0d0 FaultAddress : Uint8B
+0x0d0 ContextRecord : Uint8B
+0x0d0 TimeStampCKCL : Uint8B
+0x0d8 Dr0 : Uint8B
+0x0e0 Dr1 : Uint8B
+0x0e8 Dr2 : Uint8B
+0x0f0 Dr3 : Uint8B
+0x0f8 Dr6 : Uint8B
+0x100 Dr7 : Uint8B
+0x108 DebugControl : Uint8B
+0x110 LastBranchToRip : Uint8B
+0x118 LastBranchFromRip : Uint8B
+0x120 LastExceptionToRip : Uint8B
+0x128 LastExceptionFromRip : Uint8B
+0x108 LastBranchControl : Uint8B
+0x110 LastBranchMSR : Uint4B
+0x130 SegDs : Uint2B
+0x132 SegEs : Uint2B
+0x134 SegFs : Uint2B
+0x136 SegGs : Uint2B
+0x138 TrapFrame : Uint8B
+0x140 Rbx : Uint8B
+0x148 Rdi : Uint8B
+0x150 Rsi : Uint8B
+0x158 Rbp : Uint8B
+0x160 ErrorCode : Uint8B
+0x160 ExceptionFrame : Uint8B
+0x160 TimeStampKlog : Uint8B
+0x168 Rip : Uint8B
+0x170 SegCs : Uint2B
+0x172 Fill0 : UChar
+0x173 Logging : UChar
+0x174 Fill1 : [2] Uint2B
+0x178 EFlags : Uint4B
+0x17c Fill2 : Uint4B
+0x180 Rsp : Uint8B
+0x188 SegSs : Uint2B
+0x18a Fill3 : Uint2B
+0x18c CodePatchCycle : Int4B
分析一下nt!KiSystemCall64()是如何填充_KTRAP_FRAME结构的,注意syscall指令并不会设置rsp的值,也不会保存rsp的值。其rsp是由nt!KiSystemCall64()在入口处保存和设置的
nt!KiSystemCall64:
fffff800`03ecd640 0f01f8 swapgs //将gs的值与MSR[c0000102]值互换,之后gs.base指向_KPCR
fffff800`03ecd643 654889242510000000 mov qword ptr gs:[10h],rsp //保存rsp在_KPCR.UserRsp中
fffff800`03ecd64c 65488b2425a8010000 mov rsp,qword ptr gs:[1A8h] //设置rsp = _KPCR.RspBase
fffff800`03ecd655 6a2b push 2Bh //设置_KTRAP_FRAME.SegSs = 2Bh
fffff800`03ecd657 65ff342510000000 push qword ptr gs:[10h] //保存RSP
fffff800`03ecd65f 4153 push r11 //保存EFLAGES
fffff800`03ecd661 6a33 push 33h //设置_KTRAP_FRAME.SegCs = 33h
fffff800`03ecd663 51 push rcx //保存rip
fffff800`03ecd664 498bca mov rcx,r10 //还原rcx的值
fffff800`03ecd667 4883ec08 sub rsp,8
fffff800`03ecd66b 55 push rbp //保存rbp
fffff800`03ecd66c 4881ec58010000 sub rsp,158h
fffff800`03ecd673 488dac2480000000 lea rbp,[rsp+80h]
fffff800`03ecd67b 48899dc0000000 mov qword ptr [rbp+0C0h],rbx //保存rbx
fffff800`03ecd682 4889bdc8000000 mov qword ptr [rbp+0C8h],rdi //保存rdi
fffff800`03ecd689 4889b5d0000000 mov qword ptr [rbp+0D0h],rsi //保存rsi
fffff800`03ecd690 c645ab02 mov byte ptr [rbp-55h],2
fffff800`03ecd694 65488b1c2588010000 mov rbx,qword ptr gs:[188h] //获取_KPCR.CurrentThread
fffff800`03ecd69d 0f0d8bd8010000 prefetchw [rbx+1D8h] //保存_KTHREAD.TrapFrame
fffff800`03ecd6a4 0fae5dac stmxcsr dword ptr [rbp-54h]
fffff800`03ecd6a8 650fae142580010000 ldmxcsr dword ptr gs:[180h]
fffff800`03ecd6b1 807b0300 cmp byte ptr [rbx+3],0
fffff800`03ecd6b5 66c785800000000000 mov word ptr [rbp+80h],0
fffff800`03ecd6be 0f848c000000 je nt!KiSystemCall64+0x110 (fffff800`03ecd750)
fffff800`03ecd6c4 488945b0 mov qword ptr [rbp-50h],rax //保存rax
fffff800`03ecd6c8 48894db8 mov qword ptr [rbp-48h],rcx //保存rcx
fffff800`03ecd6cc 488955c0 mov qword ptr [rbp-40h],rdx //保存rdx
fffff800`03ecd6d0 f6430303 test byte ptr [rbx+3],3 //测试_KTHREAD.DebugActive是否处于调试状态
fffff800`03ecd6d4 4c8945c8 mov qword ptr [rbp-38h],r8 //保存r8
fffff800`03ecd6d8 4c894dd0 mov qword ptr [rbp-30h],r9 //保存r9
fffff800`03ecd6dc 7405 je nt!KiSystemCall64+0xa3 (fffff800`03ecd6e3) //如果处于调试状态就跳转到指定代码处保存调试寄存器
fffff800`03ecd6de e80d140000 call nt!KiSaveDebugRegisterState (fffff800`03eceaf0)
fffff800`03ecd6e3 f6430380 test byte ptr [rbx+3],80h
fffff800`03ecd6e7 7442 je nt!KiSystemCall64+0xeb (fffff800`03ecd72b)
fffff800`03ecd6e9 b9020100c0 mov ecx,0C0000102h
fffff800`03ecd6ee 0f32 rdmsr //得到旧的gs的值
fffff800`03ecd6f0 48c1e220 shl rdx,20h
fffff800`03ecd6f4 480bc2 or rax,rdx
fffff800`03ecd6f7 483983b8000000 cmp qword ptr [rbx+0B8h],rax //比较_KTHREAD.Teb 与 gs的值
fffff800`03ecd6fe 742b je nt!KiSystemCall64+0xeb (fffff800`03ecd72b)
fffff800`03ecd700 483983b0010000 cmp qword ptr [rbx+1B0h],rax //比较_KTHREAD.TebMappedLowVa 与 gs的值
fffff800`03ecd707 7422 je nt!KiSystemCall64+0xeb (fffff800`03ecd72b)
fffff800`03ecd709 488b93b8010000 mov rdx,qword ptr [rbx+1B8h]
fffff800`03ecd710 0fba6b4c0b bts dword ptr [rbx+4Ch],0Bh
fffff800`03ecd715 66ff8bc4010000 dec word ptr [rbx+1C4h]
fffff800`03ecd71c 48898280000000 mov qword ptr [rdx+80h],rax
fffff800`03ecd723 fb sti
fffff800`03ecd724 e8170b0000 call nt!KiUmsCallEntry (fffff800`03ece240)
fffff800`03ecd729 eb0f jmp nt!KiSystemCall64+0xfa (fffff800`03ecd73a)
fffff800`03ecd72b f6430340 test byte ptr [rbx+3],40h
fffff800`03ecd72f 7409 je nt!KiSystemCall64+0xfa (fffff800`03ecd73a)
fffff800`03ecd731 f00fbaab0001000008 lock bts dword ptr [rbx+100h],8
fffff800`03ecd73a 488b45b0 mov rax,qword ptr [rbp-50h]
fffff800`03ecd73e 488b4db8 mov rcx,qword ptr [rbp-48h]
fffff800`03ecd742 488b55c0 mov rdx,qword ptr [rbp-40h]
fffff800`03ecd746 4c8b45c8 mov r8,qword ptr [rbp-38h]
fffff800`03ecd74a 4c8b4dd0 mov r9,qword ptr [rbp-30h]
fffff800`03ecd74e 6690 xchg ax,ax
fffff800`03ecd750 fb sti
fffff800`03ecd751 48898be0010000 mov qword ptr [rbx+1E0h],rcx
fffff800`03ecd758 8983f8010000 mov dword ptr [rbx+1F8h],eax
接着调用nt!KiSystemServiceStart----》nt!KiSystemServiceRepeat----》call r10调用系统服务
调用完对应的系统服务后调用nt!KiSystemServiceExit(),此函数执行swapgs还原gs的值后执行sysretq返回到用户层。