一个比较重要的 Windows 结构

该结构在不同版本的 Windows 下不同!具体可以参考 https://www.vergiliusproject.com/

参考链接

https://bbs.kanxue.com/thread-266678.htm
https://blog.csdn.net/CSNN2019/article/details/113113347
https://blog.csdn.net/simon798/article/details/107128069
https://blog.csdn.net/zhoujiaxq/article/details/23169587

定义

  Windows XP SP3

typedef struct _PEB { // Size: 0x1D8
    000h    UCHAR           InheritedAddressSpace;
    001h    UCHAR           ReadImageFileExecOptions;
    002h    UCHAR           BeingDebugged;              //Debug运行标志
    003h    UCHAR           SpareBool;
    004h    HANDLE          Mutant;
    008h    HINSTANCE       ImageBaseAddress;           //程序加载的基地址
    00Ch    struct _PEB_LDR_DATA    *Ldr                //Ptr32 _PEB_LDR_DATA
    010h    struct _RTL_USER_PROCESS_PARAMETERS  *ProcessParameters;
    014h    ULONG           SubSystemData;
    018h    HANDLE          DefaultHeap;
    01Ch    KSPIN_LOCK      FastPebLock;
    020h    ULONG           FastPebLockRoutine;
    024h    ULONG           FastPebUnlockRoutine;
    028h    ULONG           EnvironmentUpdateCount;
    02Ch    ULONG           KernelCallbackTable;
    030h    LARGE_INTEGER   SystemReserved;
    038h    struct _PEB_FREE_BLOCK  *FreeList
    03Ch    ULONG           TlsExpansionCounter;
    040h    ULONG           TlsBitmap;
    044h    LARGE_INTEGER   TlsBitmapBits;
    04Ch    ULONG           ReadOnlySharedMemoryBase;
    050h    ULONG           ReadOnlySharedMemoryHeap;
    054h    ULONG           ReadOnlyStaticServerData;
    058h    ULONG           AnsiCodePageData;
    05Ch    ULONG           OemCodePageData;
    060h    ULONG           UnicodeCaseTableData;
    064h    ULONG           NumberOfProcessors;
    068h    LARGE_INTEGER   NtGlobalFlag;               // Address of a local copy
    070h    LARGE_INTEGER   CriticalSectionTimeout;
    078h    ULONG           HeapSegmentReserve;
    07Ch    ULONG           HeapSegmentCommit;
    080h    ULONG           HeapDeCommitTotalFreeThreshold;
    084h    ULONG           HeapDeCommitFreeBlockThreshold;
    088h    ULONG           NumberOfHeaps;
    08Ch    ULONG           MaximumNumberOfHeaps;
    090h    ULONG           ProcessHeaps;
    094h    ULONG           GdiSharedHandleTable;
    098h    ULONG           ProcessStarterHelper;
    09Ch    ULONG           GdiDCAttributeList;
    0A0h    KSPIN_LOCK      LoaderLock;
    0A4h    ULONG           OSMajorVersion;
    0A8h    ULONG           OSMinorVersion;
    0ACh    USHORT          OSBuildNumber;
    0AEh    USHORT          OSCSDVersion;
    0B0h    ULONG           OSPlatformId;
    0B4h    ULONG           ImageSubsystem;
    0B8h    ULONG           ImageSubsystemMajorVersion;
    0BCh    ULONG           ImageSubsystemMinorVersion;
    0C0h    ULONG           ImageProcessAffinityMask;
    0C4h    ULONG           GdiHandleBuffer[0x22];
    14Ch    ULONG           PostProcessInitRoutine;
    150h    ULONG           TlsExpansionBitmap;
    154h    UCHAR           TlsExpansionBitmapBits[0x80];
    1D4h    ULONG           SessionId;
    1d8h AppCompatFlags   : _ULARGE_INTEGER
    1e0h AppCompatFlagsUser : _ULARGE_INTEGER
    1e8h pShimData        : Ptr32 Void
    1ech AppCompatInfo    : Ptr32 Void
    1f0h CSDVersion       : _UNICODE_STRING
    1f8h ActivationContextData : Ptr32 Void
    1fch ProcessAssemblyStorageMap :Ptr32 Void
    200h SystemDefaultActivationContextData : Ptr32 Void
    204h SystemAssemblyStorageMap : Ptr32 Void
    208h MinimumStackCommit : Uint4B
} PEB, *PPEB;

  Win7

ntdll!_PEB
   +0x000 InheritedAddressSpace : UChar
   +0x001 ReadImageFileExecOptions : UChar
   +0x002 BeingDebugged    : UChar
   +0x003 BitField         : UChar
   +0x003 ImageUsesLargePages : Pos 0, 1 Bit
   +0x003 IsProtectedProcess : Pos 1, 1 Bit
   +0x003 IsLegacyProcess  : Pos 2, 1 Bit
   +0x003 IsImageDynamicallyRelocated : Pos 3, 1 Bit
   +0x003 SkipPatchingUser32Forwarders : Pos 4, 1 Bit
   +0x003 SpareBits        : Pos 5, 3 Bits
   +0x004 Mutant           : Ptr32 Void
   +0x008 ImageBaseAddress : Ptr32 Void
   +0x00c Ldr              : Ptr32 _PEB_LDR_DATA
   +0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
   +0x014 SubSystemData    : Ptr32 Void
   +0x018 ProcessHeap      : Ptr32 Void
   +0x01c FastPebLock      : Ptr32 _RTL_CRITICAL_SECTION
   +0x020 AtlThunkSListPtr : Ptr32 Void
   +0x024 IFEOKey          : Ptr32 Void
   +0x028 CrossProcessFlags : Uint4B
   +0x028 ProcessInJob     : Pos 0, 1 Bit
   +0x028 ProcessInitializing : Pos 1, 1 Bit
   +0x028 ProcessUsingVEH  : Pos 2, 1 Bit
   +0x028 ProcessUsingVCH  : Pos 3, 1 Bit
   +0x028 ProcessUsingFTH  : Pos 4, 1 Bit
   +0x028 ReservedBits0    : Pos 5, 27 Bits
   +0x02c KernelCallbackTable : Ptr32 Void
   +0x02c UserSharedInfoPtr : Ptr32 Void
   +0x030 SystemReserved   : [1] Uint4B
   +0x034 AtlThunkSListPtr32 : Uint4B
   +0x038 ApiSetMap        : Ptr32 Void
   +0x03c TlsExpansionCounter : Uint4B
   +0x040 TlsBitmap        : Ptr32 Void
   +0x044 TlsBitmapBits    : [2] Uint4B
   +0x04c ReadOnlySharedMemoryBase : Ptr32 Void
   +0x050 HotpatchInformation : Ptr32 Void
   +0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
   +0x058 AnsiCodePageData : Ptr32 Void
   +0x05c OemCodePageData  : Ptr32 Void
   +0x060 UnicodeCaseTableData : Ptr32 Void
   +0x064 NumberOfProcessors : Uint4B
   +0x068 NtGlobalFlag     : Uint4B
   +0x070 CriticalSectionTimeout : _LARGE_INTEGER
   +0x078 HeapSegmentReserve : Uint4B
   +0x07c HeapSegmentCommit : Uint4B
   +0x080 HeapDeCommitTotalFreeThreshold : Uint4B
   +0x084 HeapDeCommitFreeBlockThreshold : Uint4B
   +0x088 NumberOfHeaps    : Uint4B
   +0x08c MaximumNumberOfHeaps : Uint4B
   +0x090 ProcessHeaps     : Ptr32 Ptr32 Void
   +0x094 GdiSharedHandleTable : Ptr32 Void
   +0x098 ProcessStarterHelper : Ptr32 Void
   +0x09c GdiDCAttributeList : Uint4B
   +0x0a0 LoaderLock       : Ptr32 _RTL_CRITICAL_SECTION
   +0x0a4 OSMajorVersion   : Uint4B
   +0x0a8 OSMinorVersion   : Uint4B
   +0x0ac OSBuildNumber    : Uint2B
   +0x0ae OSCSDVersion     : Uint2B
   +0x0b0 OSPlatformId     : Uint4B
   +0x0b4 ImageSubsystem   : Uint4B
   +0x0b8 ImageSubsystemMajorVersion : Uint4B
   +0x0bc ImageSubsystemMinorVersion : Uint4B
   +0x0c0 ActiveProcessAffinityMask : Uint4B
   +0x0c4 GdiHandleBuffer  : [34] Uint4B
   +0x14c PostProcessInitRoutine : Ptr32     void 
   +0x150 TlsExpansionBitmap : Ptr32 Void
   +0x154 TlsExpansionBitmapBits : [32] Uint4B
   +0x1d4 SessionId        : Uint4B
   +0x1d8 AppCompatFlags   : _ULARGE_INTEGER
   +0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER
   +0x1e8 pShimData        : Ptr32 Void
   +0x1ec AppCompatInfo    : Ptr32 Void
   +0x1f0 CSDVersion       : _UNICODE_STRING
   +0x1f8 ActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA
   +0x1fc ProcessAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP
   +0x200 SystemDefaultActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA
   +0x204 SystemAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP
   +0x208 MinimumStackCommit : Uint4B
   +0x20c FlsCallback      : Ptr32 _FLS_CALLBACK_INFO
   +0x210 FlsListHead      : _LIST_ENTRY
   +0x218 FlsBitmap        : Ptr32 Void
   +0x21c FlsBitmapBits    : [4] Uint4B
   +0x22c FlsHighIndex     : Uint4B
   +0x230 WerRegistrationData : Ptr32 Void
   +0x234 WerShipAssertPtr : Ptr32 Void
   +0x238 pContextData     : Ptr32 Void
   +0x23c pImageHeaderHash : Ptr32 Void
   +0x240 TracingFlags     : Uint4B
   +0x240 HeapTracingEnabled : Pos 0, 1 Bit
   +0x240 CritSecTracingEnabled : Pos 1, 1 Bit
   +0x240 SpareTracingBits : Pos 2, 30 Bits

  Win7 下的结构更大

PEB 的重要结构

 +0x002 BeingDebugged    : UChar
 +0x008 ImageBaseAddress : Ptr32 Void
 +0x00c Ldr              : Ptr32 _PEB_LDR_DATA
 +0x018 ProcessHeap      : Ptr32 Void
 +0x068 NtGlobalFlag     : Uint4B

BeingDebugged

   kernel32.dll 中有一个函数如下:

BOOL WINAPI IsDebuggerPresent()

   该 API 用来判断当前进程是否处于调试状态,并返回判断结果。而该 API 就是通过检测 PEB.BeingDebugged 成员来确定是否正在调试程序(是,则返回 1;否,则返回 0)。Win7 中,IsDebuggerPresent()是在 Kernelbase.dll 中实现的。而在 Windows XP 及以前版本的操作系统中,它是在 kernel32.dll 中。Win10 中也是在 Kernel32.dll 中。

ImageBaseAddress

   ImageBaseAddress 成员用来表示进程的 ImageBase。Windows 用 GetModuleHandle 函数获取 ImageBase:

HMODULE WINAPI GetModuleHandle(
__in_opt     LPCTSTR        lpModuleName
)

   函数返回值被存在 EAX 中。

Ldr

   Ldr 成员是指向了 _PEB_LDR_DATA 结构体指针。如下:

ypedef struct _PEB_LDR_DATA
{
 ULONG         Length;                             // 00h
 BOOLEAN       Initialized;                        // 04h
 PVOID         SsHandle;                           // 08h
 LIST_ENTRY    InLoadOrderModuleList;              // 0ch 模块链表,以加载顺序排序
 LIST_ENTRY    InMemoryOrderModuleList;            // 14h 模块链表,以内存位置排序
 LIST_ENTRY    InInitializationOrderModuleList;    // 1ch 模块链表,以初始化顺序排序
 EntryInProgress  //Ptr32 Void
 ShutdownInProgress  //Uchar
 ShutdownThreadId //Ptr32 Void
}
    PEB_LDR_DATA,
    *PPEB_LDR_DATA;                                 // 24h

   当模块(DLL)加载到进程后,通过 ldr 成员可以直接获得该模块的加载基地址。

   _PEB_LDR_DATA 结构体成员中有 3 个 LIST_ENTRY 类型的成员 (InLoadOrderModuleList;InMemoryOrderModuleList; InInitializationOrderModuleList; ),LIST_ENTRY 结构体的定义如下所示:

typedef struct _LIST_ENTRY{
    struct _LIST_ENTRY *Flink;      // 指向下一个节点
    struct _LIST_ENTRY *Blink;      // 
}LIST_ENTRY,*LIST_ENTRY

   从上面可看出,_LIST_ENTRY 结构体提供了双向链表机制。而链表中保存了 _LDR_DATA_TABLE_ENTRY 结构体的信息。

//0x78 bytes (sizeof)  Win7 下
struct _LDR_DATA_TABLE_ENTRY
{
    struct _LIST_ENTRY InLoadOrderLinks;                                    //0x0
    struct _LIST_ENTRY InMemoryOrderLinks;                                  //0x8
    struct _LIST_ENTRY InInitializationOrderLinks;                          //0x10
    VOID* DllBase;                                                          //0x18
    VOID* EntryPoint;                                                       //0x1c
    ULONG SizeOfImage;                                                      //0x20
    struct _UNICODE_STRING FullDllName;                                     //0x24
    struct _UNICODE_STRING BaseDllName;                                     //0x2c
    ULONG Flags;                                                            //0x34
    USHORT LoadCount;                                                       //0x38
    USHORT TlsIndex;                                                        //0x3a
    union
    {
        struct _LIST_ENTRY HashLinks;                                       //0x3c
        struct
        {
            VOID* SectionPointer;                                           //0x3c
            ULONG CheckSum;                                                 //0x40
        };
    };
    union
    {
        ULONG TimeDateStamp;                                                //0x44
        VOID* LoadedImports;                                                //0x44
    };
    struct _ACTIVATION_CONTEXT* EntryPointActivationContext;                //0x48
    VOID* PatchInformation;                                                 //0x4c
    struct _LIST_ENTRY ForwarderLinks;                                      //0x50
    struct _LIST_ENTRY ServiceTagLinks;                                     //0x58
    struct _LIST_ENTRY StaticLinks;                                         //0x60
    VOID* ContextInformation;                                               //0x68
    ULONG OriginalBase;                                                     //0x6c
    union _LARGE_INTEGER LoadTime;                                          //0x70
}; 

   每个加载到进程中的 DLL 模块都有与之对应的 _LDR_DATA_TABLE_EBTRY 结构体,这些结构体相互链接,最终形成 _LIST_ENTRY 双向链表。
   需要注意的是,_PEB_LDR_DATA 结构体中存在 3 种链表。也就说,存在多个 _LDR_DATA_TABLE_ENTRY 结构体,有三种链接方式可以把它们链接起来。

   借用下前人的图:

前人的图

ProcessHeap

   x64 中 ProcessHeap 在 PEB 的偏移是 0x30.
   x86 中 ProcessHeap 在 PEB 的偏移是 0x18.
   ProcessHeap 是指向 HEAP 结构体的指针,在 HEAP 结构体中的两个成员 Flags 和 Force Flags 。 进程运行正常时,Heap.Flags 的成员值为0x2,HEAP.ForceFlags 成员的值为0x0

   HeapFlags
   对于 x64 系统,vista 以上版本的 HeapFlags 位于 ProcessHeap + 0x70,剩余低版本的 windows 中的 HeapFlags 位于 ProcessHeap+ 0x14。
   对于 x86 系统 vista 以上版本的 HeapFlags 位于 ProcessHeap + 0x40,剩余低版本的 windows 中的 HeapFlags 位于 ProcessHeap + 0x0c。
   如果 HeapFlags 的值 大于 2 说明程序处于调试状态。示例代码如下:

// 环境 - win10 x64
ULONG64 ul_processHeap = (ULONG64)(__readgsqword(0x60) + 0x30);
ULONG64 ul_heapFlags = (ULONG64)(*ul_processHeap + 0x70);
if(*ul_heapFlags > 2){
    cout << "发现调试器" << endl;
}
else{
    cout << "没有调试器" << endl;
}

   HeapForceFlags
   对于 x64 系统,vista 以上版本的 HeapForceFlags位于 ProcessHeap+ 0x74,剩余低版本的 windows 中的 HeapForceFlags 位于 ProcessHeap+ 0x18。

   对于 x86 系统 vista 以上版本的 HeapForceFlags 位于 ProcessHeap + 0x44,剩余低版本的 windows 中的 HeapForceFlags 位于 ProcessHeap + 0x10。
   如果 HeapForceFlags 的值 大于 0 则说明处于调试状态。

// 环境 - win10 x64
ULONG64 ul_processHeap = (ULONG64)(__readgsqword(0x60) + 0x30);
ULONG64 ul_heapForceFlags = (ULONG64)(*ul_processHeap + 0x74);
if(*ul_heapForceFlags > 0){
    cout << "发现调试器" << endl;
}
else{
    cout << "没有调试器" << endl;
}

NtGlobalFlag

   通常程序没有被调试时,NtGlobalFlag成员值为0,如果进程被调试这个成员通常值为0x70(代表下述标志被设置):
   FLG_HEAP_ENABLE_TAIL_CHECK(0X10)
   FLG_HEAP_ENABLE_FREE_CHECK(0X20)
   FLG_HEAP_VALIDATE_PARAMETERS(0X40)