|
网络安全编程:HOOK SSDT计算机与网络安全 前天
一次性付费进群,长期免费索取资料。
回复公众号:微信群 可查看进群流程。
SSDT把用户层的Win32 API与内核层的Native API做了一个关联,而整个Native API都保存在SSDT中的一个函数指针数组中,只要修改函数指针数组中的某一项,就相当于HOOK了某个Native API函数。比如,修改SSDT中函数指针数组中的最后一个函数指针,就相当于HOOK了NtQueryPortInformationProcess()函数。
下面HOOK一个比较熟悉的函数,即创建进程函数NtCreateProcessEx()。该函数在指针数组的第0x30项(该编号根据系统版本的不同而不同,是系统相关的)。通过编程获取SSDT表,然后找到Native API的函数指针数组,再修改其中第0x30项的内容为自己的函数地址。为了不影响进程的正常创建,在函数中调用NtCreateProcessEx()函数。代码如下:
- #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 函数的地址
- NTCREATEPROCESSEX ulNtCreateProcessEx = 0;
- // 在指针数组中 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
- )
- {
- 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()中调用了HookCreateProcess()函数,该函数的作用是将指针数组中NtCreateProcessEx()函数的地址替换为MyNtCreateProcessEx()函数的地址。而MyNtCreateProcessEx()函数是用来取代NtCreateProcessEx()函数的函数,在这里的函数中调用了一条KdPrint()用于输出代码。整个HOOK的过程非常简单,只要找到指针数组的位置,保存原地址后修改为新的地址即可。代码中出现了两个函数,分别是UN_PROTECT()和RE_PROTECT()。这两个函数的作用是禁止和开启CPU向标志为只读的内存页进行写入的操作。执行UN_PROTECT后, CPU可以向标志为只读的内存页进行写入操作。当写入完成后,调用RE_PROTECT()函数恢复到原来的状态。把它放到虚拟机中,打开DebugView,然后加载该驱动,加载成功后随便运行一个可执行程序。可以看到,DebugView中显示了在MyNtCreateProcessEx()中的输出,如图1所示,说明HOOK成功了。
图1 MyNtCreateProcessEx()函数的输出
|
|