axxo1337/AntiDebug

GitHub: axxo1337/AntiDebug

一个基于终端界面的 Windows x86_64 反调试技术演示与测试工具,集成了多种经典反调试检测方法并附带详细的原理文档和绕过说明。

Stars: 42 | Forks: 7

# AntiDebug ## 这是什么? 这是一个简单的终端界面工具,用于测试 Windows x86_64 反调试技术。它是为一个名为 axxo1337 的现已退役的项目制作的。我将此仓库移至我的个人主页,因为该组织已被设置为只读。 ## 构建 这仅需要安装 Visual Studio 2022(最低版本)以及“使用 C++ 的桌面开发”包。本项目使用的唯一依赖项是 Windows API 和 [FTXUI](https://github.com/ArthurSonzogni/FTXUI)。FTXUI 已直接包含在 `dependencies/` 文件夹中。我知道这不是一个好的做法,但我希望尽可能保持构建环境简单, 并尽量与整个课程中初学者所使用的环境保持一致。 ## 用法 只需运行可执行文件,然后将你的调试器附加到该进程即可。启动时控制台标题会被方便地设置为“AntiDebug”,以便你轻松找到它。你可以使用向上和向下方向键导航并按 `ENTER` 或 `SPACE` 来切换检测项。你可以使用 `Q` 终止程序。检测在启用后会持续运行,所以只需动手实验,看看哪些能检测到你,祝你好运! ![AntiDebug 运行截图。](https://static.pigsec.cn/wp-content/uploads/repos/cas/ff/fff98df384f56037cf86650eccd0dc7365a58427c40666ada972f2a586f6436c.png) ### 添加检测 如果你想添加检测,首先需要为该检测创建回调。为此,请进入 `antidebug.h`,使用以下格式进行定义: `void callbackMyDetectionName(AntiDebugOption& option);`,然后进入 `antidebug.cpp`,像这样创建其声明: ``` void AntiDebug::callbackMyDetectionName(AntiDebugOption& option) { // Example detection option.detected = IsDebuggerPresent(); } ``` 将 `option.detected` 设置为 true 或 false,一旦检测完全集成,就会立即更新 UI。在这里你只需根据需要设置该值,无需处理其他任何事情。禁用逻辑和 UI 事件已在 `main` 函数中处理完毕。
现在要集成该检测,请在 `antidebug.h` 中找到 `options`,并在其初始化列表中添加一个新成员,如下所示: ``` AntiDebug::AntiDebugOptions anti_debug_options { ADD_ANTI_DEBUG_OPTION("IsDebuggerPresent", true, callbackIsDebuggerPresent), ADD_ANTI_DEBUG_OPTION("MyDetectionName", true, callbackMyDetectionName), // <- Example integration }; ``` 第一个参数是要在 UI 中显示的标题,第二个参数设置默认是否启用该检测,第三个参数是你的回调。如果您的回调比较消耗资源,你现在还可以指定第四个参数,用于设置每次执行回调后等待的延迟时间(以毫秒为单位)。 ## 文档 在本节中,我将尽我所能记录所使用的不同反调试技术,并可能添加一些额外的细节,比如如何规避它们等。 ### `IsDebuggerPresent` `IsDebuggerPresent` 是一个 Windows API 函数,它只需访问当前进程的线程环境块(TEB)中的 `BeingDebugged` 布尔值。当进程正在被调试时,该值为 true。只需将 `BeingDebugged` 设置为 0 即可绕过此检测。修改 `IsDebuggerPresent` 可能看起来很蠢,因为任何有一点常识的人都不会直接调用它,然而现有的大多数程序都这么做了。 ### `BeingDebugged` 此技术直接读取当前进程的进程环境块(PEB)中的 `BeingDebugged` 成员。如果为 true,则表示正在被调试。只需将该值设置为 0 即可绕过。 ### `CheckRemoteDebuggerPresent` `CheckRemoteDebuggerPresent` 是一个 Windows API 函数,它在底层调用 `NtQueryInformationProcess` 来获取 `ProcessDebugPort` 值。如果此值为 0,则表示未附加调试器;否则,它会将传递给它的布尔指针设置为 true。可以通过 hook `NtQueryInformationProcess` 或该函数本身来绕过。由于 `ProcessDebugPort` 位于 `EPROCESS` 结构中,因此它不受用户模式下的这些小把戏的影响。 ### `NtQueryInformationProcess` `NtQueryInformationProcess` 可用于获取 `ProcessDebugPort`,从而指示是否存在调试器。它还可以以未公开的方式查询一些值,例如堆标志(`ProcessDebugFlags`)和调试对象句柄(`ProcessDebugHandle`)。 ### `FindWindowByTitle` 通过枚举窗口并检查其标题来搜索调试工具。它会搜索窗口标题中的子字符串,例如 "Cheat Engine" 或 "Process Hacker"。这种子字符串匹配提供了灵活性,即使工具版本号被附加到标题上,也能检测出不同版本的工具。可以通过隐藏调试器窗口、更改窗口标题以及 hook `FindWindowExA` 函数来绕过此检测。 ### `FindWindowByClass` 通过枚举窗口并检查其类来搜索调试工具。它使用 Windows API 的 `FindWindowA` 来检测特定的窗口类,如 "OLLYDBG" 和 "WinDbgFrameClass"。可以通过隐藏调试器窗口、更改窗口类以及 hook `FindWindowA`/`FindWindowExA` 函数来绕过此检测。 ### `GetThreadContext` `GetThreadContext` 是一个 Windows API 函数,用于检索当前线程上下文,包括硬件调试寄存器(Dr0、Dr1、Dr2、Dr3)。当调试器设置硬件断点时,相应的调试寄存器会包含断点地址。此技术检查这些寄存器中是否包含非零值,以此指示存在活动的硬件断点。高级调试器更倾向于使用硬件断点,因为它们不会修改进程内存。可以通过在检测之前清除调试寄存器、或 hook `GetThreadContext` 使其返回干净的寄存器来绕过此检测。 ### `NtQuerySystemInformation` `NtQuerySystemInformation` 可用于获取 `DebuggerInformation`。它是一个未公开的结构体,由三个布尔值组成: ``` typedef struct _SYSTEM_KERNEL_DEBUGGER_INFORMATION_EX { BOOLEAN DebuggerAllowed; BOOLEAN DebuggerEnabled; BOOLEAN DebuggerPresent; } SYSTEM_KERNEL_DEBUGGER_INFORMATION_EX, * PSYSTEM_KERNEL_DEBUGGER_INFORMATION_EX; ``` 当系统允许内核调试器时,`DebuggerAllowed` 为 true。当系统附加了调试器时,这通常会变为 true。如果内核在初始化时启用了调试,`KernelDebuggerEnabled` 就会为 true。当系统当前附加了调试器时,`DebuggerPresent` 为 true。 ### `CloseHandle` 当关闭一个无效句柄时,如果附加了调试器,就会引发 `EXCEPTION_INVALID_HANDLE (0xC0000008)` 异常。一旦调试器将控制权交还给进程,就可以通过 `__except` 块捕获此异常。如果不存在调试器,则不会抛出任何异常。此检测默认处于禁用状态,因为它会引发异常。 ### `DbgPrint` 此方法会引发 `DBG_PRINTEXCEPTION_C (0x40010006)` 异常。如果它由程序自身处理,则说明没有附加调试器。否则,显然是附加了调试器。该异常通常由 `DbgPrint` 等函数引发。 ### `NtGlobalFlag` `NtGlobalFlag` 是 PEB 的一个成员,当附加调试器时,会有三个标志位被设置为 1,分别是:`0x10: EAP_ENABLE_TAIL_CHECK`、`0x20: HEAP_ENABLE_FREE_CHECK` 和 `0x40: HEAP_VALIDATE_PARAMETERS`。绕过方法很简单,只需将其设置为不匹配这些标志的任何值即可(设置为 0 就非常好用)。此外,似乎只有在程序通过调试器启动时才会设置这些标志。如果在启动后才附加调试器,则不会设置这些标志。 ### `EnumDeviceDrivers` `EnumDeviceDrivers` 仅用于枚举已加载的各种设备驱动程序。可以通过查找已签名的、与调试器相关的内核驱动程序(例如用于 DBVM 的 `dbk64.sys`)来检测调试器。 ### `CyclesPassed` `CyclesPassed` 使用 MSVC 内部函数检查函数序言和尾声之间的 CPU 周期差值是否异常庞大。如果这种情况发生次数大于或等于 3 次,它就会认为其中被设置了断点。 ### `IsWindowsFunctionBreakpointed` `IsWindowsFunctionBreakpointed` 扫描 WinAPI 函数(主要是那些用于反调试的函数)的前 1 或 2 个字节,以查找软件断点 INT3(0xCC / 0xCD03)以及可用于抛出异常的未定义指令(0x0F0B)。 ## 特别感谢 感谢所有已经为本项目做出贡献的人,同时感谢 [vergiliusproject.com](https://www.vergiliusproject.com/) 记录了未公开的 Windows 结构体, 并感谢 [anti-debug.checkpoint.com](https://anti-debug.checkpoint.com/) 记录了大量的反调试技巧。 ## 许可证 本项目基于 MIT 许可证授权,并附带一些特别说明 - 有关详细信息,请参阅 [LICENSE](LICENSE) 文件。
标签:C++, DOM解析, 云资产清单, 反调试, 数据擦除, 端点可见性, 逆向工程