关于IsDebuggerPresent
关于IsDebuggerPresent()函数的文章已经数不胜数了,随便一搜就能找出一堆来,但是我还是准备写一份放在这里,算是留个备份吧。
微软给我们提供了一个API函数用来检测当前程序是否正在被调试,这就是可怜的IsDebuggerPresent() ,这个函数的存在似乎只是为了证明在Windows的世界里确实存在一个最杯具的API函数……这个函数的实现很简单,直接windbg查看一下:
0:000> u kernel32!IsDebuggerPresent
kernel32!IsDebuggerPresent:
7c813133 64a118000000 mov eax,dword ptr fs:[00000018h]
7c813139 8b4030 mov eax,dword ptr [eax+30h]
7c81313c 0fb64002 movzx eax,byte ptr [eax+2]
7c813140 c3 ret
fs:[0]是本线程TEB的头部,那么fs:[18h]呢?继续让windbg告诉我们:
0:000> dt -b ntdll!_TEB
+0x000 NtTib : _NT_TIB
+0x000 ExceptionList : Ptr32
+0x004 StackBase : Ptr32
+0x008 StackLimit : Ptr32
+0x00c SubSystemTib : Ptr32
+0x010 FiberData : Ptr32
+0x010 Version : Uint4B
+0x014 ArbitraryUserPointer : Ptr32
+0x018 Self : Ptr32
+0x01c EnvironmentPointer : Ptr32
+0x020 ClientId : _CLIENT_ID
+0x000 UniqueProcess : Ptr32
+0x004 UniqueThread : Ptr32
+0x028 ActiveRpcHandle : Ptr32
+0x02c ThreadLocalStoragePointer : Ptr32
+0x030 ProcessEnvironmentBlock : Ptr32
+0x034 LastErrorValue : Uint4B下面的省略掉了
很显然,fs:[18h]是一个指向自身的指针,那么[eax+30h]就是获得PEB的地址,再来看PEB的结构:
0:000> dt -b ntdll!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32
+0x008 ImageBaseAddress : Ptr32
+0x00c Ldr : Ptr32
+0x010 ProcessParameters : Ptr32
下面的省略掉了
那个movzx eax,byte ptr [eax+2]就取出BeingDebugged中的值,然后就直接返回了。
现在IsDebuggerPresent()就兴高采烈地返回了,我们自然可以事先偷偷地改掉BeingDebugged这个值,让它将杯具进行到底。
最后贴一个垃圾代码充字数吧,这是一个使用IsDebuggerPresent()检测调试器的例子。
#include <windows.h>
#include <tchar.h>
int main(int argc,char *argv[])
{
if (IsDebuggerPresent())
{
MessageBox(GetDesktopWindow(),_T("发现调试器!!"),_T("IsDebuggerPresent"),MB_OK);
}
else
{
MessageBox(GetDesktopWindow(),_T("没有发现调试器。"),_T("IsDebuggerPresent"),MB_OK);
}
return 0;
}