关于这篇文章的题目,思索良久,其实一些技术术语一直是我的软肋。高大上标题,别人会认为你言过其实,低调隐晦的标题,又根本提不起别人打开这篇博文的兴趣。许久之后,就下定决心,那么就起一个朴实无华的名字算了,所以就想到了“进程保护”,但仔细想想,其实这也是一个非常大的技术专题,包括众多的技术细节。所以就此声明,其实这只是一篇利用了一个简单的小技术,在一定程度上达到防止你的程序被结束的技术而已。通过读这篇文章,你可能会了解,什么叫 inline hook ?如何利用 inline hook 来保护进程的。
Hook 有很多种,那些多如牛毛的术语,如 IAT HOOK, Inline Hook, SSDT Hook, Message Hook 等,要想知道这些名词,搜索引擎会帮助你,而为了保护进程,也有很多 Hook 方法来完成,这里我选用了inline hook。
简单地介绍下 inline hook 。所谓 inline hook 就是,在所要被拦截的函数的里,通过指令跳转,来达到跳转到目的函数的手段,当跳转到目标函数以后,就可以在堆栈中得到调用被拦截函数的参数,当然,此时你就可以做一些自己的逻辑,比如记录一些信息,然后放行,或者直接返回到调用者,以达到欺骗调用者的目的,让它误以为调用该函数失败。
那么本文用的到原理也就显而易见,大致原理就是,利用 inline hook 技术,拦截 NtOpenProcess API 调用,当检测到正在打开我们想要保护的进程的时候,就直接返回失败,让调用失败。因为想要结束一个进程,往往会先打开该进程,然后得到句柄,如果打开进程都失败了,那么自然就不能结束掉我们的进程了。
代码如下:
1 #define DBG 1 2 3 #include <ntifs.h> 4 #include <ntddk.h> 5 #include <ntstrsafe.h> 6 7 VOID PPUnload(PDRIVER_OBJECT driverObject); 8 9 10 #define HAPI unsigned int 11 #define PFUNC void * 12 13 14 #define SHELLCODE_SHADOW_OFFSET 0xD + 1 15 #define TARTGET_HEADE 0x28 16 #define SHELLCODE_TARGET_OFFSET 0x2D + 1 17 #define HOOKAPI _cdecl 18 #define HOOK_PARAMETERS ULONG henzox_hookIsBlock, ULONG henzox_hookCount, ULONG henzox_hookRet, 19 #define HookBlock(block, count) *&henzox_hookCount = 4*(count); *&henzox_hookIsBlock = block; 20 21 22 void __declspec(naked) GhostTemplate() 23 { 24 _asm { 25 push 0 ; 压入 block 参数 26 push 0 ; 压入 count 参数 27 call _SAVE_EIP 28 _SAVE_EIP: 29 add dword ptr[esp], 9 30 ; 跳转到影子函数处执行 31 _emit 0xE9 32 _emit 0x00 33 _emit 0x00 34 _emit 0x00 35 _emit 0x00 36 _BACK: 37 ; 判断是否放行 38 cmp dword ptr[esp], 0 39 jz _PASS ; 放行 40 ; 被阻止,返回到调用者 41 mov edx, dword ptr 8[esp] ; 保存返回地址 42 add esp, dword ptr 4[esp] 43 add esp, 0xC ; 略去压入的我们的两个参数和原返回地址 44 push edx 45 ret 46 _PASS: 47 add esp, 8 48 ; 目标函数的头五个字节 49 _emit 0x00 50 _emit 0x00 51 _emit 0x00 52 _emit 0x00 53 _emit 0x00 54 _JMP_2_TAR: ; 跳转到目标函数处执行 55 _emit 0xE9 56 _emit 0x00 57 _emit 0x00 58 _emit 0x00 59 _emit 0x00 60 } 61 } 62 63 64 65 66 HAPI HookApi(PFUNC target, PFUNC shadow) 67 { 68 unsigned char shellCode[] = {0xE9,0x00,0x00,0x00,0x00}; 69 DWORD dwBytes = 0; 70 int nOffset; 71 unsigned char *tmpCode = NULL; 72 void * addrGhost; 73 int count = 64; 74 75 76 /* 申请一段可执行的内存作为跳转代码并调整里面的跳转 */ 77 tmpCode = (unsigned char *)ExAllocatePool(NonPagedPool, 0x1000); 78 RtlZeroMemory(tmpCode, 0x1000); 79 // 写入目标函数被修改的地址 80 *(ULONG *)((ULONG)tmpCode + 0x1000 - 4) = (ULONG)target; 81 // 拷贝模板代码 82 // DEBUG addrGhost = (ULONG)GhostTmplate + *(ULONG *)((PUCHAR)GhostTmplate + 1) + 5; 83 addrGhost = (ULONG)GhostTemplate; 84 RtlMoveMemory(tmpCode, addrGhost, 512); 85 // 修改跳转到 Shadow 函数的地址 86 *(ULONG *)&tmpCode[SHELLCODE_SHADOW_OFFSET] = (ULONG)shadow - (ULONG)&tmpCode[SHELLCODE_SHADOW_OFFSET] - 4; 87 // 修改跳转到目标函数的地址 88 *(ULONG *)&tmpCode[SHELLCODE_TARGET_OFFSET] = (ULONG)target + 5 - (ULONG)&tmpCode[SHELLCODE_TARGET_OFFSET] - 4; 89 90 // 保存原来的五个字节 91 memcpy(&tmpCode[TARTGET_HEADE], (ULONG)target,5); 92 /* 写入跳转地址 */ 93 nOffset = (ULONG)tmpCode - (ULONG)target - 5; 94 *(ULONG *)&shellCode[1] = nOffset; 95 memcpy(target, shellCode, sizeof(shellCode)); 96 97 return (HAPI)tmpCode; 98 } 99 100 NTSTATUS UnHookApi(HAPI handle) 101 { 102 DWORD dwBytes = 0; 103 int i = 10000; 104 unsigned char *tmpCode = (unsigned char *)handle; 105 void *target = *(ULONG *)((ULONG)tmpCode + 0x1000 - 4); 106 107 memcpy(target, &tmpCode[TARTGET_HEADE], 5); 108 while (i-- > 0) { 109 i = i; 110 } 111 ExFreePool((void *)handle); 112 113 return STATUS_SUCCESS; 114 } 115 116 NTSTATUS 117 HOOKAPI 118 MyNtOpenProcess ( 119 HOOK_PARAMETERS 120 __out PHANDLE ProcessHandle, 121 __in ACCESS_MASK DesiredAccess, 122 __in POBJECT_ATTRIBUTES ObjectAttributes, 123 __in_opt PCLIENT_ID ClientId 124 ) 125 { 126 PEPROCESS process; 127 char *imageName; 128 //KdPrint(("MyNtOpenProcess.\n")); 129 130 if (!ClientId) { 131 goto _NONBLOCK; 132 } 133 134 PsLookupProcessByProcessId(ClientId->UniqueProcess, &process); 135 if (!process) { 136 goto _NONBLOCK; 137 } 138 139 imageName = (char *)((PUCHAR)process + 0x164); 140 if (!strcmp(imageName, "Locator.exe")) { 141 HookBlock(TRUE, 4); 142 KdPrint(("Pid: %d, Image: %s is blocked!\n", ClientId->UniqueProcess, imageName)); 143 144 return STATUS_INVALID_PARAMETER_MIX; 145 } 146 147 _NONBLOCK: 148 HookBlock(FALSE, 4); 149 return STATUS_SUCCESS; 150 } 151 152 static HAPI s_hNtOpenProcess; 153 154 NTSTATUS DriverEntry(IN PDRIVER_OBJECT driverObject,IN PUNICODE_STRING serviceRegPath) 155 { 156 KdPrint(("DriverEntry.\n")); 157 158 driverObject->DriverUnload = PPUnload; 159 160 s_hNtOpenProcess = HookApi(NtOpenProcess, MyNtOpenProcess); 161 162 163 return STATUS_SUCCESS; 164 } 165 166 VOID PPUnload(PDRIVER_OBJECT driverObject) 167 { 168 KdPrint(("PPUnload.\n")); 169 170 UnHookApi(s_hNtOpenProcess); 171 }
我一直认为,所以的细节都体现在源码中,代码和原理都很简单。以上代码可以编译为一个驱动程序,它会 Hook 掉 NtOpenProcess ,在我们自己的函数中,保护了 Locator.exe 进程。
写文章真是一件值得的事情,也许很多年后,我都忘了怎么写程序的时候,翻一翻现在的文章,也可以感叹下此时的时光!
浙公网安备 33010602011771号