1.前言
这个反调试需要hook,但实际上有很多的限制。比如win10/win11的超强patchgurad,让做这件事情变得困难。但是大致hook的一个模板记录,总体遵循几个步骤:模块首地址-》段首地址-》段内搜索函数特征-》找到函数-》hook。本篇看下。
2.概括
ntoskrnl.exe里面有个函数HvlGetQpcBias,这个函数可以适当的Hook来检测或者更改一些东西。.
__int64 HvlGetQpcBias(){return *((_QWORD *)HvlpReferenceTscPage + 3);}
函数体
0: kd> x *!*HvlGetQpcBiasfffff803`22d91e40 nt!HvlGetQpcBias (HvlGetQpcBias)0: kd> uf fffff803`22d91e40nt!HvlGetQpcBias:fffff803`22d91e40 488b0589be9800 mov rax,qword ptr [nt!HvlpReferenceTscPage (fffff803`2371dcd0)]fffff803`22d91e47 488b4018 mov rax,qword ptr [rax+18h]fffff803`22d91e4b c3 ret
这里可以先获取ntoskrnl.exe模块首地址
ZwQuerySystemInformation(11, &length, 0, &length);const unsigned long tag = 'VMON';PSYSTEM_MODULE_INFORMATION system_modules = (PSYSTEM_MODULE_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, length, tag);NTSTATUS status = ZwQuerySystemInformation(11, system_modules, length, 0);for (unsigned long long i = 0; i < system_modules->ulModuleCount; i++){PSYSTEM_MODULE mod = &system_modules->Modules[i];if (strstr(mod->ImageName, name)){addr = (unsigned long long)mod->Base;if (size) *size = (unsigned long)mod->Size;break;}}ExFreePoolWithTag(system_modules, tag);return addr;
找到首地址后,找到它的text段,因为HvlGetQpcBias在text段,参考下面
.text:000000014036CA00 HvlGetQpcBias proc near ; DATA XREF: .pdata:00000001400E7F24↑o.text:000000014036CA00 ; HvlGetEnlightenmentInfo+46F↓o.text:000000014036CA00 48 8B 05 B1 12 9B 00 mov rax, cs:HvlpReferenceTscPage.text:000000014036CA07 48 8B 40 18 mov rax, [rax+18h].text:000000014036CA0B C3 retn.text:000000014036CA0B HvlGetQpcBias endp
找text段
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)addr;if (dos->e_magic != IMAGE_DOS_SIGNATURE) return 0;PIMAGE_NT_HEADERS64 nt = (PIMAGE_NT_HEADERS64)(addr + dos->e_lfanew);if (nt->Signature != IMAGE_NT_SIGNATURE) return 0;PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(nt);for (unsigned short i = 0; i < nt->FileHeader.NumberOfSections; i++){PIMAGE_SECTION_HEADER p = §ion[i];if (strstr((const char*)p->Name, name)){unsigned long long result = find_pattern(addr + p->VirtualAddress, p->Misc.VirtualSize, pattern, mask);if (result) return result;}}
找到text段之后,就可以搜索匹配特征了,先找HvlpReferenceTscPage。因为HvlGetQpcBias里面调用了它,需要在HvlGetQpcBias里面返回这个。
fffff803`22d91e40 488b0589be9800 mov rax,qword ptr [nt!HvlpReferenceTscPage (fffff803`2371dcd0)]fffff803`22d91e47 488b4018 mov rax,qword ptr [rax+18h]fffff803`22d91e4b c3 ret特征码可以浓缩成如下:\x48\x8b\xCC\xCC\xCC\xCC\xCC\x48\x8b\xCC\xCC\C3通过一个for循环,把函数HvlpReferenceTscPage地址找出来size是pSection->Misc.VirtualSize的长度,也就是text段在内存分配的长度。len也就是上面特征码所占的长度for (ULONG_PTR i = 0; i < size - len; i++){BOOLEAN found = TRUE;for (ULONG_PTR j = 0; j < len; j++){if (pattern[j] != wildcard && pattern[j] != ((PCUCHAR)base)[i + j]) //只要特征码符合第一个不等于0xCC,并且相等,循环到len长度结束,就表示找到了函数指令lea所在的地址。{found = FALSE;break;}}if (found != FALSE){*hvlp = (PUCHAR)base + i;return STATUS_SUCCESS;}}hvlp函数HvlpReferenceTscPage动态内存的地址
因为hvlp是指令lea所在的地址,所以这里需要计算出函数HvlpReferenceTscPage真正的地址
//这里的+7是lea指令占7字节,加了之后到lea指令下一个地址//把lea指令下一个地址加上上一个地址的机器码*(ULONG*)(hvlp + 3)就等于HvlpReferenceTscPage//这里的hvlp+3因为lea指令占7个字节,前3个字节是前置码,opcode,以及mdo/rm。后面的四个是计算lea指令的立即数,所以这里加3hvlp = hvlp + 7+*(ULONG*)(hvlp + 3)
HvlGetQpcBias函数也以同样的方法找到其动态内存地址(地址用hvgq表示),然后进行如下操作
同上面一样hvgq = hvlp + 7+*(ULONG*)(hvgq + 3)
可以用下面的函数来hook HvlGetQpcBias.
__int64 Hookhvgq(){if (ExGetPreviousMode() != KernelMode)//如果不是内核态调试,则hook,否则不hook{hook();}return *((ULONG64*)(*((ULONG64*)HvlpReferenceTscPage)) + 3);//这里加3是因为HvlGetQpcBias函数返回值是 *((_QWORD *)HvlpReferenceTscPage + 3);}
这里的hook()函数其实是搜索栈顶栈底,通过特征码找到
SystemCallIndex, SystemCallFunction,然后替换相应的函数。代码太多,这点下一篇看下