1.前言
这个反调试需要hook,但实际上有很多的限制。比如win10/win11的超强patchgurad,让做这件事情变得困难。但是大致hook的一个模板记录,总体遵循几个步骤:模块首地址-》段首地址-》段内搜索函数特征-》找到函数-》hook。本篇看下。
2.概括
ntoskrnl.exe里面有个函数HvlGetQpcBias,这个函数可以适当的Hook来检测或者更改一些东西。.
__int64 HvlGetQpcBias()
{
return *((_QWORD *)HvlpReferenceTscPage + 3);
}
函数体
0: kd> x *!*HvlGetQpcBias
fffff803`22d91e40 nt!HvlGetQpcBias (HvlGetQpcBias)
0: kd> uf fffff803`22d91e40
nt!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指令的立即数,所以这里加3
hvlp = 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,然后替换相应的函数。代码太多,这点下一篇看下