看了uty的《kernel inline hook 绕过vice检测》,思路很好,但实现起来麻烦了点。
这里以CmEnumerateKey为例介绍实现inline hook的简单方法,尽量依靠编译器做更多的事情。
1、编写fake_CmEnumerateKey,在其中对CmEnumerateKey的调用做后续处理。
NTSTATUS
fake_CmEnumerateKey(
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
IN ULONG Index,
IN KEY_INFORMATION_CLASS KeyInformationClass,
IN PVOID KeyInformation,
IN ULONG Length,
IN PULONG ResultLength
)
{
NTSTATUS status = STATUS_SUCCESS;
KdPrint(("[cmhook] nt!CmEnumerateKey() called./n"));
status = JMP_CmEnumerateKey(KeyControlBlock, Index, KeyInformationClass, KeyInformation, Length, ResultLength);
return status;
}
2、取得CmEnumerateKey的地址,备份前7个字节,之后修改CmEnumerateKey的前5个字节为jmp xxxxxxxx(fake_CmEnumerateKey入口偏移地址)。
// 保存原始的7个字节代码
BYTE g_cOrigCode[7] = {0};
// JMP xxxxxxxx
BYTE g_cHookCode[5] = { 0xe9, 0, 0, 0, 0 };
RtlCopyMemory(g_cOrigCode, (BYTE*)CmEnumerateKey, 7);
// calc jmp offset
*( (ULONG*)(g_cHookCode+1) ) = (ULONG)fake_CmEnumerateKey - (ULONG)CmEnumerateKey - 5;
DisableWriteProtect(&ulAttr);
RtlCopyMemory((BYTE*)CmEnumerateKey, g_cHookCode, 5);
EnableWriteProtect(ulAttr);
3、编写__declspec(naked) JMP_CmEnumerateKey,这个例程实现CmEnumerateKey被修改个7个字节的代码,并转入nt!CmEnumerateKey+7处开始执行。
__declspec(naked)
NTSTATUS
JMP_CmEnumerateKey(
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
IN ULONG Index,
IN KEY_INFORMATION_CLASS KeyInformationClass,
IN PVOID KeyInformation,
IN ULONG Length,
IN PULONG ResultLength
)
{
__asm
{
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
}
}
JMP_CmEnumerateKey的主要实现代码在程序运行时进行填充,以下是实现代码:
BYTE jmp_orig_code[7] = { 0xEA, 0, 0, 0, 0, 0x08, 0x00 };
// jmp 0008:nt!CmEnumerateKey+7
*( (ULONG*)(jmp_orig_code+1) ) = (ULONG)((BYTE*)CmEnumerateKey + 7);
DisableWriteProtect(&ulAttr);
//
// CmEnumerateKey修改之前的前7个字节的代码
// 6a18 push 18h
// 68d0de4d80 push offset nt!`string'+0xf8 (804dded0)
//
RtlCopyMemory((BYTE*)JMP_CmEnumerateKey, g_cOrigCode, 7);
// jmp 0008:nt!CmEnumerateKey+7
RtlCopyMemory((BYTE*)JMP_CmEnumerateKey+7, jmp_orig_code, 7);
EnableWriteProtect(ulAttr);
这里要注意一个细节,虽然只CmEnumerateKey修改了前5个字节,但这5个字节里面包含两条指令(共7个字节),因此要保存前7个字节。
修改前:
kd> u nt!CmEnumerateKey
nt!CmEnumerateKey:
80605f54 6a18 push 18h
80605f56 68d0de4d80 push offset nt!`string'+0xf8 (804dded0)
80605f5b e80839f2ff call nt!_SEH_prolog (80529868)
80605f60 e829abffff call nt!CmpLockRegistry (80600a8e)
80605f65 8b4508 mov eax,dword ptr [ebp+8]
80605f68 f6400502 test byte ptr [eax+5],2
80605f6c 7407 je nt!CmEnumerateKey+0x21 (80605f75)
80605f6e be7c0100c0 mov esi,0C000017Ch
修改后:
kd> u nt!CmEnumerateKey
nt!CmEnumerateKey:
80605f54 e999143279 jmp cmhook+0x3f2 (f99273f2)
80605f59 4d dec ebp
80605f5a 80e808 sub al,8
80605f5d 39f2 cmp edx,esi
80605f5f ff ???
80605f60 e829abffff call nt!CmpLockRegistry (80600a8e)
80605f65 8b4508 mov eax,dword ptr [ebp+8]
80605f68 f6400502 test byte ptr [eax+5],2
–
by CDrea