介绍通用Hook的一点思想:
在系统内核级中,MS的很多信息都没公开,包括函数的参数数目,每个参数的类型等。在系统内核中,访问了大量的寄存器,而很多寄存器的值,是上层调用者提供的。如果值改变系统就会变得不稳定。很可能出现不可想象的后果。另外有时候对需要Hook的函数的参数不了解,所以不能随便就去改变它的堆栈,如果不小心也有可能导致蓝屏。所以Hook的最佳原则是在自己的Hook函数中呼叫原函数的时候,所有的寄存器值,堆栈里面的值和Hook前的信息一样。这样就能保证在原函数中不会出错。一般我们自己的Hook的函数都是写在C文件里面的。例如Hook的目标函数KiReadyThread。
那么一般就自己实现一个:
MyKiReadyThread(...)
{
......
call KiReadyThread
......
}
但是用C编译器编译出来的代码会出现一个堆栈帧:
Push ebp
mov ebp,esp
这就和我们的初衷不改变寄存器的数违背了。所以我们可以自己用汇编来实现MyKiReadyThread。
_func@0 proc pushad ;保存通用寄存器 call _cfunc@0 ;这里是在进入原来函数前进行的一些处理。 popad ;恢复通用寄存器 push eax mov eax,[esp+4] ;得到系统在call 目标函数时入栈的返回地址。 mov ds:_OrgRet,eax ;保存在一个临时变量中 pop eax mov [esp],retaddr ;把目标函数的返回地址改成自己的代码空间的返回地址,使其返回 后能接手继续的处理 jmp _OrgDestFunction ;跳到原目标函数中 retaddr: pushad ;原函数处理完后保存寄存器 call _HookDestFunction@0 ;再处理 popad ;回复寄存器 jmp ds:_OrgRet ;跳到系统调用目标函数的下一条指令。 _func@0 endp 当我们要拦截目标API的时候,只要修改原函数头5个字节的机器为一个JMP _func就行了。 然后把原来的5字节保存。在跳入原函数时,恢复那5个字节即可。 Hook KiReadyThread检测系统中的进程: 在线程调度抢占的的时候会调用KiReadyThread,它的原型为 VOID FASTCALL KiReadyThread (IN PRKTHREAD Thread) 在进入KiReadyThread时,ecx指向Thread。 所以完全可以Hook KiReadyThread 然后用ecx的值得到但前线程的进程信息。 KiReadyThread没被ntosknrl.exe导出,所以通过硬编码来。在2000Sp4中地址为0x8043141f 具体实现: // 1.cpp #ifdef __cplusplus extern "C" { #endif #include "ntddk.h" #include "string.h" #include "ntifs.h" #include "stdio.h" #define FILE_DEVICE_EVENT 0x8000 #define IOCTL_PASSBUF / CTL_CODE(FILE_DEVICE_EVENT, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS) void DriverUnload (IN PDRIVER_OBJECT pDriverObject); NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath); void cfunc (); void HookDestFunction(); NTSTATUS DeviceIoControlDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP pIrp); extern void func(); void ResumeDestFunction(); const WCHAR devLink[] = L"//??//MyEvent"; const WCHAR devName[] = L"//Device//MyEvent"; UNICODE_STRING devNameUnicd; UNICODE_STRING devLinkUnicd; ULONG OrgDestFunction = (ULONG)0x8043141f; //KiReadyThread char JmpMyCode [] = {0xE9,0x00,0x00,0x00,0x00}; char OrgCode [5]; char OutBuf[128][16]; int Count = 0; ULONG orgcr0; #ifdef __cplusplus } #endif VOID DisableWriteProtect( PULONG pOldAttr) { ULONG uAttr; _asm { push eax; mov eax, cr0; mov uAttr, eax; and eax, 0FFFEFFFFh; // CR0 16 BIT = 0 mov cr0, eax; pop eax; }; *pOldAttr = uAttr; //保存原有的 CRO 属性 } VOID EnableWriteProtect( ULONG uOldAttr ) { _asm { push eax; mov eax, uOldAttr; //恢复原有 CR0 属性 mov cr0, eax; pop eax; }; } NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING RegistryPath) { NTSTATUS Status; PDEVICE_OBJECT pDevice; DbgPrint("DriverEntry called!/n"); RtlInitUnicodeString (&devNameUnicd, devName ); RtlInitUnicodeString (&devLinkUnicd, devLink ); Status = IoCreateDevice ( pDriverObject, 0, &devNameUnicd, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevice ); if( !NT_SUCCESS(Status)) { DbgPrint(("Can not create device./n")); return Status; } Status = IoCreateSymbolicLink (&devLinkUnicd, &devNameUnicd); if( !NT_SUCCESS(Status)) { DbgPrint(("Cannot create link./n")); return Status; } pDriverObject->DriverUnload = DriverUnload; pDriverObject->MajorFunction[IRP_MJ_CREATE] = pDriverObject->MajorFunction[IRP_MJ_CLOSE] = pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceIoControlDispatch; pDriverObject->DriverUnload = DriverUnload; * ( (ULONG*) (JmpMyCode+1) ) = (ULONG)func - (ULONG)OrgDestFunction - 5; memcpy(OrgCode,(char*)OrgDestFunction,5); HookDestFunction(); return STATUS_SUCCESS; } void DriverUnload (IN PDRIVER_OBJECT pDriverObject) { NTSTATUS status; ResumeDestFunction(); if(pDriverObject->DeviceObject != NULL) { status=IoDeleteSymbolicLink( &devLinkUnicd ); if ( !NT_SUCCESS( status ) ) { DbgPrint(( "IoDeleteSymbolicLink() failed/n" )); } IoDeleteDevice( pDriverObject->DeviceObject ); } } void DisplayName(PKTHREAD Thread) { PKPROCESS Process = Thread->ApcState.Process; PEPROCESS pEprocess = (PEPROCESS)Process; DbgPrint("ImageFileName = %s /n",pEprocess->ImageFileName); sprintf(OutBuf[Count++],"%s",pEprocess->ImageFileName); } void cfunc (void) { ULONG PKHeader=0; __asm { mov PKHeader,ecx //ecx寄存器是KiReadyThread中的PRKTHREAD参数 } ResumeDestFunction(); if ( PKHeader != 0 && Count < 128 ) { DisplayName((PKTHREAD)PKHeader); } } void HookDestFunction() { DisableWriteProtect(&orgcr0); memcpy((char*)OrgDestFunction,JmpMyCode,5); EnableWriteProtect(orgcr0); } void ResumeDestFunction() { DisableWriteProtect(&orgcr0); memcpy((char*)OrgDestFunction,OrgCode,5); EnableWriteProtect(orgcr0); } NTSTATUS DeviceIoControlDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP pIrp ) { PIO_STACK_LOCATION irpStack; NTSTATUS status; PVOID inputBuffer; ULONG inputLength; PVOID outputBuffer; ULONG outputLength; OBJECT_HANDLE_INFORMATION objHandleInfo; status = STATUS_SUCCESS; // 取出IOCTL请求代码 irpStack = IoGetCurrentIrpStackLocation(pIrp); switch (irpStack->MajorFunction) { case IRP_MJ_CREATE : DbgPrint("Call IRP_MJ_CREATE/n"); break; case IRP_MJ_CLOSE: DbgPrint("Call IRP_MJ_CLOSE/n"); break; case IRP_MJ_DEVICE_CONTROL: DbgPrint("IRP_MJ_DEVICE_CONTROL/n"); inputLength=irpStack->Parameters.DeviceIoControl.InputBufferLength; outputLength=irpStack->Parameters.DeviceIoControl.OutputBufferLength; switch (irpStack->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_PASSBUF: { RtlCopyMemory(pIrp->UserBuffer, OutBuf, 20*16); memset(OutBuf,0,128*16); Count = 0; break; } default: break; } default: DbgPrint("Call IRP_MJ_UNKNOWN/n"); break; } pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = 0; IoCompleteRequest (pIrp, IO_NO_INCREMENT); return status; } // 1.asm .386 .model small .data _OrgRet dd 0 .code public _func@0 extrn _cfunc@0:near extrn _HookDestFunction@0:near extrn _OrgDestFunction:DWORD _func@0 proc pushad call _cfunc@0 popad push eax mov eax,[esp+4] mov ds:_OrgRet,eax pop eax mov [esp],retaddr jmp _OrgDestFunction retaddr: pushad call _HookDestFunction@0 popad jmp ds:_OrgRet _func@0 endp END // // app.cpp // #include <windows.h> #include <stdio.h> #define FILE_DEVICE_EVENT 0x8000 #define CTL_CODE( DeviceType, Function, Method, Access ) ( / ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) / ) #define FILE_ANY_ACCESS 0 #define METHOD_BUFFERED 0 #define FILE_DEVICE_UNKNOWN 0x00000022 #define IOCTL_PASSBUF / CTL_CODE(FILE_DEVICE_EVENT, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS) int main() { HANDLE hDevice; bool status; ULONG dwReturn; char outbuf[129][16]; hDevice = NULL; m_hCommEvent = NULL; hDevice = CreateFile( ".//MyEvent", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hDevice == INVALID_HANDLE_VALUE) { printf("createfile wrong/n"); getchar(); return 0; } while(1) { memset(outbuf,0,129*16); status =DeviceIoControl(hDevice, IOCTL_PASSBUF, NULL, 0, &outbuf, 128*16, &dwReturn,NULL); if( !status) { printf("IO wrong+%d/n", GetLastError()); getchar(); return 0; } int c=0; while( *((char*)(&outbuf)+c*16) ) { //把csrss.exe和自身进程信息跳过,因为会产生有大量的信息。 if ( strcmp((char*)(&outbuf)+c*16,"app.exe") && / strcmp((char*)(&outbuf)+c*16,"csrss.exe") ) printf("%s/n",(char*)(&outbuf)+c*16); c++; } Sleep(1); } }