驱动代码基本结构

模块名 功能定位
_global 全局工具库:封装内核内存分配、随机化、安全拷贝等通用操作,是其他模块的基础依赖。
hider 隐藏规则管理:维护进程隐藏条目列表,处理 “隐藏 / 取消隐藏” 指令,联动反调试标记修改。
hooklib 挂钩工具库:实现内联Hook(指令替换)的基础逻辑,支持32/64位系统的API挂钩 / 解钩。
hooks 核心挂钩实现:挂钩内核调试相关API(如NtQueryInformationProcess),篡改返回结果实现反调试。
log 日志模块:将驱动运行信息同时输出到调试器(WinDbg)和磁盘日志文件,用于调试和问题定位。
misc 辅助工具:提供 “通过句柄获取PID” 等内核对象操作的辅助函数,依赖未公开内核结构。
ntdll NTDLL解析:加载ntdll.dll到内核内存,提取其导出函数对应的SSDT索引,为挂钩提供基础。
pe PE文件解析:处理PE文件的RVA / 文件偏移转换、导出函数查找,支持内核中解析NTDLL等模块。
ssdt SSDT操作:查找系统服务描述符表(SSDT),实现内核函数的SSDT Hook/ 解钩,是内核层挂钩的核心。
threadhidefromdbg 线程反调试处理:动态定位ETHREAD结构体偏移,移除线程的HideFromDebugger反调试标记。
TitanHide 驱动主入口:负责驱动初始化(设备创建、符号链接、模块依赖加载)、IRP处理(用户层指令交互)、卸载清理。
undocumented 未公开API封装:定义并调用Windows内核未公开的函数 / 结构(如ZwQueryInformationProcess),突破官方接口限制。

TitanHide 驱动程序入口点

TitanHideWindows内核模式驱动,其入口点是DriverEntry(定义在TitanHide.cpp中),这是Windows内核驱动的标准入口函数(类似用户态程序的main)。
DriverEntry核心初始化流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
// Initialize name buffers
// 初始化设备名缓冲区(内核态设备名)
RtlInitEmptyUnicodeString(&DeviceName, DeviceNameBuffer, sizeof(DeviceNameBuffer));
RtlAppendUnicodeToString(&DeviceName, L"\\Device\\");
// 初始化符号链接缓冲区(用户态可见的链接名)
RtlInitEmptyUnicodeString(&Win32Device, Win32DeviceBuffer, sizeof(Win32DeviceBuffer));
RtlAppendUnicodeToString(&Win32Device, L"\\DosDevices\\");

// Derive the device name and symbolic link from the registry path, 从注册表路径提取驱动名(优先),否则用默认名TitanHide
UNICODE_STRING DriverName = {};
if (RegistryPath != NULL && RegistryPath->Buffer != NULL)
{
// 从注册表路径提取驱动名
for (int i = 0; i < RegistryPath->Length / sizeof(WCHAR); i++)
{
auto index = RegistryPath->Length / sizeof(WCHAR) - i - 1;
if (RegistryPath->Buffer[index] == L'\\')
{
index++; // skip the backslash
DriverName.Buffer = RegistryPath->Buffer + index;
DriverName.Length = (USHORT)(RegistryPath->Length - index * sizeof(WCHAR));
DriverName.MaximumLength = DriverName.Length;
break;
}
}
}

// Fall back to default driver name,兜底:默认驱动名TitanHide
if (DriverName.Length == 0)
{
RtlInitUnicodeString(&DriverName, L"TitanHide");
}

// Use the driver name
RtlAppendUnicodeStringToString(&DeviceName, &DriverName);
RtlAppendUnicodeStringToString(&Win32Device, &DriverName);
// 初始化日志(驱动名作为日志标识)
InitLog(&DriverName);
Log("[TITANHIDE] DriverName: %.*ws\r\n", DriverName.Length / sizeof(WCHAR), DriverName.Buffer);

PDEVICE_OBJECT DeviceObject = NULL;
NTSTATUS status;

//set callback functions
DriverObject->DriverUnload = DriverUnload;// 注册卸载函数(驱动卸载时调用)
for(unsigned int i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)// 初始化所有IRP主函数为默认处理函数(返回不支持)
DriverObject->MajorFunction[i] = DriverDefaultHandler;
// 覆盖CREATE/CLOSE/WRITE的处理函数
DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreateClose;// 打开/关闭设备
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverCreateClose;
DriverObject->MajorFunction[IRP_MJ_WRITE] = DriverWrite;// 处理用户态写入的指令

//read ntdll.dll from disk so we can use it for exports,初始化NTDLL模块(读取ntdll.dll的导出表,用于获取SSDT索引)
if(!NT_SUCCESS(NTDLL::Initialize()))
{
Log("[TITANHIDE] Ntdll::Initialize() failed...\r\n");
return STATUS_UNSUCCESSFUL;
}

//initialize undocumented APIs,初始化未公开内核函数指针(Undocumented模块)
if(!Undocumented::UndocumentedInit())
{
Log("[TITANHIDE] UndocumentedInit() failed...\r\n");
return STATUS_UNSUCCESSFUL;
}
Log("[TITANHIDE] UndocumentedInit() was successful!\r\n");

//find the offset of CrossThreadFlags in ETHREAD,查找ETHREAD的CrossThreadFlags偏移(ThreadHideFromDebugger模块)
status = FindCrossThreadFlagsOffset(&CrossThreadFlagsOffset);
if(!NT_SUCCESS(status))
{
Log("[TITANHIDE] FindCrossThreadFlagsOffset() failed: 0x%lX\r\n", status);
return status;
}

//create io device,创建内核设备对象
status = IoCreateDevice(DriverObject,
0,// 无扩展数据
&DeviceName,// 设备名:\Device\TitanHide
FILE_DEVICE_UNKNOWN, // 未知设备类型
FILE_DEVICE_SECURE_OPEN, // 安全打开(需要权限)
FALSE,
&DeviceObject);
if(!NT_SUCCESS(status))
{
Log("[TITANHIDE] IoCreateDevice Error...\r\n");
return status;
}
if(!DeviceObject)
{
Log("[TITANHIDE] Unexpected I/O Error...\r\n");
return STATUS_UNEXPECTED_IO_ERROR;
}
Log("[TITANHIDE] Device %.*ws created successfully!\r\n", DeviceName.Length / sizeof(WCHAR), DeviceName.Buffer);

//create symbolic link,配置设备属性(缓冲I/O)
DeviceObject->Flags |= DO_BUFFERED_IO;// 缓冲I/O模式(系统拷贝用户态数据到内核态)
DeviceObject->Flags &= (~DO_DEVICE_INITIALIZING); // 清除初始化标志(设备可用)
status = IoCreateSymbolicLink(&Win32Device, &DeviceName); //创建用户态符号链接
if(!NT_SUCCESS(status))
{
Log("[TITANHIDE] IoCreateSymbolicLink Error...\r\n");
return status;
}
Log("[TITANHIDE] Symbolic link %.*ws->%.*ws created!\r\n", Win32Device.Length / sizeof(WCHAR), Win32Device.Buffer, DeviceName.Length / sizeof(WCHAR), DeviceName.Buffer);

//initialize hooking
Log("[TITANHIDE] Hooks::Initialize() hooked %d functions\r\n", Hooks::Initialize());

return STATUS_SUCCESS;
}
  1. 设备与符号链接初始化:
    构造内核设备名(默认\Device\TitanHide)和用户态符号链接名(\DosDevices\TitanHide),用于用户态与内核态通信。
  2. 注册驱动回调:
    注册DriverUnload函数(驱动卸载时的清理逻辑);
    初始化IRPI/O请求包)处理函数(仅处理IRP_MJ_CREATE/CLOSE/WRITE三类请求)。
  3. 依赖模块初始化:
    加载ntdll.dll到内核内存(NTDLL::Initialize),提取其导出函数的SSDT索引;
    初始化未公开内核APIUndocumented::UndocumentedInit);
    动态查找ETHREAD结构体中CrossThreadFlags偏移(FindCrossThreadFlagsOffset)。
  4. 创建内核设备与符号链接:
    调用IoCreateDevice创建内核设备对象;
    调用IoCreateSymbolicLink创建用户态可访问的符号链接(用户态通过\\.\TitanHide访问)。
  5. 挂钩内核函数:
    调用Hooks::Initialize挂钩调试相关内核API(如NtQueryInformationProcess、NtSetInformationThread)。

TitanHide 驱动工作流程

  1. 用户态指令接收:
    用户态(GUI/ 插件)通过WriteFile向驱动设备(\\.\TitanHide)写入HIDE_INFO结构体(包含指令、目标PID、隐藏类型);
    驱动通过IRP_MJ_WRITE处理请求,解析HIDE_INFO数据。

  2. 隐藏规则维护:
    调用Hider::ProcessData处理指令:
    HidePid:将目标PID及隐藏类型加入隐藏条目列表,并调用UndoHideFromDebuggerInRunningThreads移除线程的反调试标记;
    UnhidePid:从隐藏条目列表中移除目标PID
    UnhideAll:清空隐藏条目列表。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    //usable functions
    bool Hider::ProcessData(PVOID Buffer, ULONG Size)
    {
    // 校验数据合法性:数据长度必须是HIDE_INFO的整数倍
    if(Size % sizeof(HIDE_INFO))
    return false;
    size_t HideInfoCount = Size / sizeof(HIDE_INFO);
    HIDE_INFO* HideInfo = (HIDE_INFO*)Buffer;// 解析用户态传来的指令数组
    for(size_t i = 0; i < HideInfoCount; i++)
    {
    switch(HideInfo[i].Command)
    {
    case HidePid: // 隐藏指定PID的进程
    {
    int FoundEntry = EntryFind(HideInfo[i].Pid);
    if(FoundEntry == -1)// 该进程未被隐藏,新增条目
    {
    HIDE_ENTRY HideEntry;
    HideEntry.Pid = HideInfo[i].Pid;
    HideEntry.Type = HideInfo[i].Type;
    EntryAdd(&HideEntry);
    }
    else// 该进程已被隐藏,叠加隐藏类型
    {
    EntrySet(FoundEntry, HideInfo[i].Type);
    }

    // Use DKOM to disable HideThreadHideFromDebugger in any threads in the target process that already have this flag set
    if((HideInfo[i].Type & (ULONG)HideThreadHideFromDebugger) != 0 && CrossThreadFlagsOffset != 0)
    {
    const NTSTATUS Status = UndoHideFromDebuggerInRunningThreads(HideInfo[i].Pid);// 通过DKOM清理目标进程已有线程的HideThreadHideFromDebugger标志
    if(!NT_SUCCESS(Status))
    {
    Log("[TITANHIDE] Failed to undo HideThreadHideFromDebugger in running threads! Status = 0x%08lX\n", Status);
    }
    }
    }
    break;

    case UnhidePid:// 取消指定PID的部分/全部隐藏类型
    {
    int FoundEntry = EntryFind(HideInfo[i].Pid);
    if(FoundEntry != -1)
    {
    EntryUnset(FoundEntry, HideInfo[i].Type);
    if(!EntryGet(FoundEntry)) //nothing left to hide for PID, 若该进程已无任何隐藏类型,删除条目
    EntryDel(FoundEntry);
    }
    }
    break;

    case UnhideAll:// 取消所有进程的隐藏
    {
    EntryClear();
    }
    break;
    }
    }
    return true;
    }
  3. 内核API挂钩拦截:
    被挂钩的内核API(如NtQueryInformationProcess)被调用时,先检查当前进程PID是否在隐藏列表中;
    若处于隐藏状态,篡改API返回结果(如伪装调试端口为空、隐藏调试对象、清空调试寄存器),使VMP等反调试无法获取真实的调试状态。

插件与驱动的通信机制

TitanHide插件(x64dbg/x32dbg插件)是用户态工具,通过Windows设备I/O接口 与内核驱动通信,核心流程如下:

  1. 通信前提
    驱动已加载:插件需先确认TitanHide驱动已通过sc start TitanHide等方式加载;
    设备路径一致:插件与驱动约定设备名(默认TitanHide),插件通过\\.\TitanHide访问驱动设备。
  2. 通信流程(以x64dbg插件为例)
    (1)插件获取目标进程PID
    插件通过x64dbg的回调(CBCREATEPROCESS/CBATTACH)捕获当前调试进程的PID
    (2)插件构造指令结构体
    插件构造HIDE_INFO结构体(定义在TitanHide.h中):
    1
    2
    3
    4
    5
    struct HIDE_INFO {
    HIDE_COMMAND Command; // 指令:HidePid/UnhidePid/UnhideAll
    ULONG Type; // 隐藏类型(位掩码,如 HideProcessDebugPort)
    ULONG Pid; // 目标进程 PID
    };
    (3)插件打开驱动设备
    调用CreateFileA打开驱动设备(用户态访问内核设备的标准方式):
    1
    HANDLE hDevice = CreateFileA("\\\\.\\TitanHide", GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
    (4)插件发送指令
    调用WriteFileHIDE_INFO结构体写入驱动设备,完成指令传递:
    1
    WriteFile(hDevice, &HideInfo, sizeof(HIDE_INFO), &written, 0);
    (5)驱动处理指令
    驱动通过IRP_MJ_WRITE接收HIDE_INFO数据,执行 “隐藏 / 取消隐藏” 逻辑。
  3. 通信特点
    同步通信:插件调用WriteFile是同步操作,直到驱动处理完请求才返回;
    无返回值:插件仅发送指令,驱动执行结果通过日志(log.cpp)记录,插件需通过调试器日志或驱动日志查看状态;
    权限要求:插件需以管理员权限运行(否则CreateFileA无法打开内核设备)。

具体示例

VMP通过查询ProcessDebugPort(7)ProcessDebugObjectHandle(31)两个核心特征判定调试器是否附加,TitanHide则通过内核层挂钩篡改查询结果 + 维护隐藏规则,从内核层彻底屏蔽这两个特征,实现反调试绕过。

  1. 用户态注册隐藏规则(插件 /GUI触发)
    当用户通过x64dbg插件 /GUI选择 “隐藏目标PID” 时,会向TitanHide驱动发送指令:
    构造指令结构体:

    1
    2
    3
    4
    5
    HIDE_INFO hideInfo = {
    .Command = HidePid, // 隐藏指定PID指令
    .Type = HideProcessDebugPort | HideProcessDebugObjectHandle, // 需隐藏的特征类型
    .Pid = 目标进程PID(如VMP保护的进程)
    };

    发送指令到驱动:
    插件 /GUI通过CreateFileA(“\\\\.\\TitanHide“)打开驱动设备;
    调用WriteFile将hideInfo写入驱动,驱动通过IRP_MJ_WRITE接收指令;
    驱动调用Hider::ProcessData将该PID和隐藏类型(DebugPort+DebugObjectHandle)加入HideEntries隐藏列表。

  2. 内核层挂钩NtQueryInformationProcess
    TitanHide在初始化时(DriverEntry)会调用Hooks::Initialize(),通过SSDT Hook替换内核原生的NtQueryInformationProcess为自定义的HookNtQueryInformationProcess函数,当VMP调用该API 时,会触发以下篡改逻辑:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    static NTSTATUS NTAPI HookNtQueryInformationProcess(
    IN HANDLE ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength)
    {
    // 获取目标进程 PID
    ULONG pid = Misc::GetProcessIDFromProcessHandle(ProcessHandle);

    // Handle ProcessDebugObjectHandle early,优先拦截 ProcessDebugObjectHandle
    if(ProcessInformationClass == ProcessDebugObjectHandle &&
    ProcessInformation != nullptr &&
    ProcessInformationLength == sizeof(HANDLE) &&
    Hider::IsHidden(pid, HideProcessDebugObjectHandle))//检查该PID是否在隐藏列表中
    {
    // 验证进程句柄,获取EPROCESS内核对象
    PEPROCESS Process;
    NTSTATUS Status = ObReferenceObjectByHandle(ProcessHandle,
    PROCESS_QUERY_INFORMATION,
    *PsProcessType,
    ExGetPreviousMode(),
    (PVOID*)&Process,
    nullptr);
    if(!NT_SUCCESS(Status))
    return Status;

    // (The kernel calls DbgkOpenProcessDebugPort here)

    ObDereferenceObject(Process);

    __try
    {
    ProbeForWrite(ProcessInformation, sizeof(HANDLE), 4);

    if (ReturnLength != nullptr)
    ProbeForWrite(ReturnLength, sizeof(ULONG), 1);

    //将调试对象句柄强制置为NULL(VMP检测到=0,判定无调试器)
    *(PHANDLE)ProcessInformation = nullptr;

    if (ReturnLength != nullptr)
    *ReturnLength = sizeof(HANDLE);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
    return GetExceptionCode();
    }
    return STATUS_PORT_NOT_SET;
    }

    // 调用原生 API 获取真实结果,先查真实值再篡改
    NTSTATUS ret = Undocumented::NtQueryInformationProcess(ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength);
    if(NT_SUCCESS(ret) &&
    ProcessInformation &&
    ProcessInformationClass != ProcessBasicInformation) //prevent stack overflow
    {
    if(ProcessInformationClass == ProcessDebugFlags)// 篡改 ProcessDebugFlags
    {
    if(Hider::IsHidden(pid, HideProcessDebugFlags))//检查该PID是否在隐藏列表中
    {
    Log("[TITANHIDE] ProcessDebugFlags by %d\r\n", pid);
    __try
    {
    BACKUP_RETURNLENGTH();

    *(unsigned int*)ProcessInformation = TRUE;// 篡改:强制置为TRUE

    RESTORE_RETURNLENGTH();
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
    ret = GetExceptionCode();
    }
    }
    }
    else if (ProcessInformationClass == ProcessDebugPort)// 篡改 ProcessDebugPort
    {
    if(Hider::IsHidden(pid, HideProcessDebugPort))//检查该PID是否在隐藏列表中
    {
    Log("[TITANHIDE] ProcessDebugPort by %d\r\n", pid);
    __try
    {
    BACKUP_RETURNLENGTH();

    *(ULONG_PTR*)ProcessInformation = 0;// 篡改:调试端口强制置0

    RESTORE_RETURNLENGTH();
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
    ret = GetExceptionCode();
    }
    }
    }
    }
    return ret;
    }