Infinity Hook
ETW (Event Tracing for Windows)
윈도우 운영체제에는 커널 또는 애플리케이션에서 발생한 이벤트를 추적할 수 있는 ETW (Event Tracing for Windows) 라는 기능이 존재합니다. Infinity Hook은 ETW 시스템 내에서 호출되는 함수를 후킹하여 시스템 콜을 가로채는 오픈소스 도구입니다. 
EtwpDebuggerData
후킹에 있어 가장 먼저 찾아야 하는 데이터입니다. ntoskrnl 이미지 영역 내 2C 08 04 38 0C 시그니처 검색을 통해 찾을 수 있습니다. 검색된 주소 -0x02 위치가 EtwpDebuggerData 에 해당됩니다.
1
2
3
4
5
6
7
8
9
2: kd> s nt L? 1046000 2c 08 04 38 0c
fffff807`6e010e3a 2c 08 04 38 0c f8 d8 00-70 10 98 0c 04 60 40 ac ,..8....p....`@.
2: kd> ln fffff807`6e010e3a-2
Browse module
Set bu breakpoint
(fffff807`6e010e38) nt!EtwpDebuggerData | (fffff807`6e010e60) nt!ExpSystemIsInCmosMode
Exact matches:
EtwpDebuggerDataSilo
EtwpDebuggerData + 0x10 위치에 함수 테이블 EtwpDebuggerDataSilo이 존재합니다.
1
2
2: kd> dq EtwpDebuggerData + 0x10
fffff807`6e010e48 ffffd98c`2790ac40 // EtwpDebuggerDataSilo[]
CkclWmiLoggerContext
EtwpDebuggerDataSilo + 0x10 위치에는 nt!_WMI_LOGGER_CONTEXT 구조를 가진 CkclWmiLoggerContext이 존재합니다.
1
2
2: kd> dq ffffd98c`2790ac40+0x10
ffffd98c`2790ac50 ffffd98c`2790e940 // CkclWmiLoggerContext
이 안에는 GetCpuClock이라는 필드가 존재합니다. 후킹에 사용되는 실질적인 데이터입니다.
1
2
3
4
5
6
7
8
9
10
11
2: kd> dt_WMI_LOGGER_CONTEXT ffffd98c`2790e940
nt!_WMI_LOGGER_CONTEXT
+0x000 LoggerId : 2
+0x004 BufferSize : 0x1000
+0x008 MaximumEventSize : 0xfb8
+0x00c LoggerMode : 0x2800480
+0x010 AcceptNewEvents : 0n0
+0x014 EventMarker : [2] 0xc0130000
+0x01c ErrorMarker : 0xc00d0000
+0x020 SizeMask : 0xffff
+0x028 GetCpuClock : 3
InfinityHook
InfinityHook은 Windows 7 부터 Windows 10 (Build 18363) 까지 지원합니다. 해당 버전 커널에서는 GetCpuClock가 함수 포인터로 사용됩니다. 이를 조작하여 후킹 코드를 실행합니다.
1
2
3
4
5
6
7
if (ctx->BuildNumber <= 18363)
{
// win 7 -> win10 1909
LOG_INFO("GetCpuClock Is 0x%p", *ctx->GetCpuClock);
*ctx->GetCpuClock = (PVOID)FakeGetCpuClock; // replace function.
LOG_INFO("Update GetCpuClock Is 0x%p", *ctx->GetCpuClock);
}
InfinityHookPro (Windows 10 Version 1909, Build 18363+)
InfinityHookPro는 Windows 10 (Build 19041) 부터 최신 버전 윈도우 (Windows 11) 까지 지원합니다.
해당 버전 커널에서는 더 이상 GetCpuClock 가 함수 포인터로 사용되지 않고, 인덱스 값으로 사용됩니다.
1
2
3
4
5
6
7
8
9
10
1: kd> dt nt!_WMI_LOGGER_CONTEXT ffff830f`7fda5240
+0x000 LoggerId : 2
+0x004 BufferSize : 0x1000
+0x008 MaximumEventSize : 0xfb8
+0x00c LoggerMode : 0x2800480
+0x010 AcceptNewEvents : 0n0
+0x014 EventMarker : [2] 0xc0130000
+0x01c ErrorMarker : 0xc00d0000
+0x020 SizeMask : 0xffff
+0x028 GetCpuClock : 2
인덱스 값에 따라 nt!EtwpGetLoggerTimeStamp 내부에서 호출되는 함수가 달라집니다.
1
2
3
4
5
6
7
8
9
10
1: kd> u EtwpGetLoggerTimeStamp
nt!EtwpGetLoggerTimeStamp:
fffff804`0d42c448 4883ec28 sub rsp,28h
fffff804`0d42c44c 488b4128 mov rax,qword ptr [rcx+28h]
fffff804`0d42c450 4883f803 cmp rax,3
fffff804`0d42c454 0f87f7ca1f00 ja nt!EtwpGetLoggerTimeStamp+0x1fcb09 (fffff804`0d628f51)
fffff804`0d42c45a 85c0 test eax,eax
fffff804`0d42c45c 7416 je nt!EtwpGetLoggerTimeStamp+0x2c (fffff804`0d42c474)
fffff804`0d42c45e 83e801 sub eax,1
fffff804`0d42c461 0f85b1ca1f00 jne nt!EtwpGetLoggerTimeStamp+0x1fcad0 (fffff804`0d628f18)
[0x02] nt!HalpTimerQueryHostPerformanceCounter
InfinityHookPro 에서는 GetCpuClock 값을 0x02 로 변경하고 있습니다.
1
2
*ctx->GetCpuClock = (PVOID)2;
LOG_INFO("Update GetCpuClock Is 0x%p", *ctx->GetCpuClock);
인덱스가 0x02인 경우 nt!HalpTimerQueryHostPerformanceCounter 가 호출됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
1: kd> u EtwpGetLoggerTimeStamp
nt!EtwpGetLoggerTimeStamp:
fffff804`0d42c448 4883ec28 sub rsp,28h
fffff804`0d42c44c 488b4128 mov rax,qword ptr [rcx+28h]
fffff804`0d42c450 4883f803 cmp rax,3
fffff804`0d42c454 0f87f7ca1f00 ja nt!EtwpGetLoggerTimeStamp+0x1fcb09 (fffff804`0d628f51)
fffff804`0d42c45a 85c0 test eax,eax
fffff804`0d42c45c 7416 je nt!EtwpGetLoggerTimeStamp+0x2c (fffff804`0d42c474)
fffff804`0d42c45e 83e801 sub eax,1
fffff804`0d42c461 0f85b1ca1f00 jne nt!EtwpGetLoggerTimeStamp+0x1fcad0 (fffff804`0d628f18)
1: kd> u nt!EtwpGetLoggerTimeStamp+0x1fcad0
nt!EtwpGetLoggerTimeStamp+0x1fcad0:
fffff804`0d628f18 83e801 sub eax,1
fffff804`0d628f1b 7413 je nt!EtwpGetLoggerTimeStamp+0x1fcae8 (fffff804`0d628f30)
fffff804`0d628f1d 83f801 cmp eax,1
fffff804`0d628f20 752f jne nt!EtwpGetLoggerTimeStamp+0x1fcb09 (fffff804`0d628f51)
fffff804`0d628f22 0f31 rdtsc
fffff804`0d628f24 48c1e220 shl rdx,20h
fffff804`0d628f28 480bc2 or rax,rdx
fffff804`0d628f2b e93e35e0ff jmp nt!EtwpGetLoggerTimeStamp+0x26 (fffff804`0d42c46e)
fffff804`0d628f30 488364243000 and qword ptr [rsp+30h],0
fffff804`0d628f36 488d4c2430 lea rcx,[rsp+30h]
fffff804`0d628f3b 488b059e7a7d00 mov rax,qword ptr [nt!HalPrivateDispatchTable+0x450 (fffff804`0de009e0)]
fffff804`0d628f42 e839edfdff call nt!guard_dispatch_icall (fffff804`0d607c80)
fffff804`0d628f47 488b442430 mov rax,qword ptr [rsp+30h]
fffff804`0d628f4c e91d35e0ff jmp nt!EtwpGetLoggerTimeStamp+0x26 (fffff804`0d42c46e)
fffff804`0d628f51 b93d000000 mov ecx,3Dh
fffff804`0d628f56 cd29 int 29h
1: kd> dqs fffff804`0de009e0
fffff804`0de009e0 fffff804`0d6b6990 nt!HalpTimerQueryHostPerformanceCounter
nt!HalpTimerQueryHostPerformanceCounter 내부에서는 함수 포인터를 통해 nt!HvlGetQpcBias 를 호출합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1: kd> uf HalpTimerQueryHostPerformanceCounter
nt!HalpTimerQueryHostPerformanceCounter:
fffff804`0d6b6990 48895c2408 mov qword ptr [rsp+8],rbx
fffff804`0d6b6995 57 push rdi
fffff804`0d6b6996 4883ec20 sub rsp,20h
fffff804`0d6b699a 488b05af567900 mov rax,qword ptr [nt!HalpPerformanceCounter (fffff804`0de4c050)]
fffff804`0d6b69a1 488bf9 mov rdi,rcx
fffff804`0d6b69a4 4885c0 test rax,rax
fffff804`0d6b69a7 743f je nt!HalpTimerQueryHostPerformanceCounter+0x58 (fffff804`0d6b69e8) Branch
nt!HalpTimerQueryHostPerformanceCounter+0x19:
fffff804`0d6b69a9 83b8e400000008 cmp dword ptr [rax+0E4h],8
fffff804`0d6b69b0 7536 jne nt!HalpTimerQueryHostPerformanceCounter+0x58 (fffff804`0d6b69e8) Branch
nt!HalpTimerQueryHostPerformanceCounter+0x22:
fffff804`0d6b69b2 48833dde39790000 cmp qword ptr [nt!HalpEnlightenment+0x178 (fffff804`0de4a398)],0
fffff804`0d6b69ba 742c je nt!HalpTimerQueryHostPerformanceCounter+0x58 (fffff804`0d6b69e8) Branch
InfinityHookPro는 해당 포인터 위치를 시그니처 검색을 통해 찾고 후킹 코드로 변조합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//
// Find HvlGetQpcBias.
//
ULONG64 RefHvlGetQpcBiasAddress = FindPatternImage(
ctx->NtoskrnlBase,
"\x48\x8b\x05\x00\x00\x00\x00\x48\x85\xc0\x74\x00\x48\x83\x3d\x00\x00\x00\x00\x00\x74", // before Win10 22H2 & before Win11 22621
"xxx????xxxx?xxx?????x",
".text");
if (!RefHvlGetQpcBiasAddress)
{
//All of these feature codes are present.
RefHvlGetQpcBiasAddress = FindPatternImage(
ctx->NtoskrnlBase,
"\x48\x8b\x05\x00\x00\x00\x00\xe8\x00\x00\x00\x00\x48\x03\xd8\x48\x89\x1f",
"xxx????x????xxxxxx",
".text");
if (!RefHvlGetQpcBiasAddress)
{
LOG_ERROR("Find HvlGetQpcBias Failed!");
return FALSE;
}
}
덤프를 통해 확인해보면 함수 포인터가 변경되어 있습니다.
1
2
1: kd> dqs nt!HalpEnlightenment+0x178
fffff804`0de4a398 fffff806`18307d40 0Ab5dgbQCYlbeUuu+0x7d40