Windows API Hook 实现保护进程
关于这篇文章的题目,思索良久,其实一些技术术语一直是我的软肋。高大上标题,别人会认为你言过其实,低调隐晦的标题,又根本提不起别人打开这篇博文的兴趣。许久之后,就下定决心,那么就起一个朴实无华的名字算了,所以就想到了“进程保护”,但仔细想想,其实这也是一个非常大的技术专题,包括众多的技术细节。所以就此声明,其实这只是一篇利用了一个简单的小技术,在一定程度上达到防止你的程序被结束的技术而已。通过读这篇文章,你可能会了解,什么叫 inline hook ?如何利用 inline hook 来保护进程的。
Hook 有很多种,那些多如牛毛的术语,如 IAT HOOK, Inline Hook, SSDT Hook, Message Hook 等,要想知道这些名词,搜索引擎会帮助你,而为了保护进程,也有很多 Hook 方法来完成,这里我选用了 inline hook。
简单地介绍下 inline hook 。所谓 inline hook 就是,在所要被拦截的函数的里,通过指令跳转,来达到跳转到目的函数的手段,当跳转到目标函数以后,就可以在堆栈中得到调用被拦截函数的参数,当然,此时你就可以做一些自己的逻辑,比如记录一些信息,然后放行,或者直接返回到调用者,以达到欺骗调用者的目的,让它误以为调用该函数失败。
那么本文用的到原理也就显而易见,大致原理就是,利用 inline hook 技术,拦截 NtOpenProcess API 调用,当检测到正在打开我们想要保护的进程的时候,就直接返回失败,让调用失败。因为想要结束一个进程,往往会先打开该进程,然后得到句柄,如果打开进程都失败了,那么自然就不能结束掉我们的进程了。
#define DBG 1
#include <ntifs.h>
#include <ntddk.h>
#include <ntstrsafe.h>
VOID PPUnload(PDRIVER_OBJECT driverObject);
#define HAPI unsigned int
#define PFUNC void *
#define SHELLCODE_SHADOW_OFFSET 0xD + 1
#define TARTGET_HEADE 0x28
#define SHELLCODE_TARGET_OFFSET 0x2D + 1
#define HOOKAPI _cdecl
#define HOOK_PARAMETERS ULONG henzox_hookIsBlock, ULONG henzox_hookCount, ULONG henzox_hookRet,
#define HookBlock(block, count) *&henzox_hookCount = 4*(count); *&henzox_hookIsBlock = block;
void __declspec(naked) GhostTemplate()
{
_asm {
push 0 ; 压入 block 参数
push 0 ; 压入 count 参数
call _SAVE_EIP
_SAVE_EIP:
add dword ptr[esp], 9
; 跳转到影子函数处执行
_emit 0xE9
_emit 0x00
_emit 0x00
_emit 0x00
_emit 0x00
_BACK:
; 判断是否放行
cmp dword ptr[esp], 0
jz _PASS ; 放行
; 被阻止,返回到调用者
mov edx, dword ptr 8[esp] ; 保存返回地址
add esp, dword ptr 4[esp]
add esp, 0xC ; 略去压入的我们的两个参数和原返回地址
push edx
ret
_PASS:
add esp, 8
; 目标函数的头五个字节
_emit 0x00
_emit 0x00
_emit 0x00
_emit 0x00
_emit 0x00
_JMP_2_TAR: ; 跳转到目标函数处执行
_emit 0xE9
_emit 0x00
_emit 0x00
_emit 0x00
_emit 0x00
}
}
HAPI HookApi(PFUNC target, PFUNC shadow)
{
unsigned char shellCode[] = {0xE9,0x00,0x00,0x00,0x00};
DWORD dwBytes = 0;
int nOffset;
unsigned char *tmpCode = NULL;
void * addrGhost;
int count = 64;
/* 申请一段可执行的内存作为跳转代码并调整里面的跳转 */
tmpCode = (unsigned char *)ExAllocatePool(NonPagedPool, 0x1000);
RtlZeroMemory(tmpCode, 0x1000);
// 写入目标函数被修改的地址
*(ULONG *)((ULONG)tmpCode + 0x1000 - 4) = (ULONG)target;
// 拷贝模板代码
// DEBUG addrGhost = (ULONG)GhostTmplate + *(ULONG *)((PUCHAR)GhostTmplate + 1) + 5;
addrGhost = (ULONG)GhostTemplate;
RtlMoveMemory(tmpCode, addrGhost, 512);
// 修改跳转到 Shadow 函数的地址
*(ULONG *)&tmpCode[SHELLCODE_SHADOW_OFFSET] = (ULONG)shadow - (ULONG)&tmpCode[SHELLCODE_SHADOW_OFFSET] - 4;
// 修改跳转到目标函数的地址
*(ULONG *)&tmpCode[SHELLCODE_TARGET_OFFSET] = (ULONG)target + 5 - (ULONG)&tmpCode[SHELLCODE_TARGET_OFFSET] - 4;
// 保存原来的五个字节
memcpy(&tmpCode[TARTGET_HEADE], (ULONG)target,5);
/* 写入跳转地址 */
nOffset = (ULONG)tmpCode - (ULONG)target - 5;
*(ULONG *)&shellCode[1] = nOffset;
memcpy(target, shellCode, sizeof(shellCode));
return (HAPI)tmpCode;
}
NTSTATUS UnHookApi(HAPI handle)
{
DWORD dwBytes = 0;
int i = 10000;
unsigned char *tmpCode = (unsigned char *)handle;
void *target = *(ULONG *)((ULONG)tmpCode + 0x1000 - 4);
memcpy(target, &tmpCode[TARTGET_HEADE], 5);
while (i-- > 0) {
i = i;
}
ExFreePool((void *)handle);
return STATUS_SUCCESS;
}
NTSTATUS
HOOKAPI
MyNtOpenProcess (
HOOK_PARAMETERS
__out PHANDLE ProcessHandle,
__in ACCESS_MASK DesiredAccess,
__in POBJECT_ATTRIBUTES ObjectAttributes,
__in_opt PCLIENT_ID ClientId
)
{
PEPROCESS process;
char *imageName;
//KdPrint(("MyNtOpenProcess.\n"));
if (!ClientId) {
goto _NONBLOCK;
}
PsLookupProcessByProcessId(ClientId->UniqueProcess, &process);
if (!process) {
goto _NONBLOCK;
}
imageName = (char *)((PUCHAR)process + 0x164);
if (!strcmp(imageName, "notepad.exe")) {
HookBlock(TRUE, 4);
KdPrint(("Pid: %d, Image: %s is blocked!\n", ClientId->UniqueProcess, imageName));
return STATUS_INVALID_PARAMETER_MIX;
}
_NONBLOCK:
HookBlock(FALSE, 4);
return STATUS_SUCCESS;
}
static HAPI s_hNtOpenProcess;
NTSTATUS DriverEntry(IN PDRIVER_OBJECT driverObject,IN PUNICODE_STRING serviceRegPath)
{
KdPrint(("DriverEntry.\n"));
driverObject->DriverUnload = PPUnload;
s_hNtOpenProcess = HookApi(NtOpenProcess, MyNtOpenProcess);
return STATUS_SUCCESS;
}
VOID PPUnload(PDRIVER_OBJECT driverObject)
{
KdPrint(("PPUnload.\n"));
UnHookApi(s_hNtOpenProcess);
}
我一直认为,所以的细节都体现在源码中,代码和原理都很简单。以上代码可以编译为一个驱动程序,它会 Hook 掉 NtOpenProcess ,在我们自己的函数中,我保护了 Notepad.exe 进程。
写博客真是一件值得的事情,也许很多年后,我都忘了怎么写程序的时候,翻一翻现在的文章,也可以感叹下此时的时光!
作者:henzox
来源:CSDN
原文:https://blog.csdn.net/henzox/article/details/32108517 转载已获得授权