PUBLIC _KiFastCallEntry
_KiFastCallEntry        proc



; Sanitize the segment registers
        mov     ecx, KGDT_R3_DATA OR RPL_MASK    
        push    KGDT_R0_PCR
        pop     fs              ;fs指向pcr
        mov     ds, ecx     ;ds指向用户数据段
        mov     es, ecx     ;ex指向用户数据段

; 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.


        mov     ecx, PCR[PcTss]        ;从pcr中取得tts
        mov     esp, [ecx]+TssEsp    ;从tts中获得当前线程堆栈的esp

        push    KGDT_R3_DATA OR RPL_MASK   ; Push user SS(压入用户态线程的堆栈段寄存器ss)
        push    edx                         ; Push ESP(压入esp)
        pushfd                                ;压入标志寄存器(eflags)
        push    2                           ; Sanitize eflags, clear direction, NT etc;设置eflags为2,,表示所有标志位都是0(中断被关闭)
        add     edx, 8                      ; (edx) -> arguments(移动栈顶指针到参数)
        popfd                               ;
        or      byte ptr [esp+1], EFLAGS_INTERRUPT_MASK/0100h ; Enable interrupts in eflags(打开用户态eflags的中断)

        push    KGDT_R3_CODE OR RPL_MASK    ; Push user CS
        push    dword ptr ds:[USER_SHARED_DATA+UsSystemCallReturn] ; push return address(使其返回时指向用户空间

        push    0                           ; put pad dword for error on stack
        push    ebp                         ; save the non-volatile registers
        push    ebx                         ;
        push    esi                         ;
        push    edi                         ;保存现场
        mov     ebx, PCR[PcSelfPcr]         ; Get PRCB address(获得prcb地址)
        push    KGDT_R3_TEB OR RPL_MASK     ; Push user mode FS
        mov     esi, [ebx].PcPrcbData+PbCurrentThread   ; get current thread address(使esi指向当前线程的KTHREAD)

; Save the old exception list in trap frame and initialize a new empty
; exception list.

        push    [ebx].PcExceptionList       ; save old exception list(保存老的ExceptionList)
        mov     [ebx].PcExceptionList, EXCEPTION_CHAIN_END ; set new empty list(新的ExceptionList为空白)
        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(保存先前模式)
        sub     esp,TsPreviousPreviousMode ; allocate remainder of trap frame(分配了48h字节, 也就是DbgEbp到Eax所占据的空间

        mov     byte ptr [esi].ThPreviousMode,MODE_MASK ; set new previous mode of user;设置ETHREAD


; 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     ;判断堆栈内压入的值是否正确(这些堆栈内压入的东西,就好比一个trap框架)
        jne     short Kfsc91    ;不正确跳转

; Set the new trap frame address.
        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


; (eax) = Service number      ;系统服务序号
; (edx) = Callers stack pointer      ;参数指针
; (esi) = Current thread address       ;线程ETHREAD
; All other registers have been saved and are free.
; Check if the service number within valid range


        mov     edi, eax                ; copy system service number
        shr     edi, SERVICE_TABLE_SHIFT ; isolate service table number
        and     edi, SERVICE_TABLE_MASK ;
        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

; 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
        jae     Kss_ErrorHandler        ; if ae, try to convert to GUI thread

; 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
        jne     short Kss40             ; if ne, not GUI service(如果不是GUI跳走)
        mov     ecx, PCR[PcTeb]         ; get current thread TEB address(获得TEB)
        xor     ebx, ebx                ; get number of batched GDI calls

        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.


Kss40:  inc     dword ptr PCR[PcPrcbData+PbSystemCalls] ; system calls

if DBG

        mov     ecx, [edi]+SdCount      ; get count table address
        jecxz   short @f                ; if zero, table not specified
        inc     dword ptr [ecx+eax*4]   ; increment service count
@@:                                     ;


FPOFRAME ?FpoValue, 0

        mov     esi, edx                ; (esi)->User arguments
        mov     ebx, [edi]+SdNumber     ; get argument table address
        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

KiSystemServiceCopyArguments:          ;向ring0堆栈中拷贝参数起始于标号
        rep     movsd                   ; copy the arguments to top of stack.
                                        ; Since we usually copy more than 3
                                        ; arguments.  rep movsd is faster than
                                        ; mov instructions.

; Check if low resource usage should be simulated.

if DBG

        test    _MmInjectUserInpageErrors, 2
        jz      short @f
        stdCall _MmTrimProcessMemory, <0>
        jmp     short kssdoit

        mov     eax,PCR[PcPrcbData+PbCurrentThread]
        mov     eax,[eax]+ThApcState+AsProcess
        test    dword ptr [eax]+PrFlags,0100000h ; is this a inpage-err process?
        je      short @f
        stdCall _MmTrimProcessMemory, <0>


; Make actual call to system service

        call    ebx                     ; call system service


; Check for return to user mode at elevated IRQL.

if DBG

        test    byte ptr [ebp]+TsSegCs,MODE_MASK ; test if previous mode user
        jz      short kss50b            ; if z, previous mode not user
        mov     esi,eax                 ; save return status
        CurrentIrql                     ; get current IRQL
        or      al,al                   ; check if IRQL is passive level
        jnz     kss100                  ; if nz, IRQL not passive level
        mov     eax,esi                 ; restore return status

; Check if kernel APCs are disabled or a process is attached.
        mov     ecx,PCR[PcPrcbData+PbCurrentThread] ; get current thread address
        mov     dl,[ecx]+ThApcStateIndex ; get APC state index
        or      dl,dl                   ; check if process attached
        jne     kss120                  ; if ne, process is attached
        mov     edx,[ecx]+ThCombinedApcDisable ; get kernel APC disable
        or      edx,edx                 ; check if kernel APCs disabled
        jne     kss120                  ; if ne, kernel APCs disabled.
kss50b:                                 ;



; Upon return, (eax)= status code. This code may also be entered from a failed
; KiCallbackReturn call.

        mov     esp, ebp                ; deallocate stack space for arguments

; Restore old trap frame address from the current trap frame.

kss70:  mov     ecx, PCR[PcPrcbData+PbCurrentThread] ; get current thread address
        mov     edx, [ebp].TsEdx        ; restore previous trap frame address
        mov     [ecx].ThTrapFrame, edx  ;


HOOK KiFastCallEntry 执行KeLowerIrql总是出错


[code=c]////////////////////////////////////////////////////////////////rn//文件描述:HOOK KiFastCallEntry函数,对NtCreateProcess进行日志rn// 记录rn//作者 :jimmyrn//时间 :2013-12-31rn////////////////////////////////////////////////////////////////rnrn////////////////////////////////////////////////////////////////rn//文件包含rn#includern#include rn////////////////////////////////////////////////////////////////rnrnrn////////////////////////////////////////////////////////////////rn//函数声明:rn//入口函数rnextern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver,PUNICODE_STRING pRegPath);rn//卸载函数rnextern "C" VOID DriverUnload(PDRIVER_OBJECT pDriverObj);rnextern "C" NTSTATUS MyNtOpenProcess(rn PHANDLE ProcessHandle,rn ACCESS_MASK DesiredAccess,rn POBJECT_ATTRIBUTES ObjectAttributes,rn PCLIENT_ID ClientId);rnextern "C" void MyFunction();rn//NtOpenProcess函数指针类型定义rntypedef NTSTATUS (*NTOPENPROCESS)(rn PHANDLE ProcessHandle,rn ACCESS_MASK DesiredAccess,rn POBJECT_ATTRIBUTES ObjectAttributes,rn PCLIENT_ID ClientIdrn );rn#define MEM_TAG 'HOOK'rn#define BYTE unsigned charrn#ifdef ALLOC_PRAGMArn#pragma alloc_text(PAGE, DriverUnload)rn#pragma alloc_text(NONPAGED,MyNtOpenProcess)rn#pragma alloc_text(NONPAGED,MyFunction)rn#endifrn#define LOCKEDCODE code_seg()rn////////////////////////////////////////////////////////////////rnrnrnrn////////////////////////////////////////////////////////////////rn//全局变量rnNTOPENPROCESS OldNtOpenProcess=0;rnULONG HookAddr=0;//call ebx函数地址rnBYTE jmpCode[8]='\xe9',0,0,0,0,0x90,0x90,0x90;//使用jmp offset的地址代替call ebx,以及进行字节对其rnBYTE callMyCode[6]=0x68,0x00,0x00,0x00,0x00,0x90;;//调用MyFunction的代码 call MyFunctionrnBYTE orgCode[5]='\x6c','\x45','\xf6','\xd3','\xff';rnULONG DyNamicMem=0;rnULONG JmpRet=0;;//跳回原地址继续执行rnULONG midJmp=0;rnrn////////////////////////////////////////////////////////////////rnrnrnrnrn////////////////////////////////////////////////////////////////rn//函数名称:logrn//函数描述:记录驱动执行步骤rnVOID log(PUNICODE_STRING Msg)rnrn HANDLE hFile=0;rn NTSTATUS status;rn UNICODE_STRING FilePath=0;rn //在C盘中进行日志记录rn RtlInitUnicodeString(&FilePath,L"\\??\\C:\\lo.txt");rn OBJECT_ATTRIBUTES obj=0;rn InitializeObjectAttributes(&obj,&FilePath,OBJ_CASE_INSENSITIVE |OBJ_KERNEL_HANDLE,NULL,NULL);rn IO_STATUS_BLOCK ioBlock=0;rn status=ZwCreateFile(&hFile,GENERIC_ALL,&obj,&ioBlock,NULL,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ,FILE_OPEN_IF ,FILE_NON_DIRECTORY_FILE,NULL,0);rn if(!NT_SUCCESS(status))rn rn return;rn rn //查询文件大小rn FILE_STANDARD_INFORMATION fileStandardInfo=0;rn status=ZwQueryInformationFile(hFile,&ioBlock,&fileStandardInfo,sizeof(FILE_STANDARD_INFORMATION),FileStandardInformation);rn if(!NT_SUCCESS(status))rn rn return;rn rn LARGE_INTEGER la=fileStandardInfo.EndOfFile;rn //开始写入文件rn ZwWriteFile(hFile,NULL,NULL,NULL,&ioBlock,Msg->Buffer,Msg->Length,&la,NULL);rn //关闭文件rn ZwClose(hFile);rnrn////////////////////////////////////////////////////////////////rnrn////////////////////////////////////////////////////////////////rn//函数名称:MyNtCreateProcessrnNTSTATUS MyNtOpenProcess(rn PHANDLE ProcessHandle,rn ACCESS_MASK DesiredAccess,rn POBJECT_ATTRIBUTES ObjectAttributes,rn PCLIENT_ID ClientId)rnrn UNICODE_STRING Msg=00;rn RtlInitUnicodeString(&Msg,L"process has been created \r\n");rn log(&Msg);rn return OldNtOpenProcess( ProcessHandle,rn DesiredAccess,rn ObjectAttributes,rn ClientId);rnrn////////////////////////////////////////////////////////////////rnrnrnrnrn////////////////////////////////////////////////////////////////rn//函数名称:MyFunctionrn__declspec(naked) void MyFunction()rnrn _asmrn rn mov edx,ebx;rn cmp ebx,OldNtOpenProcess;rn jne Label1;rn mov ebx,MyNtOpenProcessrnLabel1:rn ;代替原来的rn ;call ebxrn ;test byte ptr [ebp+6Ch],1rn ;je nt!KiFastCallEntry+0x164 (8445c464)rn rn call ebx; rn test byte ptr[ebp+6ch],1; rn je Label2;rn ;条件不成立,则直接返回下一指令地址rn jmp [JmpRet];rnLabel2:rn jmp [midJmp];rnrn rnrn////////////////////////////////////////////////////////////////rnrnrnrn////////////////////////////////////////////////////////////////rn//函数名称:DriverEntryrn//函数描述:驱动的入口函数,在这里找到KiFastCallEntry函数地址,rn// 并加以改变rn#pragma LOCKEDCODErnextern "C"NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver,PUNICODE_STRING pRegPath)rn rn UNICODE_STRING Msg=0;rn KIRQL oldIrql;rn NTSTATUS status=STATUS_SUCCESS;rn pDriver->DriverUnload=DriverUnload;rn //进行记录rn RtlInitUnicodeString(&Msg,L"we hava enter the DriverEntry\r\n");rn log(&Msg);rn //开始得到NtCreateProcess函数的地址rn UNICODE_STRING NtFuncName=0;rn ULONG uKiFastCallEntry=0;rn RtlInitUnicodeString(&NtFuncName, L"NtOpenProcess" );rn //通过函数名称,获得函数地址rn OldNtOpenProcess=(NTOPENPROCESS)MmGetSystemRoutineAddress( &NtFuncName);rn if(OldNtOpenProcess==NULL)rn rn RtlInitUnicodeString(&Msg,L"get the old Function Address failed exit...\r\n");rn log(&Msg);rn return status;rn rn RtlInitUnicodeString(&Msg,L"we have found the NtOpenProcess\r\n");rn log(&Msg);rn //在这里获取KiFastCallEntry的函数地址rn __asmrn rn pushad;rn pushfd;rn mov ecx,0x176;rn rdmsr;rn mov uKiFastCallEntry,eax;//获得KiFastCallEntry函数地址rn mov midJmp,eax;rn xor ecx,ecx;索引位置初始化为0rnLabel:;根据特征码查找相应的插入位置rn cmp ecx,0x300;最多查找 0x300个位置rn je Label3;结束返回rn mov edx,dword ptr[eax];rn cmp edx,0xe9c1e12b; rn je Label2;rn inc ecx;rn inc eax;rn jmp Label;rnLabel2:rn add eax,0x44;定位到call ebxrn mov HookAddr,eax;HookAddr定位到call ebx的位置 rnLabel3:rn popfd;rn popad;rn rn midJmp+=0x164;rn if(uKiFastCallEntry==0)rn rn RtlInitUnicodeString(&Msg,L"sorry we could not found the position of KiFastCallEntry\r\n");rn log(&Msg);rn return status;rn rn RtlInitUnicodeString(&Msg,L"we have found the position of KiFastCallEntry\r\n");rn log(&Msg);rn if(HookAddr==0)rn rn RtlInitUnicodeString(&Msg,L"sorry,we cant find the position of 'call ebx',\r\n");rn log(&Msg);rn return status;rn rn RtlInitUnicodeString(&Msg,L"we have found the position of'call ebx',\r\n");rn log(&Msg); rn //开辟内存空间rn DyNamicMem=(ULONG)ExAllocatePoolWithTag(NonPagedPool,6,MEM_TAG);rn if(DyNamicMem==NULL)rn rn RtlInitUnicodeString(&Msg,L"sorry allocate the memory failed...\r\n");rn log(&Msg);rn return status;rn rn //要求跳转到我动态申请的内存中去rn *(ULONG*)&jmpCode[1]=(ULONG)(DyNamicMem)-(HookAddr+5);//要求跳转到DynamicMem中去rn //填充callMyCode,在动态内存中要求执行MyFunctionrn *(ULONG*)&callMyCode[1]=(ULONG)MyFunction;rn //要求跳回callrn JmpRet=(ULONG)HookAddr+0x8;rn //确保当前irql等于或小于DISPATCH_LEVELrn ASSERT(KeGetCurrentIrql()<=DISPATCH_LEVEL);rn //提升中断请求等级rn //oldIrql=KeRaiseIrqlToDpcLevel();rnrn KeRaiseIrql(DISPATCH_LEVEL,&oldIrql);rn //关闭中断rn _asmrn rn cli;rn mov eax,cr0;rn and eax,not 10000h;rn mov cr0,eaxrn rn //进行hook操作rn RtlCopyMemory((PVOID)DyNamicMem,callMyCode,6);//该指令也已成功执行了! rn RtlCopyMemory((PVOID)HookAddr,jmpCode,8);rn //开启中断rn _asmrn rn mov eax,cr0;rn or eax,10000h;rn mov cr0,eax;rn sti;rn rn //回复先前中断请求等级,在驱动开发过程中最重要的是注意IRQ所在等级,如果是DISPATCH_LEVEL则应该注意该函数体必须在非分页内存中,如果是PASSIVE_LEVEL则可以在扉 页内存中rn KeLowerIrql(oldIrql);rn RtlInitUnicodeString(&Msg,L"hook install successfully\r\n");rn log(&Msg);rn return status;rnrn////////////////////////////////////////////////////////////////rnrnrn////////////////////////////////////////////////////////////////rn//函数名称:DriverUnloadrn//函数描述:将钩子卸载掉rnVOID DriverUnload(PDRIVER_OBJECT pDriver)rnrn if(HookAddr!=0)rn rn if(OldNtOpenProcess!=0)rn rn //只有同时满足这两个条件钩子才会安装rn KIRQL oldIrql=KeRaiseIrqlToDpcLevel();rn //关闭中断rn _asmrn rn cli;rn mov eax,cr0;rn and eax,not 10000h;rn mov cr0,eax;rn rn //进行还原操作 rn RtlCopyMemory((PVOID)HookAddr,orgCode,6);rn //关闭中断rn _asmrn rn mov eax,cr0;rn or eax,10000h;rn mov cr0,eax;rn sti;rn rn //回复先前的中断请求等级rn KeLowerIrql(oldIrql);rn //释放内存rn ExFreePool((PVOID)DyNamicMem);rn rn rn UNICODE_STRING Msg=0;rn RtlInitUnicodeString(&Msg,L"DriverUnload has been called\r\n"); rnrn////////////////////////////////////////////////////////////////[/code]rn很简单的内核模块,但是总是在KeLowerIrql出错,网上有人说在执行DISPATCH_LEVEL中断时必须是非分页内存中,这一点,我也做到了,可是为什么还是出错?rnwindbg给出的提示是这样的,由于新手,不是很理解,求助大神rn*** Fatal System Error: 0x00000050rn (0xF08B9192,0x00000000,0x8445442C,0x00000002)rnrnWARNING: This break is not a step/trace completion.rnThe last command has been cleared to preventrnaccidental continuation of this unrelated event.rnCheck the event, location and thread before resuming.rnBreak instruction exception - code 80000003 (first chance)rnrnA fatal system error has occurred.rnDebugger entered on first try; Bugcheck callbacks have not been invoked.rnrnA fatal system error has occurred.rnrnConnected to Windows 7 7600 x86 compatible target at (Sat Jan 4 09:44:18.167 2014 (UTC + 8:00)), ptr64 FALSErnLoading Kernel Symbolsrn...............................................................rn................................................................rn..............................rnLoading User Symbolsrn................................................................rn......................rnLoading unloaded module listrn...........rn*******************************************************************************rn* *rn* Bugcheck Analysis *rn* *rn*******************************************************************************rnrnUse !analyze -v to get detailed debugging information.rnrnBugCheck 50, f08b9192, 0, 8445442c, 2rnProbably caused by : memory_corruptionrnFollowup: memory_corruptionrnnt!RtlpBreakWithStatusInstruction:rn8447c394 cc int 3rnkd> !analyze -vrn* Bugcheck Analysis *rnPAGE_FAULT_IN_NONPAGED_AREA (50)rnInvalid system memory was referenced. This cannot be protected by try-except,rnit must be protected by a Probe. Typically the address is just plain bad or itrnis pointing at freed memory.rnArguments:rnArg1: f08b9192, memory referenced.rnArg2: 00000000, value 0 = read operation, 1 = write operation.rnArg3: 8445442c, If non-zero, the instruction address which referenced the bad memoryrn address.rnArg4: 00000002, (reserved)rnrnDebugging Details:rn------------------