SSDT把用戶(hù)層的Win32 API與內(nèi)核層的Native API做了一個(gè)關(guān)聯(lián),而整個(gè)Native API都保存在SSDT中的一個(gè)函數(shù)指針數(shù)組中,只要修改函數(shù)指針數(shù)組中的某一項(xiàng),就相當(dāng)于HOOK了某個(gè)Native API函數(shù)。比如,修改SSDT中函數(shù)指針數(shù)組中的最后一個(gè)函數(shù)指針,就相當(dāng)于HOOK了NtQueryPortInformationProcess()函數(shù)。
下面HOOK一個(gè)比較熟悉的函數(shù),即創(chuàng)建進(jìn)程函數(shù)NtCreateProcessEx()。該函數(shù)在指針數(shù)組的第0x30項(xiàng)(該編號(hào)根據(jù)系統(tǒng)版本的不同而不同,是系統(tǒng)相關(guān)的)。通過(guò)編程獲取SSDT表,然后找到Native API的函數(shù)指針數(shù)組,再修改其中第0x30項(xiàng)的內(nèi)容為自己的函數(shù)地址。為了不影響進(jìn)程的正常創(chuàng)建,在函數(shù)中調(diào)用NtCreateProcessEx()函數(shù)。代碼如下:
#include <ntddk.h>
typedef struct _SERVICE_DESCRIPTOR_TABLE
{
PULONG ServiceTableBase;
PULONG ServiceCounterTableBase;
ULONG NumberOfServices;
PUCHAR ParamTableBase;
}SERVICE_DESCRIPTOR_TABLE, *PSERVICE_DESCRIPTOR_TABLE;
extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;
typedef NTSTATUS (*NTCREATEPROCESSEX)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES,
HANDLE, ULONG, HANDLE, HANDLE, HANDLE, ULONG);
// 保存 NtCreateProcessEx 函數(shù)的地址
NTCREATEPROCESSEX ulNtCreateProcessEx = 0;
// 在指針數(shù)組中 NtCreateProcessEx 的地址
ULONG ulNtCreateProcessExAddr = 0;
VOID UN_PROTECT()
{
__asm
{
push eax
mov eax, CR0
and eax, 0FFFEFFFFh
mov CR0, eax
pop eax
}
}
VOID RE_PROTECT()
{
__asm
{
push eax
mov eax, CR0
or eax, 0FFFEFFFFh
mov CR0, eax
pop eax
}
}
VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
UN_PROTECT();
// 替換 NtCreateProcessEx 的地址為 MyNtCreateProcessEx
*(PULONG)ulNtCreateProcessExAddr = (ULONG)ulNtCreateProcessEx;
RE_PROTECT();
}
NTSTATUS
MyNtCreateProcessEx(
__out PHANDLE ProcessHandle,
__in ACCESS_MASK DesiredAccess,
__in_opt POBJECT_ATTRIBUTES ObjectAttributes,
__in HANDLE ParentProcess,
__in ULONG Flags,
__in_opt HANDLE SectionHandle,
__in_opt HANDLE DebugPort,
__in_opt HANDLE ExceptionPort,
__in ULONG JobMemberLevel
?。?/p>
{
NTSTATUS Status = STATUS_SUCCESS;
KdPrint((“Enter MyNtCreateProcessEx! \r\n”));
Status = ulNtCreateProcessEx(ProcessHandle,
DesiredAccess,
ObjectAttributes,
ParentProcess,
Flags,
SectionHandle,
DebugPort,
ExceptionPort,
JobMemberLevel);
return Status;
}
VOID HookCreateProcess()
{
ULONG ulSsdt = 0;
// 保存 NtCreateProcess 的地址
// 獲取 SSDT
ulSsdt = (ULONG)KeServiceDescriptorTable->ServiceTableBase;
// 獲取 NtCreateProcessEx 地址的指針
ulNtCreateProcessExAddr = ulSsdt + 0x30 * 4;
// 備份 NtCreateProcessEx 的原始地址
ulNtCreateProcessEx = (NTCREATEPROCESSEX) *(PULONG)ulNtCreateProcessExAddr;
UN_PROTECT();
// 替換 NtCreateProcessEx 的地址為 MyNtCreateProcessEx
*(PULONG)ulNtCreateProcessExAddr = (ULONG)MyNtCreateProcessEx;
RE_PROTECT();
}
NTSTATUS DriverEntry(
PDRIVER_OBJECT pDriverObject,
PUNICODE_STRING pRegistryPath
)
{
NTSTATUS Status = STATUS_SUCCESS;
pDriverObject->DriverUnload = DriverUnload;
HookCreateProcess();
return Status;
}
DriverEntry()中調(diào)用了HookCreateProcess()函數(shù),該函數(shù)的作用是將指針數(shù)組中NtCreateProcessEx()函數(shù)的地址替換為MyNtCreateProcessEx()函數(shù)的地址。而MyNtCreateProcessEx()函數(shù)是用來(lái)取代NtCreateProcessEx()函數(shù)的函數(shù),在這里的函數(shù)中調(diào)用了一條KdPrint()用于輸出代碼。整個(gè)HOOK的過(guò)程非常簡(jiǎn)單,只要找到指針數(shù)組的位置,保存原地址后修改為新的地址即可。代碼中出現(xiàn)了兩個(gè)函數(shù),分別是UN_PROTECT()和RE_PROTECT()。這兩個(gè)函數(shù)的作用是禁止和開(kāi)啟CPU向標(biāo)志為只讀的內(nèi)存頁(yè)進(jìn)行寫(xiě)入的操作。執(zhí)行UN_PROTECT后, CPU可以向標(biāo)志為只讀的內(nèi)存頁(yè)進(jìn)行寫(xiě)入操作。當(dāng)寫(xiě)入完成后,調(diào)用RE_PROTECT()函數(shù)恢復(fù)到原來(lái)的狀態(tài)。把它放到虛擬機(jī)中,打開(kāi)DebugView,然后加載該驅(qū)動(dòng),加載成功后隨便運(yùn)行一個(gè)可執(zhí)行程序??梢钥吹?,DebugView中顯示了在MyNtCreateProcessEx()中的輸出,如圖1所示,說(shuō)明HOOK成功了。
圖1 MyNtCreateProcessEx()函數(shù)的輸出