C# 反调试方法 OutputDebugString 不能正常工作

C# Anti-Debug method OutputDebugString doesn't work right

所以最近我一直在阅读反调试机制和我遇到的一种检查当前进程是否正在调试的流行方法是 OutputDebugString。我已经编写了这段代码,但它并没有完全按预期工作,有人可以解释一下为什么或我做错了什么吗?

private static bool stub_OutputDebugString()
{
    uint ErrCode = 0x12A6;
    Native.SetLastError(ErrCode);
    Native.OutputDebugString("System.Core\n");
    if (Marshal.GetLastWin32Error() == (int)ErrCode)
    {
        //Debugger Detected
        return true;
    }
    else
    {
        //No Debugger Detected
        return false;
    }
}

我的 P/Invoke 签名

[DllImport("kernel32.dll", SetLastError = true)]
public static extern void SetLastError(uint dwErrCode);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern void OutputDebugString(string lpOutputString);

请注意,我阅读了如何不从本机环境调用 GetLastError,因为值可以更改,所以我使用 Marshal.GetLastWin32Error()

代码应该可以工作,但是当我尝试使用 windbg 或任何其他调试器调试应用程序时,最后一个错误没有改变。

为什么不直接使用 IsDebuggerPresent 功能?..我相信它的存在就是为了你想要的目的。

您关于 GetLastError() 的推理(不应该使用它,因为上一个错误可能会由于任何其他 internal/invisible/background 操作而被 .NET Framework 覆盖 ) 也适用于 SetLastError()。简单地说,您不能可靠地调用它并期望它的值在您调用 Marshal.GetLastWin32Error().

之前完好无损。

我们可能会讨论如何规避这个问题...

...该技术(在某种程度上)对本机代码有意义,因为 OutputDebugString() 被广泛使用并且反汇编已编译代码更难(然后你 保护 自己与一些混淆代码)。然而,托管代码很容易反编译,而且仍然很容易阅读和理解,这种简单的混淆不会有帮助,你应该简单地使用 System.Diagnostics.Debugger.IsAttached 属性.


如果你真的真的想检测调试器(即使很容易看到你在做什么,那么它也不会提供太多保护)你必须 使事情变得更复杂,因为您可能希望防止托管调试器或本机调试器。是的,它们是不同的。

除非托管调试器构建在本机调试器之上,否则如果您调用本机 IsDebuggerPresent(),对于托管调试器,您将始终获得 FALSE,其中 Debugger.IsAttached 将 return true。相反的情况也是如此:附加了本机调试器后,您将从 IsDebuggerPresent() 获得 TRUE,但从 Debugger.IsAttached 获得 false。在广阔的世界中,您会遇到所有三种类型的调试器。为了更好地讨论 IsDebuggerPresent(),您可以阅读 Anti-Debugging

你可以检查两者,但你离检测调试器还很远,因为它们只检查本地 managed/native 调试器,但你没有关于远程调试器的信息(除非你也调用 CheckRemoteDebuggerPresent() ) 或不在用户模式下的调试器(除非你也玩 NtQuerySystemInformation)。有一些稍微更强大的技术,但你不能在托管世界中做到这一点(另请参阅 Detecting System Debugger)。

一个可能的解决方案是使用 DebugActiveProcess() 调试您自己的进程,如果您失败(并且这不是权限错误),则附加另一个调试器,此外,直到您保持附加,另一个调试器无法附加。请注意,一个进程不能自我调试(AFAIK),那么它必须从一个子进程完成(它以某种方式与您想要 protect 的主进程通信)。这不是什么新鲜事,基本上与 How to detect if the current process is being run by GDB? 中描述的技术相同,但 Windows 具体。

另请参阅 Debuggers aren't supposed to change behavior and Managed vs. Native debugging APIs 了解有关此主题的更多信息。