信息来源:http://www.whitecell.org
原始连接:http://delphi.ktop.com.tw/TOPIC.ASP?TOPIC_ID=50997
內核級HOOK的幾種實現與應用
Author : sinister
Email : sinister@whitecell.org
HomePage: http://www.whitecell.org
實現內核級 HOOK 對於攔截、分析、跟蹤系統內核起著致關重要的作用。實現的方法不同意味著應用側重點的不同。
如想要攔截 NATIVE API 那麼可能常用的就是 HOOK SERVICE TABLE 的方法。
如果要分析一些系統調用,那麼可能想到用 HOOK INT 2E 中斷來實現。
如果想要攔截或跟蹤其他內核 DRIVER 的調用,那麼就要用到HOOK PE 的方法來實現。
這裏我們更注重的是實現,原理方面已有不少高手在網上發表過文章。
大家可以結合起來讀。
下面以我寫的幾個實例程式來講解一下各種方法的實現。
錯誤之處還望各位指正。
1、HOOK SERVICE TABLE 方法:
這種方法對於攔截 NATIVE API 來說用的比較多。
原理就是通過替換系統導
出的一個 SERVICE TABLE 中相應的 NATIVE API 的位址來達到攔截的目的。
因為此方法較為簡單,網上也有不少資料來介紹。
所以這裏就不給出實例程式了。SERVICE TABLE 的結構如下:
typedef struct ServiceDescriptorEntry {
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase;
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
2、HOOK INT 2E 方法:
這種方法對於跟蹤、分析系統調用來說用的比較多。原理是通過替換 IDT
表中的 INT 2E 中斷,使之指向我們自己的中斷服務處理常式來實現的。
掌握此方法需要你對保護模式有一定的基礎。下面的程式演示了這一過程。
/*****************************************************************
檔案名 : WssHookInt2e.c
描述 : 系統調用跟蹤
作者 : sinister
最後修改日期 : 2002-11-02
*****************************************************************/
#include "ntddk.h"
#include "string.h"
#define DWORD unsigned __int32
#define WORD unsigned __int16
#define BYTE unsigned __int8
#define BOOL __int32
#define LOWORD(l) ((WORD)(l))
#define HIWORD(l) ((WORD)(((DWORD)(l) >> 16) & 0xFFFF))
#define LOBYTE(w) ((BYTE)(w))
#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF))
#define MAKELONG(a, b) ((LONG) (((WORD) (a)) | ((DWORD) ((WORD) (b))) << 16))
#define SYSTEMCALL 0x2e
#define SYSNAME "System"
#define PROCESSNAMELEN 16
#pragma pack(1)
//定義 IDTR
typedef struct tagIDTR {
WORD IDTLimit;
WORD LowIDTbase;
WORD HiIDTbase;
}IDTR, *PIDTR;
//定義 IDT
typedef struct tagIDTENTRY{
WORD OffsetLow;
WORD selector;
BYTE unused_lo;
unsigned char unused_hi:5;
unsigned char DPL:2;
unsigned char P:1;
WORD OffsetHigh;
} IDTENTRY, *PIDTENTRY;
#pragma pack()
DWORD OldInt2eService;
ULONG ProcessNameOffset;
TCHAR ProcessName[PROCESSNAMELEN];
static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject);
ULONG GetProcessNameOffset();
VOID GetProcessName( PCHAR Name );
VOID InstallNewInt2e();
VOID UninstallNewInt2e();
VOID __fastcall NativeApiCall()
{
KIRQL OldIrql;
DWORD ServiceID;
DWORD ProcessId;
__asm mov ServiceID,eax;
ProcessId = (DWORD)PsGetCurrentProcessId();
GetProcessName(ProcessName);
KeRaiseIrql(HIGH_LEVEL, &OldIrql); // 提升當前的 IRQL 級別防止被中斷
switch ( ServiceID )
{
case 0x20:
DbgPrint("NEWINT2E: ProcessName: %s; ProcessID: %d; Native Api: NtCreateFile() \n",ProcessName,ProcessId);
break;
case 0x2b:
DbgPrint("NEWINT2E: ProcessName: %s; ProcessID: %d; Native Api: NtCreateSection() \n",ProcessName,ProcessId);
break;
case 0x30:
DbgPrint("NEWINT2E: ProcessName: %s; ProcessID: %d; Native Api: NtCreateToken() \n",ProcessName,ProcessId);
break;
}
KeLowerIrql(OldIrql); //恢復原始 IRQL
}
__declspec(naked) NewInt2eService()
{
__asm{
pushad
pushfd
push fs
mov bx,0x30
mov fs,bx
push ds
push es
sti
call NativeApiCall; // 調用記錄函數
cli
pop es
pop ds
pop fs
popfd
popad
jmp OldInt2eService; //跳到原始 INT 2E 繼續工作
}
}
VOID InstallNewInt2e()
{
IDTR idtr;
PIDTENTRY OIdt;
PIDTENTRY NIdt;
//得到 IDTR 中得段界限與基底位址
__asm {
sidt idtr;
}
//得到IDT基底位址
OIdt = (PIDTENTRY)MAKELONG(idtr.LowIDTbase,idtr.HiIDTbase);
//保存原來的 INT 2E 服務常式
OldInt2eService = MAKELONG(OIdt[SYSTEMCALL].OffsetLow,OIdt[SYSTEMCALL].OffsetHigh);
NIdt = &(OIdt[SYSTEMCALL]);
__asm {
cli
lea eax,NewInt2eService; //得到新的 INT 2E 服務常式偏移
mov ebx, NIdt;
mov [ebx],ax; //INT 2E 服務常式低 16 位元
shr eax,16 //INT 2E 服務常式高 16 位元
mov [ebx+6],ax;
lidt idtr //裝入新的 IDT
sti
}
}
VOID UninstallNewInt2e()
{
IDTR idtr;
PIDTENTRY OIdt;
PIDTENTRY NIdt;
__asm {
sidt idtr;
}
OIdt = (PIDTENTRY)MAKELONG(idtr.LowIDTbase,idtr.HiIDTbase);
NIdt = &(OIdt[SYSTEMCALL]);
_asm {
cli
lea eax,OldInt2eService;
mov ebx, NIdt;
mov [ebx],ax;
shr eax,16
mov [ebx+6],ax;
lidt idtr
sti
}
}
// 驅動入口
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )
{
UNICODE_STRING nameString, linkString;
PDEVICE_OBJECT deviceObject;
NTSTATUS status;
HANDLE hHandle;
int i;
//卸載驅動
DriverObject->DriverUnload = DriverUnload;
//建立設備
RtlInitUnicodeString( &nameString, L"\\Device\\WssHookInt2e" );
status = IoCreateDevice( DriverObject,
0,
&nameString,
FILE_DEVICE_UNKNOWN,
0,
TRUE,
&deviceObject
);
if (!NT_SUCCESS( status ))
return status;
RtlInitUnicodeString( &linkString, L"\\DosDevices\\WssHookInt2e" );
status = IoCreateSymbolicLink (&linkString, &nameString);
if (!NT_SUCCESS( status ))
{
IoDeleteDevice (DriverObject->DeviceObject);
return status;
}
for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
DriverObject->MajorFunction = MydrvDispatch;
}
DriverObject->DriverUnload = DriverUnload;
ProcessNameOffset = GetProcessNameOffset();
InstallNewInt2e();
return STATUS_SUCCESS;
}
//處理設備物件操作
static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0L;
IoCompleteRequest( Irp, 0 );
return Irp->IoStatus.Status;
}
VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject)
{
UNICODE_STRING nameString;
UninstallNewInt2e();
RtlInitUnicodeString( &nameString, L"\\DosDevices\\WssHookInt2e" );
IoDeleteSymbolicLink(&nameString);
IoDeleteDevice(pDriverObject->DeviceObject);
return;
}
ULONG GetProcessNameOffset()
{
PEPROCESS curproc;
int i;
curproc = PsGetCurrentProcess();
//
// Scan for 12KB, hopping the KPEB never grows that big!
//
for( i = 0; i < 3*PAGE_SIZE; i++ ) {
if( !strncmp( SYSNAME, (PCHAR) curproc + i, strlen(SYSNAME) )) {
return i;
}
}
//
// Name not found - oh, well
//
return 0;
}
VOID GetProcessName( PCHAR Name )
{
PEPROCESS curproc;
char *nameptr;
ULONG i;
if( ProcessNameOffset ) {
curproc = PsGetCurrentProcess();
nameptr = (PCHAR) curproc + ProcessNameOffset;
strncpy( Name, nameptr, 16 );
} else {
strcpy( Name, "???");
}
}
3、 HOOK PE 方法
這種方法對於攔截、分析其他內核驅動的函數調用來說用的比較多。
原理
是根據替換 PE 格式導出表中的相應函數來實現的。
此方法中需要用到一些小技巧。
如內核模式並沒有直接提供類似應用層的 GetModuleHandl()、GetProcAddress() 等函數來獲得模組的位址。
那麼我們就需要自己來編寫,這裏用到了一個未公開的函數與結構。
ZwQuerySystemInformation 與 SYSTEM_MODULE_INFORMATION 來實現得到模組的基底位址。
這樣我們就可以根據PE 格式來枚舉導出表中的函數來替換了。
但這又引出了一個問題,那就是從WINDOWS 2000 後內核資料的頁屬性都是唯讀的,不能更改。
內核模式也沒有提供類似應用層的 VirtualProtectEx() 等函數來修改頁面屬性。
那麼也需要我們自己來編寫。
因為我們是在內核模式所以我們可以通過修改 cr0 寄存器的的防寫位元來達到我們的目的。
這樣我們所期望的攔截內核模式函數的功能便得以實現。
此方法需要你對 PE 格式有一定的基礎。下面的程式演示了這一過程。
/*****************************************************************
檔案名 : WssHookPE.c
描述 : 攔截內核函數
作者 : sinister
最後修改日期 : 2002-11-02
*****************************************************************/
#include "ntddk.h"
#include "windef.h"
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation,
SystemProcessorInformation,
SystemPerformanceInformation,
SystemTimeOfDayInformation,
SystemNotImplemented1,
SystemProcessesAndThreadsInformation,
SystemCallCounts,
SystemConfigurationInformation,
SystemProcessorTimes,
SystemGlobalFlag,
SystemNotImplemented2,
SystemModuleInformation,
SystemLockInformation,
SystemNotImplemented3,
SystemNotImplemented4,
SystemNotImplemented5,
SystemHandleInformation,
SystemObjectInformation,
SystemPagefileInformation,
SystemInstructionEmulationCounts,
SystemInvalidInfoClass1,
SystemCacheInformation,
SystemPoolTagInformation,
SystemProcessorStatistics,
SystemDpcInformation,
SystemNotImplemented6,
SystemLoadImage,
SystemUnloadImage,
SystemTimeAdjustment,
SystemNotImplemented7,
SystemNotImplemented8,
SystemNotImplemented9,
SystemCrashDumpInformation,
SystemExceptionInformation,
SystemCrashDumpStateInformation,
SystemKernelDebuggerInformation,
SystemContextSwitchInformation,
SystemRegistryQuotaInformation,
SystemLoadAndCallImage,
SystemPrioritySeparation,
SystemNotImplemented10,
SystemNotImplemented11,
SystemInvalidInfoClass2,
SystemInvalidInfoClass3,
SystemTimeZoneInformation,
SystemLookasideInformation,
SystemSetTimeSlipEvent,
SystemCreateSession,
SystemDeleteSession,
SystemInvalidInfoClass4,
SystemRangeStartInformation,
SystemVerifierInformation,
SystemAddVerifier,
SystemSessionProcessesInformation
} SYSTEM_INFORMATION_CLASS;
typedef struct tagSYSTEM_MODULE_INFORMATION {
ULONG Reserved[2];
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT Index;
USHORT Unknown;
USHORT LoadCount;
USHORT ModuleNameOffset;
CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
#define IMAGE_NT_SIGNATURE 0x50450000 // PE00
#define IMAGE_NT_SIGNATURE1 0x00004550 // 00EP
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
//
// Optional header format.
//
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
//
// NT additional fields.
//
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS;
//
// Section header format.
//
#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
#define IMAGE_SIZEOF_SECTION_HEADER 40
//
// Export Format
//
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
#define BASEADDRLEN 10
NTSYSAPI
NTSTATUS
NTAPI
ZwQuerySystemInformation(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
typedef NTSTATUS (* ZWCREATEFILE)(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength
);
ZWCREATEFILE OldZwCreateFile;
static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject);
VOID DisableWriteProtect( PULONG pOldAttr);
VOID EnableWriteProtect( ULONG ulOldAttr );
FARPROC HookFunction( PCHAR pModuleBase, PCHAR pHookName, FARPROC pHookFunc );
NTSTATUS
HookNtCreateFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength
);
PCHAR MyGetModuleBaseAddress( PCHAR pModuleName )
{
PSYSTEM_MODULE_INFORMATION pSysModule;
ULONG uReturn;
ULONG uCount;
PCHAR pBuffer = NULL;
PCHAR pName = NULL;
NTSTATUS status;
UINT ui;
CHAR szBuffer[BASEADDRLEN];
PCHAR pBaseAddress;
status = ZwQuerySystemInformation( SystemModuleInformation, szBuffer, BASEADDRLEN, &uReturn );
pBuffer = ( PCHAR )ExAllocatePool( NonPagedPool, uReturn );
if ( pBuffer )
{
status = ZwQuerySystemInformation( SystemModuleInformation, pBuffer, uReturn, &uReturn );
if( status == STATUS_SUCCESS )
{
uCount = ( ULONG )*( ( ULONG * )pBuffer );
pSysModule = ( PSYSTEM_MODULE_INFORMATION )( pBuffer + sizeof( ULONG ) );
for ( ui = 0; ui < uCount; ui++ )
{
pName = MyStrchr( pSysModule->ImageName, '\\' );
if ( !pName )
{
pName = pSysModule->ImageName;
}
else {
pName++;
}
if( !_stricmp( pName, pModuleName ) )
{
pBaseAddress = ( PCHAR )pSysModule->Base;
ExFreePool( pBuffer );
return pBaseAddress;
}
pSysModule ++;
}
}
ExFreePool( pBuffer );
}
return NULL;
}
FARPROC HookFunction( PCHAR pModuleBase, PCHAR HookFunName, FARPROC HookFun )
{
PIMAGE_DOS_HEADER pDosHdr;
PIMAGE_NT_HEADERS pNtHdr;
PIMAGE_SECTION_HEADER pSecHdr;
PIMAGE_EXPORT_DIRECTORY pExtDir;
UINT ui,uj;
PCHAR FunName;
DWORD *dwAddrName;
DWORD *dwAddrFun;
FARPROC pOldFun;
ULONG uAttrib;
pDosHdr = ( PIMAGE_DOS_HEADER )pModuleBase;
if ( IMAGE_DOS_SIGNATURE == pDosHdr->e_magic )
{
pNtHdr = ( PIMAGE_NT_HEADERS )( pModuleBase + pDosHdr->e_lfanew );
if( IMAGE_NT_SIGNATURE == pNtHdr->Signature || IMAGE_NT_SIGNATURE1 == pNtHdr->Signature )
{
pSecHdr = ( PIMAGE_SECTION_HEADER )( pModuleBase + pDosHdr->e_lfanew + sizeof( IMAGE_NT_HEADERS ) );
for ( ui = 0; ui < (UINT)pNtHdr->FileHeader.NumberOfSections; ui++ )
{
if ( !strcmp( pSecHdr->Name, ".edata" ) )
{
pExtDir = ( PIMAGE_EXPORT_DIRECTORY )( pModuleBase + pSecHdr->VirtualAddress );
dwAddrName = ( PDWORD )(pModuleBase + pExtDir->AddressOfNames );
dwAddrFun = ( PDWORD )(pModuleBase + pExtDir->AddressOfFunctions );
for ( uj = 0; uj < (UINT)pExtDir->NumberOfFunctions; uj++ )
{
FunName = pModuleBase + *dwAddrName;
if( !strcmp( FunName, HookFunName ) )
{
DbgPrint(" HOOK %s()\n",FunName);
DisableWriteProtect( &uAttrib );
pOldFun = ( FARPROC )( pModuleBase + *dwAddrFun );
*dwAddrFun = ( PCHAR )HookFun - pModuleBase;
EnableWriteProtect( uAttrib );
return pOldFun;
}
dwAddrName ++;
dwAddrFun ++;
}
}
pSecHdr++;
}
}
}
return NULL;
}
// 驅動入口
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )
{
UNICODE_STRING nameString, linkString;
PDEVICE_OBJECT deviceObject;
NTSTATUS status;
HANDLE hHandle;
PCHAR pModuleAddress;
int i;
//卸載驅動
DriverObject->DriverUnload = DriverUnload;
//建立設備
RtlInitUnicodeString( &nameString, L"\\Device\\WssHookPE" );
status = IoCreateDevice( DriverObject,
0,
&nameString,
FILE_DEVICE_UNKNOWN,
0,
TRUE,
&deviceObject
);
if (!NT_SUCCESS( status ))
return status;
RtlInitUnicodeString( &linkString, L"\\DosDevices\\WssHookPE" );
status = IoCreateSymbolicLink (&linkString, &nameString);
if (!NT_SUCCESS( status ))
{
IoDeleteDevice (DriverObject->DeviceObject);
return status;
}
pModuleAddress = MyGetModuleBaseAddress("ntoskrnl.exe");
if ( pModuleAddress == NULL)
{
DbgPrint(" MyGetModuleBaseAddress()\n");
return 0;
}
OldZwCreateFile = (ZWCREATEFILE)HookFunction( pModuleAddress, "ZwCreateFile",(ZWCREATEFILE)HookNtCreateFile);
if ( OldZwCreateFile == NULL)
{
DbgPrint(" HOOK FAILED\n");
return 0;
}
DbgPrint("HOOK SUCCEED\n");
for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
DriverObject->MajorFunction = MydrvDispatch;
}
DriverObject->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
//處理設備物件操作
static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0L;
IoCompleteRequest( Irp, 0 );
return Irp->IoStatus.Status;
}
VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject)
{
UNICODE_STRING nameString;
PCHAR pModuleAddress;
pModuleAddress = MyGetModuleBaseAddress("ntoskrnl.exe");
if ( pModuleAddress == NULL)
{
DbgPrint("MyGetModuleBaseAddress()\n");
return ;
}
OldZwCreateFile = (ZWCREATEFILE)HookFunction( pModuleAddress, "ZwCreateFile",(ZWCREATEFILE)OldZwCreateFile);
if ( OldZwCreateFile == NULL)
{
DbgPrint(" UNHOOK FAILED!\n");
return ;
}
DbgPrint("UNHOOK SUCCEED\n");
RtlInitUnicodeString( &nameString, L"\\DosDevices\\WssHookPE" );
IoDeleteSymbolicLink(&nameString);
IoDeleteDevice(pDriverObject->DeviceObject);
return;
}
NTSTATUS
HookNtCreateFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength
)
{
NTSTATUS status;
DbgPrint("Hook ZwCreateFile()\n");
status = ((ZWCREATEFILE)(OldZwCreateFile))(
FileHandle,
DesiredAccess,
ObjectAttributes,
IoStatusBlock,
AllocationSize,
FileAttributes,
ShareAccess,
CreateDisposition,
CreateOptions,
EaBuffer,
EaLength
);
return status;
}
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;
};
}
原始连接:http://delphi.ktop.com.tw/TOPIC.ASP?TOPIC_ID=50997
內核級HOOK的幾種實現與應用
Author : sinister
Email : sinister@whitecell.org
HomePage: http://www.whitecell.org
實現內核級 HOOK 對於攔截、分析、跟蹤系統內核起著致關重要的作用。實現的方法不同意味著應用側重點的不同。
如想要攔截 NATIVE API 那麼可能常用的就是 HOOK SERVICE TABLE 的方法。
如果要分析一些系統調用,那麼可能想到用 HOOK INT 2E 中斷來實現。
如果想要攔截或跟蹤其他內核 DRIVER 的調用,那麼就要用到HOOK PE 的方法來實現。
這裏我們更注重的是實現,原理方面已有不少高手在網上發表過文章。
大家可以結合起來讀。
下面以我寫的幾個實例程式來講解一下各種方法的實現。
錯誤之處還望各位指正。
1、HOOK SERVICE TABLE 方法:
這種方法對於攔截 NATIVE API 來說用的比較多。
原理就是通過替換系統導
出的一個 SERVICE TABLE 中相應的 NATIVE API 的位址來達到攔截的目的。
因為此方法較為簡單,網上也有不少資料來介紹。
所以這裏就不給出實例程式了。SERVICE TABLE 的結構如下:
typedef struct ServiceDescriptorEntry {
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase;
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
2、HOOK INT 2E 方法:
這種方法對於跟蹤、分析系統調用來說用的比較多。原理是通過替換 IDT
表中的 INT 2E 中斷,使之指向我們自己的中斷服務處理常式來實現的。
掌握此方法需要你對保護模式有一定的基礎。下面的程式演示了這一過程。
/*****************************************************************
檔案名 : WssHookInt2e.c
描述 : 系統調用跟蹤
作者 : sinister
最後修改日期 : 2002-11-02
*****************************************************************/
#include "ntddk.h"
#include "string.h"
#define DWORD unsigned __int32
#define WORD unsigned __int16
#define BYTE unsigned __int8
#define BOOL __int32
#define LOWORD(l) ((WORD)(l))
#define HIWORD(l) ((WORD)(((DWORD)(l) >> 16) & 0xFFFF))
#define LOBYTE(w) ((BYTE)(w))
#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF))
#define MAKELONG(a, b) ((LONG) (((WORD) (a)) | ((DWORD) ((WORD) (b))) << 16))
#define SYSTEMCALL 0x2e
#define SYSNAME "System"
#define PROCESSNAMELEN 16
#pragma pack(1)
//定義 IDTR
typedef struct tagIDTR {
WORD IDTLimit;
WORD LowIDTbase;
WORD HiIDTbase;
}IDTR, *PIDTR;
//定義 IDT
typedef struct tagIDTENTRY{
WORD OffsetLow;
WORD selector;
BYTE unused_lo;
unsigned char unused_hi:5;
unsigned char DPL:2;
unsigned char P:1;
WORD OffsetHigh;
} IDTENTRY, *PIDTENTRY;
#pragma pack()
DWORD OldInt2eService;
ULONG ProcessNameOffset;
TCHAR ProcessName[PROCESSNAMELEN];
static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject);
ULONG GetProcessNameOffset();
VOID GetProcessName( PCHAR Name );
VOID InstallNewInt2e();
VOID UninstallNewInt2e();
VOID __fastcall NativeApiCall()
{
KIRQL OldIrql;
DWORD ServiceID;
DWORD ProcessId;
__asm mov ServiceID,eax;
ProcessId = (DWORD)PsGetCurrentProcessId();
GetProcessName(ProcessName);
KeRaiseIrql(HIGH_LEVEL, &OldIrql); // 提升當前的 IRQL 級別防止被中斷
switch ( ServiceID )
{
case 0x20:
DbgPrint("NEWINT2E: ProcessName: %s; ProcessID: %d; Native Api: NtCreateFile() \n",ProcessName,ProcessId);
break;
case 0x2b:
DbgPrint("NEWINT2E: ProcessName: %s; ProcessID: %d; Native Api: NtCreateSection() \n",ProcessName,ProcessId);
break;
case 0x30:
DbgPrint("NEWINT2E: ProcessName: %s; ProcessID: %d; Native Api: NtCreateToken() \n",ProcessName,ProcessId);
break;
}
KeLowerIrql(OldIrql); //恢復原始 IRQL
}
__declspec(naked) NewInt2eService()
{
__asm{
pushad
pushfd
push fs
mov bx,0x30
mov fs,bx
push ds
push es
sti
call NativeApiCall; // 調用記錄函數
cli
pop es
pop ds
pop fs
popfd
popad
jmp OldInt2eService; //跳到原始 INT 2E 繼續工作
}
}
VOID InstallNewInt2e()
{
IDTR idtr;
PIDTENTRY OIdt;
PIDTENTRY NIdt;
//得到 IDTR 中得段界限與基底位址
__asm {
sidt idtr;
}
//得到IDT基底位址
OIdt = (PIDTENTRY)MAKELONG(idtr.LowIDTbase,idtr.HiIDTbase);
//保存原來的 INT 2E 服務常式
OldInt2eService = MAKELONG(OIdt[SYSTEMCALL].OffsetLow,OIdt[SYSTEMCALL].OffsetHigh);
NIdt = &(OIdt[SYSTEMCALL]);
__asm {
cli
lea eax,NewInt2eService; //得到新的 INT 2E 服務常式偏移
mov ebx, NIdt;
mov [ebx],ax; //INT 2E 服務常式低 16 位元
shr eax,16 //INT 2E 服務常式高 16 位元
mov [ebx+6],ax;
lidt idtr //裝入新的 IDT
sti
}
}
VOID UninstallNewInt2e()
{
IDTR idtr;
PIDTENTRY OIdt;
PIDTENTRY NIdt;
__asm {
sidt idtr;
}
OIdt = (PIDTENTRY)MAKELONG(idtr.LowIDTbase,idtr.HiIDTbase);
NIdt = &(OIdt[SYSTEMCALL]);
_asm {
cli
lea eax,OldInt2eService;
mov ebx, NIdt;
mov [ebx],ax;
shr eax,16
mov [ebx+6],ax;
lidt idtr
sti
}
}
// 驅動入口
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )
{
UNICODE_STRING nameString, linkString;
PDEVICE_OBJECT deviceObject;
NTSTATUS status;
HANDLE hHandle;
int i;
//卸載驅動
DriverObject->DriverUnload = DriverUnload;
//建立設備
RtlInitUnicodeString( &nameString, L"\\Device\\WssHookInt2e" );
status = IoCreateDevice( DriverObject,
0,
&nameString,
FILE_DEVICE_UNKNOWN,
0,
TRUE,
&deviceObject
);
if (!NT_SUCCESS( status ))
return status;
RtlInitUnicodeString( &linkString, L"\\DosDevices\\WssHookInt2e" );
status = IoCreateSymbolicLink (&linkString, &nameString);
if (!NT_SUCCESS( status ))
{
IoDeleteDevice (DriverObject->DeviceObject);
return status;
}
for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
DriverObject->MajorFunction = MydrvDispatch;
}
DriverObject->DriverUnload = DriverUnload;
ProcessNameOffset = GetProcessNameOffset();
InstallNewInt2e();
return STATUS_SUCCESS;
}
//處理設備物件操作
static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0L;
IoCompleteRequest( Irp, 0 );
return Irp->IoStatus.Status;
}
VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject)
{
UNICODE_STRING nameString;
UninstallNewInt2e();
RtlInitUnicodeString( &nameString, L"\\DosDevices\\WssHookInt2e" );
IoDeleteSymbolicLink(&nameString);
IoDeleteDevice(pDriverObject->DeviceObject);
return;
}
ULONG GetProcessNameOffset()
{
PEPROCESS curproc;
int i;
curproc = PsGetCurrentProcess();
//
// Scan for 12KB, hopping the KPEB never grows that big!
//
for( i = 0; i < 3*PAGE_SIZE; i++ ) {
if( !strncmp( SYSNAME, (PCHAR) curproc + i, strlen(SYSNAME) )) {
return i;
}
}
//
// Name not found - oh, well
//
return 0;
}
VOID GetProcessName( PCHAR Name )
{
PEPROCESS curproc;
char *nameptr;
ULONG i;
if( ProcessNameOffset ) {
curproc = PsGetCurrentProcess();
nameptr = (PCHAR) curproc + ProcessNameOffset;
strncpy( Name, nameptr, 16 );
} else {
strcpy( Name, "???");
}
}
3、 HOOK PE 方法
這種方法對於攔截、分析其他內核驅動的函數調用來說用的比較多。
原理
是根據替換 PE 格式導出表中的相應函數來實現的。
此方法中需要用到一些小技巧。
如內核模式並沒有直接提供類似應用層的 GetModuleHandl()、GetProcAddress() 等函數來獲得模組的位址。
那麼我們就需要自己來編寫,這裏用到了一個未公開的函數與結構。
ZwQuerySystemInformation 與 SYSTEM_MODULE_INFORMATION 來實現得到模組的基底位址。
這樣我們就可以根據PE 格式來枚舉導出表中的函數來替換了。
但這又引出了一個問題,那就是從WINDOWS 2000 後內核資料的頁屬性都是唯讀的,不能更改。
內核模式也沒有提供類似應用層的 VirtualProtectEx() 等函數來修改頁面屬性。
那麼也需要我們自己來編寫。
因為我們是在內核模式所以我們可以通過修改 cr0 寄存器的的防寫位元來達到我們的目的。
這樣我們所期望的攔截內核模式函數的功能便得以實現。
此方法需要你對 PE 格式有一定的基礎。下面的程式演示了這一過程。
/*****************************************************************
檔案名 : WssHookPE.c
描述 : 攔截內核函數
作者 : sinister
最後修改日期 : 2002-11-02
*****************************************************************/
#include "ntddk.h"
#include "windef.h"
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation,
SystemProcessorInformation,
SystemPerformanceInformation,
SystemTimeOfDayInformation,
SystemNotImplemented1,
SystemProcessesAndThreadsInformation,
SystemCallCounts,
SystemConfigurationInformation,
SystemProcessorTimes,
SystemGlobalFlag,
SystemNotImplemented2,
SystemModuleInformation,
SystemLockInformation,
SystemNotImplemented3,
SystemNotImplemented4,
SystemNotImplemented5,
SystemHandleInformation,
SystemObjectInformation,
SystemPagefileInformation,
SystemInstructionEmulationCounts,
SystemInvalidInfoClass1,
SystemCacheInformation,
SystemPoolTagInformation,
SystemProcessorStatistics,
SystemDpcInformation,
SystemNotImplemented6,
SystemLoadImage,
SystemUnloadImage,
SystemTimeAdjustment,
SystemNotImplemented7,
SystemNotImplemented8,
SystemNotImplemented9,
SystemCrashDumpInformation,
SystemExceptionInformation,
SystemCrashDumpStateInformation,
SystemKernelDebuggerInformation,
SystemContextSwitchInformation,
SystemRegistryQuotaInformation,
SystemLoadAndCallImage,
SystemPrioritySeparation,
SystemNotImplemented10,
SystemNotImplemented11,
SystemInvalidInfoClass2,
SystemInvalidInfoClass3,
SystemTimeZoneInformation,
SystemLookasideInformation,
SystemSetTimeSlipEvent,
SystemCreateSession,
SystemDeleteSession,
SystemInvalidInfoClass4,
SystemRangeStartInformation,
SystemVerifierInformation,
SystemAddVerifier,
SystemSessionProcessesInformation
} SYSTEM_INFORMATION_CLASS;
typedef struct tagSYSTEM_MODULE_INFORMATION {
ULONG Reserved[2];
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT Index;
USHORT Unknown;
USHORT LoadCount;
USHORT ModuleNameOffset;
CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
#define IMAGE_NT_SIGNATURE 0x50450000 // PE00
#define IMAGE_NT_SIGNATURE1 0x00004550 // 00EP
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
//
// Optional header format.
//
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
//
// NT additional fields.
//
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS;
//
// Section header format.
//
#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
#define IMAGE_SIZEOF_SECTION_HEADER 40
//
// Export Format
//
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
#define BASEADDRLEN 10
NTSYSAPI
NTSTATUS
NTAPI
ZwQuerySystemInformation(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
typedef NTSTATUS (* ZWCREATEFILE)(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength
);
ZWCREATEFILE OldZwCreateFile;
static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject);
VOID DisableWriteProtect( PULONG pOldAttr);
VOID EnableWriteProtect( ULONG ulOldAttr );
FARPROC HookFunction( PCHAR pModuleBase, PCHAR pHookName, FARPROC pHookFunc );
NTSTATUS
HookNtCreateFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength
);
PCHAR MyGetModuleBaseAddress( PCHAR pModuleName )
{
PSYSTEM_MODULE_INFORMATION pSysModule;
ULONG uReturn;
ULONG uCount;
PCHAR pBuffer = NULL;
PCHAR pName = NULL;
NTSTATUS status;
UINT ui;
CHAR szBuffer[BASEADDRLEN];
PCHAR pBaseAddress;
status = ZwQuerySystemInformation( SystemModuleInformation, szBuffer, BASEADDRLEN, &uReturn );
pBuffer = ( PCHAR )ExAllocatePool( NonPagedPool, uReturn );
if ( pBuffer )
{
status = ZwQuerySystemInformation( SystemModuleInformation, pBuffer, uReturn, &uReturn );
if( status == STATUS_SUCCESS )
{
uCount = ( ULONG )*( ( ULONG * )pBuffer );
pSysModule = ( PSYSTEM_MODULE_INFORMATION )( pBuffer + sizeof( ULONG ) );
for ( ui = 0; ui < uCount; ui++ )
{
pName = MyStrchr( pSysModule->ImageName, '\\' );
if ( !pName )
{
pName = pSysModule->ImageName;
}
else {
pName++;
}
if( !_stricmp( pName, pModuleName ) )
{
pBaseAddress = ( PCHAR )pSysModule->Base;
ExFreePool( pBuffer );
return pBaseAddress;
}
pSysModule ++;
}
}
ExFreePool( pBuffer );
}
return NULL;
}
FARPROC HookFunction( PCHAR pModuleBase, PCHAR HookFunName, FARPROC HookFun )
{
PIMAGE_DOS_HEADER pDosHdr;
PIMAGE_NT_HEADERS pNtHdr;
PIMAGE_SECTION_HEADER pSecHdr;
PIMAGE_EXPORT_DIRECTORY pExtDir;
UINT ui,uj;
PCHAR FunName;
DWORD *dwAddrName;
DWORD *dwAddrFun;
FARPROC pOldFun;
ULONG uAttrib;
pDosHdr = ( PIMAGE_DOS_HEADER )pModuleBase;
if ( IMAGE_DOS_SIGNATURE == pDosHdr->e_magic )
{
pNtHdr = ( PIMAGE_NT_HEADERS )( pModuleBase + pDosHdr->e_lfanew );
if( IMAGE_NT_SIGNATURE == pNtHdr->Signature || IMAGE_NT_SIGNATURE1 == pNtHdr->Signature )
{
pSecHdr = ( PIMAGE_SECTION_HEADER )( pModuleBase + pDosHdr->e_lfanew + sizeof( IMAGE_NT_HEADERS ) );
for ( ui = 0; ui < (UINT)pNtHdr->FileHeader.NumberOfSections; ui++ )
{
if ( !strcmp( pSecHdr->Name, ".edata" ) )
{
pExtDir = ( PIMAGE_EXPORT_DIRECTORY )( pModuleBase + pSecHdr->VirtualAddress );
dwAddrName = ( PDWORD )(pModuleBase + pExtDir->AddressOfNames );
dwAddrFun = ( PDWORD )(pModuleBase + pExtDir->AddressOfFunctions );
for ( uj = 0; uj < (UINT)pExtDir->NumberOfFunctions; uj++ )
{
FunName = pModuleBase + *dwAddrName;
if( !strcmp( FunName, HookFunName ) )
{
DbgPrint(" HOOK %s()\n",FunName);
DisableWriteProtect( &uAttrib );
pOldFun = ( FARPROC )( pModuleBase + *dwAddrFun );
*dwAddrFun = ( PCHAR )HookFun - pModuleBase;
EnableWriteProtect( uAttrib );
return pOldFun;
}
dwAddrName ++;
dwAddrFun ++;
}
}
pSecHdr++;
}
}
}
return NULL;
}
// 驅動入口
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )
{
UNICODE_STRING nameString, linkString;
PDEVICE_OBJECT deviceObject;
NTSTATUS status;
HANDLE hHandle;
PCHAR pModuleAddress;
int i;
//卸載驅動
DriverObject->DriverUnload = DriverUnload;
//建立設備
RtlInitUnicodeString( &nameString, L"\\Device\\WssHookPE" );
status = IoCreateDevice( DriverObject,
0,
&nameString,
FILE_DEVICE_UNKNOWN,
0,
TRUE,
&deviceObject
);
if (!NT_SUCCESS( status ))
return status;
RtlInitUnicodeString( &linkString, L"\\DosDevices\\WssHookPE" );
status = IoCreateSymbolicLink (&linkString, &nameString);
if (!NT_SUCCESS( status ))
{
IoDeleteDevice (DriverObject->DeviceObject);
return status;
}
pModuleAddress = MyGetModuleBaseAddress("ntoskrnl.exe");
if ( pModuleAddress == NULL)
{
DbgPrint(" MyGetModuleBaseAddress()\n");
return 0;
}
OldZwCreateFile = (ZWCREATEFILE)HookFunction( pModuleAddress, "ZwCreateFile",(ZWCREATEFILE)HookNtCreateFile);
if ( OldZwCreateFile == NULL)
{
DbgPrint(" HOOK FAILED\n");
return 0;
}
DbgPrint("HOOK SUCCEED\n");
for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
DriverObject->MajorFunction = MydrvDispatch;
}
DriverObject->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
//處理設備物件操作
static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0L;
IoCompleteRequest( Irp, 0 );
return Irp->IoStatus.Status;
}
VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject)
{
UNICODE_STRING nameString;
PCHAR pModuleAddress;
pModuleAddress = MyGetModuleBaseAddress("ntoskrnl.exe");
if ( pModuleAddress == NULL)
{
DbgPrint("MyGetModuleBaseAddress()\n");
return ;
}
OldZwCreateFile = (ZWCREATEFILE)HookFunction( pModuleAddress, "ZwCreateFile",(ZWCREATEFILE)OldZwCreateFile);
if ( OldZwCreateFile == NULL)
{
DbgPrint(" UNHOOK FAILED!\n");
return ;
}
DbgPrint("UNHOOK SUCCEED\n");
RtlInitUnicodeString( &nameString, L"\\DosDevices\\WssHookPE" );
IoDeleteSymbolicLink(&nameString);
IoDeleteDevice(pDriverObject->DeviceObject);
return;
}
NTSTATUS
HookNtCreateFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength
)
{
NTSTATUS status;
DbgPrint("Hook ZwCreateFile()\n");
status = ((ZWCREATEFILE)(OldZwCreateFile))(
FileHandle,
DesiredAccess,
ObjectAttributes,
IoStatusBlock,
AllocationSize,
FileAttributes,
ShareAccess,
CreateDisposition,
CreateOptions,
EaBuffer,
EaLength
);
return status;
}
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;
};
}