C++ code colored by C++2HTML
內核級利用通用Hook函數方法檢測進程
文章作者:LionD8
信息來源:綠盟科技
內核級利用通用Hook函數方法檢測進程
作者: LionD8
QQ: 10415468
Email: LionD8@126.com
Blog: http:
介紹通用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
具體實現:#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;
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; mov cr0, eax;
pop eax;
};
*pOldAttr = uAttr;
}
VOID EnableWriteProtect( ULONG uOldAttr )
{
_asm
{
push eax;
mov eax, uOldAttr; 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 }
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; 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;
}.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#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) )
{ 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);
}
}
試驗結果:
......
TTPlayer.exe
System
TTPlayer.exe
vrvmon.exe
TTPlayer.exe
System
System
Explorer.EXE
Explorer.EXE
Explorer.EXE
......
測試,編譯環境 2000 Sp4 2000 DDK
沒寫出線程的隱藏進程代碼。不過基本上實現得差不多了,只需要把返回的信息,和Ring3級查詢得到的信息進行適時對比就能查出異常進程了。