所以最近我一直在阅读反调试机制,并且我遇到的一种流行方法是检查当前进程是否正在调试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或任何其他调试器调试应用程序时,最后一个错误没有改变。
答案 0 :(得分:5)
关于GetLastError()
的推理(由于任何其他内部/不可见/后台操作,因为上一个错误可能会被.NET Framework 覆盖,因此不应该使用)适用到SetLastError()
。在您致电Marshal.GetLastWin32Error()
之前,您无法可靠地调用它并期望其值保持不变。
我们可以讨论如何绕过这个问题......
...这种技术(以某种方式)对本机代码有意义,因为OutputDebugString()
被广泛使用并且反汇编编译代码更难(然后你用一些混淆代码保护自己)。但是,托管代码很容易反编译,并且仍然易于阅读和理解,这种简单的混淆无济于事,您只需使用System.Diagnostics.Debugger.IsAttached
属性。
<小时/> 如果你真的想要检测一个调试器(即使很容易看到你正在做什么,那么它也不会提供太多保护)你必须 使事情变得更复杂,因为您可能希望防止托管调试器或本机调试器。是的,他们是不同的。
如果您调用本机IsDebuggerPresent()
,则除非托管调试器构建在本机调试器之上,否则对于FALSE
将返回Debugger.IsAttached
的托管调试器,您将始终获得true
。另一种情况也是如此:如果附加了本机调试器,您将从TRUE
获得IsDebuggerPresent()
,但从false
获得Debugger.IsAttached
。在大世界中,您将满足所有三种类型的调试器。有关IsDebuggerPresent()
的更好讨论,您可以阅读Anti-Debugging。
您可以检查两者,但是您仍远未检测到调试器,因为它们只检查本地托管/本机调试器,但您没有关于远程调试器的信息(除非您还调用CheckRemoteDebuggerPresent()
)或调试器不要处于用户模式(除非你也使用NtQuerySystemInformation
)。有一些稍微强一些的技术,但你不能在托管世界中做到这一点(另见Detecting System Debugger)。
一种可能的解决方案是使用DebugActiveProcess()
调试您自己的进程,它失败(并且它不是权限错误)然后附加另一个调试器,而且直到您保持连接,另一个调试器无法附加。请注意,进程无法自行调试(AFAIK),然后必须从子进程(以某种方式与您想要保护的主进程通信)完成。这并不是什么新鲜事,基本上与How to detect if the current process is being run by GDB?中描述的技术相同,但特定于Windows。
阅读另请参阅Debuggers aren't supposed to change behavior和Managed vs. Native debugging APIs,了解有关此主题的更多信息。
答案 1 :(得分:0)
为什么不使用IsDebuggerPresent函数?我认为存在是为了你想要的目的。