KiFastCallEntry from wrk 注释、简单分析
创建时间:2010-02-02
文章类别:内核研究
文章大小:8972 Bytes
基本分为以下基本 1. 转配TrapFrame 2. 获取服务表地址,测试是否需要转化为GUI线程。 3. R3参数复制到内核栈 (顺便说一下某安全助手就是在这一步的最后进行HOOK的) 4. 系统调用 5. 返回操作。。。大部分在EXIT_ALL宏中。。。没看懂。。。 ;eax指向服务编号 ;edx指向当前用户栈、edx+8为参数列表 _KiFastCallEntry proc ; ; Sanitize the segment registers ; 初始化fs指向PCR的选择子,ds、es指向ring3数据选择子 mov ecx, KGDT_R3_DATA OR RPL_MASK push KGDT_R0_PCR pop fs mov ds, ecx mov es, ecx ; ; When we trap into the kernel via fast system call we start on the DPC stack. We need ; shift to the threads stack before enabling interrupts. ; 从PCR->tss.TssEsp0中获得系统栈的地址,赋值给esp。 ; 所以sysenter指令中设置的esp其实没有用处, MS还很happy给它起名叫DPC stack >_< mov ecx, PCR[PcTss] ; mov esp, [ecx]+TssEsp0 ; 在这里建立_KTRAP_FRAME, 也就是intel执行int指令时在系统堆栈中建立的自陷框架 ; 具体结构请dt push KGDT_R3_DATA OR RPL_MASK ; Push user SS HardwareSegSs push edx ; Push ESP HardwareEsp pushfd ; EFlags Kfsc10: push 2 ; Sanitize eflags, clear direction, NT etc add edx, 8 ; (edx) -> arguments 调整edx指向可爱的参数们 popfd ; 设置eflag寄存器为2, 有兴趣请自行查询其意义... .errnz(EFLAGS_INTERRUPT_MASK AND 0FFFF00FFh) or byte ptr [esp+1], EFLAGS_INTERRUPT_MASK/0100h ; Enable interrupts in eflags 打开用户态EFlags的中断 push KGDT_R3_CODE OR RPL_MASK ; Push user CS SegCs push dword ptr ds:[USER_SHARED_DATA+UsSystemCallReturn] ; push return address Eip返回R3时就从这里开始执行 push 0 ; put pad dword for error on stack ErrCode push ebp ; save the non-volatile registers 各寄存器值 push ebx ; push esi ; push edi ; mov ebx, PCR[PcSelfPcr] ; Get PRCB address push KGDT_R3_TEB OR RPL_MASK ; Push user mode FS 用户态FS指向的是TEB选择子 mov esi, [ebx].PcPrcbData+PbCurrentThread ; get current thread address ; ; Save the old exception list in trap frame and initialize a new empty ; exception list. ; push [ebx].PcExceptionList ; save old exception list 从PCR中获得ExceptionList mov [ebx].PcExceptionList, EXCEPTION_CHAIN_END ; set new empty list mov ebp, [esi].ThInitialStack ;ebp是内核栈顶(最高的地址) ; ; Save the old previous mode in trap frame, allocate remainder of trap frame, ; and set the new previous mode. ; push MODE_MASK ; Save previous mode as user 设置PreviousPreviousMode为UserMode(1) sub esp,TsPreviousPreviousMode ; allocate remainder of trap frame 分配了48h字节, 也就是DbgEbp到Eax所占据的空间 sub ebp, NPX_FRAME_LENGTH + KTRAP_FRAME_LENGTH mov byte ptr [esi].ThPreviousMode,MODE_MASK ; set new previous mode of user 设置ETHREAD中的PREVIOUSMode未UserMode ; ; Now the full trap frame is build. ; Calculate initial stack pointer from thread initial stack to contain NPX and trap. ; If this isn't the same as esp then we are a VX86 thread and we are rejected ; cmp ebp, esp ;这里比较堆栈是否正确建立、谁告诉我NPX在哪建的?? jne short Kfsc91 ;正确就不会跳 ; ; Set the new trap frame address. ; ebp指向了自陷框架 ; esi指向ETHREAD and dword ptr [ebp].TsDr7, 0 test byte ptr [esi].ThDebugActive, 0ffh ; See if we need to save debug registers mov [esi].ThTrapFrame, ebp ; set new trap frame address 在ETHREAD中设置自陷框架地址 jnz Dr_FastCallDrSave ; if nz, debugging is active on thread Dr_FastCallDrReturn: ; SET_DEBUG_DATA ; Note this destroys edi sti ; enable interrupts ?FpoValue = 0 ;到这里TRAP_FRAME设置完毕了, 开始调用系统服务的准备工作 ; (eax) = Service number 服务号 ; (edx) = Callers stack pointer 指向R3中的参数 ; (esi) = Current thread address 指向ETHREAD结构 ; ; All other registers have been saved and are free. ;所有寄存器随便用, 以为在框架中已经设置好备份了 ; ; Check if the service number within valid range ; 首先验证服务号是否在合法的范围内 _KiSystemServiceRepeat: ; 服务号由16位组成 4位表号A + 12位服务索引B ; 从ethread中取得ServiceTable数组,取第A项为服务表地址 mov edi, eax ; copy system service number shr edi, SERVICE_TABLE_SHIFT ; isolate service table number SERVICE_TABLE_SHIFT == 8 and edi, SERVICE_TABLE_MASK ; SERVICE_TABLE_MASK == 30h mov ecx, edi ; save service table number add edi, [esi]+ThServiceTable ; compute service descriptor address mov ebx, eax ; save system service number and eax, SERVICE_NUMBER_MASK ; isolate service table offset SERVICE_NUMBER_MASK == 0xFFF ; eax为服务索引 ; ; If the specified system service number is not within range, then attempt ; to convert the thread to a GUI thread and retry the service dispatch. ; cmp eax, [edi]+SdLimit ; check if valid service SdLimit==8 jae Kss_ErrorHandler ; if ae, try to convert to GUI thread ; 服务号太大了, 就试试win32k模块吧 ; ; If the service is a GUI service and the GDI user batch queue is not empty, ; then call the appropriate service to flush the user batch. ; cmp ecx, SERVICE_TABLE_TEST ; test if GUI service SERVICE_TABLE_TEST==10h jne short Kss40 ; if ne, not GUI service 非win32k系统调用应该跳走 mov ecx, PCR[PcTeb] ; get current thread TEB address xor ebx, ebx ; get number of batched GDI calls KiSystemServiceAccessTeb: or ebx, [ecx]+TbGdiBatchCount ; may cause an inpage exception jz short Kss40 ; if z, no batched calls push edx ; save address of user arguments push eax ; save service number call [_KeGdiFlushUserBatch] ; flush GDI user batch pop eax ; restore service number pop edx ; restore address of user arguments ; ; The arguments are passed on the stack. Therefore they always need to get ; copied since additional space has been allocated on the stack for the ; machine state frame. Note that we don't check for the zero argument case - ; copy is always done regardless of the number of arguments because the ; zero argument case is very rare. ; ; 将R3的参数复制到R0堆栈中 Kss40: inc dword ptr PCR[PcPrcbData+PbSystemCalls] ; system calls ; edx R3参数首地址 ; edi SSDT or shadowSSDT ; eax 服务索引 FPOFRAME ?FpoValue, 0 mov esi, edx ; (esi)->User arguments mov ebx, [edi]+SdNumber ; get argument table address SdNumber==0ch xor ecx, ecx mov cl, byte ptr [ebx+eax] ; (ecx) = argument size mov edi, [edi]+SdBase ; get service table address mov ebx, [edi+eax*4] ; (ebx)-> service routine ; 在系统堆栈上申请参数空间 sub esp, ecx ; allocate space for arguments shr ecx, 2 ; (ecx) = number of argument DWORDs mov edi, esp ; (edi)->location to receive 1st arg cmp esi, _MmUserProbeAddress ; check if user address jae kss80 ; if ae, then not user address ; 从R3复制到R0的堆栈上 KiSystemServiceCopyArguments: rep movsd ; copy the arguments to top of stack. ; Since we usually copy more than 3 ; arguments. rep movsd is faster than ; mov instructions. ; ; Make actual call to system service ; 这里开始系统调用 kssdoit: call ebx ; call system service kss60: ; 完成系统调用,开始返回R3 kss61: ; ; Upon return, (eax)= status code. This code may also be entered from a failed ; KiCallbackReturn call. ; ebp 指向自陷框架 mov esp, ebp ; 销毁系统调用时的参数 ; ; Restore old trap frame address from the current trap frame. ; kss70: mov ecx, PCR[PcPrcbData+PbCurrentThread] ; get current thread address mov edx, [ebp].TsEdx ; 自陷框架中的edx指向原R3堆栈顶 mov [ecx].ThTrapFrame, edx ; ; ; System service's private version of KiExceptionExit ; (Also used by KiDebugService) ; ; Check for pending APC interrupts, if found, dispatch to them ; (saving eax in frame first). ; public _KiServiceExit _KiServiceExit: cli ; disable interrupts DISPATCH_USER_APC ebp, ReturnCurrentEax ; 派遣用户APC ; ; Exit from SystemService ; EXIT_ALL NoRestoreSegs, NoRestoreVolatile ;返回操作