IsDebuggerPresent函数可以用来检测本进程是否处于被调试状态,当然,这种方法的实用性不大。
此函数在winbase.h中声明如下:WINBASEAPI BOOL WINAPI IsDebuggerPresent(void);
如果本进程当前正在被调试则返回1,否则返回0。
直接调用此函数的源程序在用VC6.0编译时会报连接错误,原因是kernel32.lib中找不到_IsDebuggerPresent这个符号。为了使用此函数,你不得不使用LoadLibrary动态加载kernel32.dll,或者使用GetModuleHandle获取kernel32.dll的映像地址,然后使用GetProcAddress取得IsDebuggerPresent的地址。
此函数在winbase.h中声明如下:WINBASEAPI BOOL WINAPI IsDebuggerPresent(void);
如果本进程当前正在被调试则返回1,否则返回0。
直接调用此函数的源程序在用VC6.0编译时会报连接错误,原因是kernel32.lib中找不到_IsDebuggerPresent这个符号。为了使用此函数,你不得不使用LoadLibrary动态加载kernel32.dll,或者使用GetModuleHandle获取kernel32.dll的映像地址,然后使用GetProcAddress取得IsDebuggerPresent的地址。
反汇编kernel32.dll观察IsDebuggerPresent的代码可以看到:----
7C813093 64:A1 18000000 mov eax, dword ptr fs:[18] |kernel32.IsDebuggerPresent
7C813099 8B40 30 mov eax, dword ptr [eax+30]
7C81309C 0FB640 02 movzx eax, byte ptr [eax+2]
7C8130A0 C3 retn
7C813099 8B40 30 mov eax, dword ptr [eax+30]
7C81309C 0FB640 02 movzx eax, byte ptr [eax+2]
7C8130A0 C3 retn
----
这里牵涉到两个重要的结构:
1 TEB (Thread Enviroment Block)
这里牵涉到两个重要的结构:
1 TEB (Thread Enviroment Block)
TEB at 7FFDE000 ExceptionList: 12f830 \ 0x0 Stack Base: 130000 | 0x4 Stack Limit: 126000 | 0x8 SubSystemTib: 0 | 0xC 这里其实是 NT_TIB 的一个结构 FiberData: 1e00 | 0x10 ArbitraryUser: 0 | 0x14 Self: 7ffde000 / 0x18 EnvironmentPtr: 0 0x1C ClientId: 4cc.4c8 0x20 Real ClientId: 4cc.4c8 0x24 RpcHandle: 0 0x28 Tls Storage: 0 0x2C PEB Address: 7ffdf000 0x30 这里指向 PEB 表,即进程环境块 LastErrorValue: 0 LastStatusValue: 8000001a Count Owned Locks:0 HardErrorsMode: 0
2 PEB (Process Enviroment Block)
struct _PEB (sizeof=488) +000 byte InheritedAddressSpace +001 byte ReadImageFileExecOptions +002 byte BeingDebugged //Debug运行标志 +003 byte SpareBool +004 void *Mutant +008 void *ImageBaseAddress //这里就是程序加载的基地址了 +00c struct _PEB_LDR_DATA *Ldr +010 struct _RTL_USER_PROCESS_PARAMETERS *ProcessParameters +014 void *SubSystemData +018 void *ProcessHeap +01c void *FastPebLock +020 void *FastPebLockRoutine +024 void *FastPebUnlockRoutine +028 uint32 EnvironmentUpdateCount +02c void *KernelCallbackTable +030 uint32 SystemReserved[2] +038 struct _PEB_FREE_BLOCK *FreeList +03c uint32 TlsExpansionCounter +040 void *TlsBitmap +044 uint32 TlsBitmapBits[2] +04c void *ReadOnlySharedMemoryBase +050 void *ReadOnlySharedMemoryHeap +054 void **ReadOnlyStaticServerData +058 void *AnsiCodePageData +05c void *OemCodePageData +060 void *UnicodeCaseTableData +064 uint32 NumberOfProcessors +068 uint32 NtGlobalFlag +070 union _LARGE_INTEGER CriticalSectionTimeout +070 uint32 LowPart +074 int32 HighPart +070 struct __unnamed3 u +070 uint32 LowPart +074 int32 HighPart +070 int64 QuadPart +078 uint32 HeapSegmentReserve +07c uint32 HeapSegmentCommit +080 uint32 HeapDeCommitTotalFreeThreshold +084 uint32 HeapDeCommitFreeBlockThreshold +088 uint32 NumberOfHeaps +08c uint32 MaximumNumberOfHeaps +090 void **ProcessHeaps +094 void *GdiSharedHandleTable +098 void *ProcessStarterHelper +09c uint32 GdiDCAttributeList +0a0 void *LoaderLock +0a4 uint32 OSMajorVersion +0a8 uint32 OSMinorVersion +0ac uint16 OSBuildNumber +0ae uint16 OSCSDVersion +0b0 uint32 OSPlatformId +0b4 uint32 ImageSubsystem +0b8 uint32 ImageSubsystemMajorVersion +0bc uint32 ImageSubsystemMinorVersion +0c0 uint32 ImageProcessAffinityMask +0c4 uint32 GdiHandleBuffer[34] +14c function *PostProcessInitRoutine +150 void *TlsExpansionBitmap +154 uint32 TlsExpansionBitmapBits[32] +1d4 uint32 SessionId +1d8 void *AppCompatInfo +1dc struct _UNICODE_STRING CSDVersion +1dc uint16 Length +1de uint16 MaximumLength +1e0 uint16 *Buffer
如何获得本线程的TEB和本进程的PEB呢?使用FS寄存器就可以,在Windows NT中,FS寄存在Ring3模式下指向TEB的首部,因此FS:[18]就是TEB的地址,FS:[30]就是PEB的地址,PEB+2这个BYTE为1时表示进程正在被调试,为0则表示进程没有被调试。至此,IsDebuggerPresent的原理已经揭开。
不能免俗的贴一份源代码:
// isdebugged.c This program demonstrates how to use IsDebuggerPresent.
// Tested on Windows XP and VC6.0
// 龙第九子 2008/05/17
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
BOOL (*IsDebuggerPresent)();
int counter = 1000;
int main()
{
HMODULE krn = GetModuleHandle("kernel32.dll");
IsDebuggerPresent = (BOOL(*)())GetProcAddress(krn, "IsDebuggerPresent");
if(IsDebuggerPresent == NULL)
{
fprintf(stderr, "Cannot get procedure address!\n");
exit(1);
}
while(counter--)
{
printf("%d\t", counter);
if(IsDebuggerPresent())
printf("Being debugged...\n");
else
printf("Not being debugged...\n");
Sleep(1000);
}
return 0;
}
// Tested on Windows XP and VC6.0
// 龙第九子 2008/05/17
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
BOOL (*IsDebuggerPresent)();
int counter = 1000;
int main()
{
HMODULE krn = GetModuleHandle("kernel32.dll");
IsDebuggerPresent = (BOOL(*)())GetProcAddress(krn, "IsDebuggerPresent");
if(IsDebuggerPresent == NULL)
{
fprintf(stderr, "Cannot get procedure address!\n");
exit(1);
}
while(counter--)
{
printf("%d\t", counter);
if(IsDebuggerPresent())
printf("Being debugged...\n");
else
printf("Not being debugged...\n");
Sleep(1000);
}
return 0;
}
参考资料:
1 看雪论坛<<笑解 API 函数 -- API 绝密档案系列之一>> 作者gzgzlxg
地址: http://bbs.pediy.com/showthread.php?t=21959
2 利用FS寄存器获取KERNEL32.DLL基址算法的证明
地址: http://blog.csdn.net/int2e/archive/2008/01/09/2032732.aspx
3 Windows中FS段寄存器
地址: http://hi.baidu.com/happyman/blog/item/9483b545a9d21f22cffca3ac.html