InfinityHook 学习文章
InfinityHook 是一种用于 Windows 内核钩子的技术,通过修改系统调用的调度机制来实现对内核操作的拦截和处理。本文将详细介绍 InfinityHook 的实现原理、早期 ETW Hook 技术的演变过程以及如何兼容最新版本的 Windows。
1. InfinityHook 概述
InfinityHook 利用微软在记录 ETW(Event Tracing for Windows)时系统调用的漏洞,通过替换函数指针实现对系统调用的拦截,而不会触发 PatchGuard(PG)的保护。ETW 是 Windows 提供的一种高效的内核级别的事件跟踪机制,广泛用于调试和性能分析。
2. 早期的 ETW Hook 技术
2.1 初代 ETW Hook
最早的 ETW Hook 通过替换一些 HalPxx 指针来实现内核级别的钩子,但这种方法已经被 PatchGuard 监控,难以继续使用。
2.2 第二代 ETW Hook
第二代 ETW Hook 的原理类似于 EAC(Easy Anti-Cheat)的异常接管,通过修改 HalpStallCounter[0xE] 和 NtGlobalFlags 实现对异常的拦截。具体方法是:
- 修改 HalpStallCounter[0xE] 指向的函数,从而接管异常。
- 修改 NtGlobalFlags 标志位,以实现对特定异常的捕获和处理。
下图展示了发生异常时的调用栈,可以看到 EAC 修改了 HalpStallCounter 来接管异常:
3. 新版 ETW Hook 的实现
新版 ETW Hook 提出了更为隐蔽和高效的实现方法,通过修改 Windows .data 节中的指针来实现内核级别的钩子。具体原理如下:
3.1 系统调用路径分析
在 Windows 系统中,ETW 的调用路径中会涉及将原始系统调用存放在栈上,这就为我们修改栈上的位置拦截系统调用提供了机会。
3.2 关键函数分析
进入 EtwTraceSiloKernelEvent
函数,可以看到在调用过程中会触发 EtwpLogKernelEvent
函数:
void __fastcall EtwTraceSiloKernelEvent(
__int64 a1,
__int64 a2,
unsigned int a3,
unsigned int a4,
unsigned __int16 a5,
int a6)
{
// ... (中间部分省略)
if ( a1 )
{
v15 = *(_QWORD *)(*(_QWORD *)(a1 + 1272) + 864i64);
if ( v15 )
{
v16 = *(_DWORD *)(v15 + 4224);
while ( 1 )
{
v11 = !_BitScanForward(&v19, v16);
if ( v11 )
break;
v17 = v19;
v16 &= v16 - 1;
v18 = 32i64 * v19 + v15 + 4260;
if ( v18 && ((unsigned int)v9 & *(_DWORD *)(v18 + 4 * (v9 >> 29)) & 0x1FFFFFFF) != 0 )
EtwpLogKernelEvent(a2, v15, *(unsigned __int8 *)(v15 + 2 * v17 + 4208), a3, a5, a6);
}
}
}
}
EtwpLogKernelEvent
是主要的事件记录函数,其中调用了 HalPrivateDispatchTable.HalCollectPmcCounters
:
v21 = *(struct _HAL_PMC_COUNTERS **)(PmcData + 8i64 * (unsigned int)KeGetPcr()->Prcb.Number + 24);
if ( v21 )
HalPrivateDispatchTable.HalCollectPmcCounters(v21, (unsigned __int64 *)(v14 + 16));
else
memset((void *)(v14 + 16), 0, 8i64 * (unsigned __int8)v8);
这个地方 HalPrivateDispatchTable.HalCollectPmcCounters
可以被替换,从而实现对系统调用的拦截。
3.3 实现步骤
- 偷指针,替换函数
替换.data
节中的指针,例如HalPrivateDispatchTable
,实现对系统调用的接管。 - 配置 NT Kernel Logger,开启 ETW
调用ZwTraceControl
开启 NT Kernel Logger,并进行相应配置。 - 设置 PMC Counter
调用NtSetSystemInformation
设置 PMC Counter,从而使系统调用进入EtwpReserveWithPmcCounters
函数。 - 栈查找定位 Syscall Routine
通过栈上的 Magic Number 定位系统调用的地址,并进行替换。 - 替换系统调用
替换需要 Hook 的系统调用,实现自定义处理逻辑。
3.4 关键代码示例
以下代码展示了如何开启 PMC Counter:
NTSTATUS EtwInitilizer::open_pmc_counter()
{
auto status = STATUS_SUCCESS;
auto pmc_count_info = (PEVENT_TRACE_PROFILE_COUNTER_INFORMATION)(nullptr);
auto pmc_event_info = (PEVENT_TRACE_SYSTEM_EVENT_INFORMATION)(nullptr);
constexpr auto syscall_hookid = 0xf33ul;
if (!__is_open) return STATUS_FLT_NOT_INITIALIZED;
do {
auto EtwpDebuggerData = reinterpret_cast<ULONG***>(
kstd::SysInfoManager::getInstance()->getSysInfo()->EtwpDebuggerData);
if (!EtwpDebuggerData) {
status = STATUS_NOT_SUPPORTED;
LOG_ERROR("failed to get EtwpDebuggerData!\r\n");
break;
}
auto logger_id = EtwpDebuggerData[2][2][0];
pmc_count_info = kalloc<EVENT_TRACE_PROFILE_COUNTER_INFORMATION>(NonPagedPool);
if (!pmc_count_info) {
LOG_ERROR("failed to alloc memory for pmc_count!\r\n");
status = STATUS_MEMORY_NOT_ALLOCATED;
break;
}
pmc_count_info->EventTraceInformationClass = EventTraceProfileCounterListInformation;
pmc_count_info->TraceHandle = ULongToHandle(logger_id);
pmc_count_info->ProfileSource[0] = 1;
status = ZwSetSystemInformation(SystemPerformanceTraceInformation, pmc_count_info, sizeof(EVENT_TRACE_PROFILE_COUNTER_INFORMATION));
if (!NT_SUCCESS(status)) {
LOG_ERROR("failed to configure pmc counter, errcode=%x\r\n", status);
break;
}
pmc_event_info = kalloc<EVENT_TRACE_SYSTEM_EVENT_INFORMATION>(NonPagedPool);
if (!pmc_event_info) {
LOG_ERROR("failed to alloc memory for pmc_event_info!\r\n");
status = STATUS_MEMORY_NOT_ALLOCATED;
break;
}
pmc_event_info->EventTraceInformationClass = EventTraceProfileEventListInformation;
pmc_event_info->TraceHandle = ULongToHandle(logger_id);
pmc_event_info->HookId[0] = syscall_hookid;
status = ZwSetSystemInformation(SystemPerformanceTraceInformation, pmc_event_info, sizeof(EVENT_TRACE_SYSTEM_EVENT_INFORMATION));
if (!NT_SUCCESS(status)) {
LOG_ERROR("failed to configure pmc event, errcode=%x\r\n", status);
break;
}
} while (false);
if (pmc_count_info) ExFreePool(pmc_count_info);
if (pmc_event_info) ExFreePool(pmc_event_info);
return status;
}
4. 安全性和注意事项
在实现和使用 InfinityHook 时,需要注意以下几点:
- 合法性:必须遵守相关法律法规和软件使用协议,避免非法监控或篡改系统行为。
- 系统稳定性:确保钩子的稳定性和兼容性,避免导致系统崩溃或不可预测的行为。
- 检测和防护:虽然 InfinityHook 具有较高的隐蔽性,但仍可能被高级防护软件检测到,需要不断改进钩子技术以应对新的安全防护措施。
5. 总结
InfinityHook 是一种强大的内核级钩子技术,通过修改系统调用调度机制实现对内核操作的拦截和处理。其高效、隐蔽和灵活的特点使其在多种安全监控和系统调试场景中具有广泛应用。本文详细介绍了 InfinityHook 的实现原理、早期 ETW Hook 的演变过程以及如何兼容最新版本的 Windows。希望本文能够帮助读者更好地理解和应用 InfinityHook 技术。