• 系统调用学习


    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返回到用户层。

    参考:https://bbs.pediy.com/thread-216995.htm

  • 相关阅读:
    [转]三五个人十来条枪 如何走出软件作坊成为开发正规军
    [转]asp.net页面字体变大问题总结
    [练手7]传值和传引用区别
    [练手3]选择排序
    [练手5]希尔排序
    oracle用select for update 中断后无法操作表的处理:杀掉SESSION
    [转]最省时管理法:让你一天随意
    [练手4]插入排序
    没有对“C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files”的写访问权限
    NUnit单元测试编写指南
  • 原文地址:https://www.cnblogs.com/revercc/p/14695986.html
Copyright © 2020-2023  润新知