所以最近我一直在阅读反调试机制和我遇到的流行方法,以检查当前进程是否正在调试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或任何其他调试器调试应用程序时,最后一个错误不会改变.
您的推理GetLastError()
(由于任何其他内部/不可见/后台操作,因为上一个错误可能被.NET Framework覆盖,因此不应该使用)也适用于SetLastError()
.简单地说,你无法可靠地调用它,并期望它的值保持不变直到你打电话Marshal.GetLastWin32Error()
.
我们可以讨论如何绕过这个问题但是......
...该技术(以某种方式)对本机代码有意义,因为它OutputDebugString()
被广泛使用并且反汇编编译代码更难(然后用一些混淆代码来保护自己).但是托管代码很容易反编译,而且仍然易于阅读和理解,这种简单的混淆无济于事,你只需要使用属性.System.Diagnostics.Debugger.IsAttached
除非托管调试是建立在原生一个的顶部,如果你调用本地IsDebuggerPresent()
你总是会得到FALSE
一个托管调试,其中Debugger.IsAttached
将返回true
.另外相反的情况是真实的:与本机调试连接你会得到TRUE
从IsDebuggerPresent()
而是false
从Debugger.IsAttached
.在大世界中,您将满足所有三种类型的调试器.有关您的更好讨论,IsDebuggerPresent()
请阅读反调试.
您可以检查两者,但是您仍远未检测到调试器,因为它们只检查本地托管/本机调试器,但您没有关于远程调试器的信息(除非您也调用CheckRemoteDebuggerPresent()
)或不在用户模式下的调试器(除非你也玩NtQuerySystemInformation
).还有一些稍微更强大的技术,但你不能做到这一点在管理世界(另见检测系统调试器).
一种可能的解决方案是使用调试自己的进程DebugActiveProcess()
,它失败(并且它不是权限错误)然后附加另一个调试器,而且直到你保持连接另一个调试器无法附加.请注意,进程无法自行调试(AFAIK),然后必须从子进程(以某种方式与要保护的主进程通信)完成.这并不是什么新鲜事,基本上与如何检测当前进程是否由GDB运行中描述的技术相同?但Windows具体.
阅读另请参阅调试器不应更改行为和Managed vs. Native调试API以获取有关此主题的更多信息.