首先给出TEB、PEB的基本解释:
TEB是Thread Environment Block的简写,同理PEB对应Process Environment Block,两者分别储存与线程和进程相关的内容,具体作用这里不进行赘述(毕竟主要是针对调试部分)
下面进入正题
TEB结构一般位于fs:[0]的位置,其声明如下:
typedef struct _NT_TEB
{
NT_TIB Tib; // 00h
PVOID EnvironmentPointer; // 1Ch
CLIENT_ID Cid; // 20h
PVOID ActiveRpcInfo; // 28h
PVOID ThreadLocalStoragePointer; // 2Ch
PPEB Peb; // 30h <--注意这里
ULONG LastErrorValue; // 34h
ULONG CountOfOwnedCriticalSections; // 38h
PVOID CsrClientThread; // 3Ch
PVOID Win32ThreadInfo; // 40h
ULONG Win32ClientInfo[0x1F]; // 44h
PVOID WOW32Reserved; // C0h
ULONG CurrentLocale; // C4h
ULONG FpSoftwareStatusRegister; // C8h
PVOID SystemReserved1[0x36]; // CCh
PVOID Spare1; // 1A4h
LONG ExceptionCode; // 1A8h
ULONG SpareBytes1[0x28]; // 1ACh
PVOID SystemReserved2[0xA]; // 1D4h
GDI_TEB_BATCH GdiTebBatch; // 1FCh
ULONG gdiRgn; // 6DCh
ULONG gdiPen; // 6E0h
ULONG gdiBrush; // 6E4h
CLIENT_ID RealClientId; // 6E8h
PVOID GdiCachedProcessHandle; // 6F0h
ULONG GdiClientPID; // 6F4h
ULONG GdiClientTID; // 6F8h
PVOID GdiThreadLocaleInfo; // 6FCh
PVOID UserReserved[5]; // 700h
PVOID glDispatchTable[0x118]; // 714h
ULONG glReserved1[0x1A]; // B74h
PVOID glReserved2; // BDCh
PVOID glSectionInfo; // BE0h
PVOID glSection; // BE4h
PVOID glTable; // BE8h
PVOID glCurrentRC; // BECh
PVOID glContext; // BF0h
NTSTATUS LastStatusValue; // BF4h
UNICODE_STRING StaticUnicodeString; // BF8h
WCHAR StaticUnicodeBuffer[0x105]; // C00h
PVOID DeallocationStack; // E0Ch
PVOID TlsSlots[0x40]; // E10h
LIST_ENTRY TlsLinks; // F10h
PVOID Vdm; // F18h
PVOID ReservedForNtRpc; // F1Ch
PVOID DbgSsReserved[0x2]; // F20h
ULONG HardErrorDisabled; // F28h
PVOID Instrumentation[0x10]; // F2Ch
PVOID WinSockData; // F6Ch
ULONG GdiBatchCount; // F70h
ULONG Spare2; // F74h
ULONG Spare3; // F78h
ULONG Spare4; // F7Ch
PVOID ReservedForOle; // F80h
ULONG WaitingOnLoaderLock; // F84h
PVOID StackCommit; // F88h
PVOID StackCommitMax; // F8Ch
PVOID StackReserve; // F90h
PVOID MessageQueue; // ???
}
TEB结构偏移30h的位置给出了其对应的PEB的地址
再来看看PEB的结构:
typedef struct _PEB
{
UCHAR InheritedAddressSpace; // 00h
UCHAR ReadImageFileExecOptions; // 01h
UCHAR BeingDebugged; // 02h 这里QAQ
UCHAR Spare; // 03h
PVOID Mutant; // 04h
PVOID ImageBaseAddress; // 08h
PPEB_LDR_DATA Ldr; // 0Ch
PRTL_USER_PROCESS_PARAMETERS ProcessParameters; // 10h
PVOID SubSystemData; // 14h
PVOID ProcessHeap; // 18h
PVOID FastPebLock; // 1Ch
PPEBLOCKROUTINE FastPebLockRoutine; // 20h
PPEBLOCKROUTINE FastPebUnlockRoutine; // 24h
ULONG EnvironmentUpdateCount; // 28h
PVOID* KernelCallbackTable; // 2Ch
PVOID EventLogSection; // 30h
PVOID EventLog; // 34h
PPEB_FREE_BLOCK FreeList; // 38h
ULONG TlsExpansionCounter; // 3Ch
PVOID TlsBitmap; // 40h
ULONG TlsBitmapBits[0x2]; // 44h
PVOID ReadOnlySharedMemoryBase; // 4Ch
PVOID ReadOnlySharedMemoryHeap; // 50h
PVOID* ReadOnlyStaticServerData; // 54h
PVOID AnsiCodePageData; // 58h
PVOID OemCodePageData; // 5Ch
PVOID UnicodeCaseTableData; // 60h
ULONG NumberOfProcessors; // 64h
ULONG NtGlobalFlag; // 68h 还有这里!_(:зゝ∠)_
UCHAR Spare2[0x4]; // 6Ch
LARGE_INTEGER CriticalSectionTimeout; // 70h
ULONG HeapSegmentReserve; // 78h
ULONG HeapSegmentCommit; // 7Ch
ULONG HeapDeCommitTotalFreeThreshold; // 80h
ULONG HeapDeCommitFreeBlockThreshold; // 84h
ULONG NumberOfHeaps; // 88h
ULONG MaximumNumberOfHeaps; // 8Ch
PVOID** ProcessHeaps; // 90h
PVOID GdiSharedHandleTable; // 94h
PVOID ProcessStarterHelper; // 98h
PVOID GdiDCAttributeList; // 9Ch
PVOID LoaderLock; // A0h
ULONG OSMajorVersion; // A4h
ULONG OSMinorVersion; // A8h
ULONG OSBuildNumber; // ACh
ULONG OSPlatformId; // B0h
ULONG ImageSubSystem; // B4h
ULONG ImageSubSystemMajorVersion; // B8h
ULONG ImageSubSystemMinorVersion; // C0h
ULONG GdiHandleBuffer[0x22]; // C4h
PVOID ProcessWindowStation; // ???
}
在偏移量为02h的地方有一个叫做BeingDebugged的标志位,很明显这是用来判断程序是否在被调试的;此外在偏移量68h的NtGlobalFlag同样可以用来执行判断
实际检验
Windows API中的IsDebuggerPresent就是使用了PEB中的BeingDebugged
这个标志位在没有调试器的时候将会置0,如果写成汇编代码,可能如下:
mov eax, fs:[30] //取PEB首址
mov al, byte ptr ds:[eax+2] //取BeingDebugged
retn
实际上IsDebuggerPresent的汇编代码也类似这样
MOV EAX,DWORD PTR FS:[30]
MOVZX EAX,BYTE PTR DS:[EAX+2]
RETN
可以看到图中,在未加插件的OD中PEB+02h处确实不为0,表示程序正在被调试
而进行了隐藏插件处理的OD中PEB+02h处显示为0
前文提到的另一个位置68h的NtGlobalFlag的使用方法类似BeingDebugged,注意上两张图中对应位置,如果在进行调试,NtGlobalFlag将会被设置为70h
补充
可以注意到PEB的18h处给出了堆的地址,其中0ch处的HeapFlags和10h处的ForceFlags也可以用来检测调试与否
HeapFlags的正常值为2
ForceFlags的正常值为0