6a. Why does PEB and TEB matter?

Resources:

https://github.com/Faran-17/Windows-Internals/blob/main/Processes and Jobs/Processes/PEB - Part 1.md

https://github.com/Faran-17/Windows-Internals/blob/main/Processes and Jobs/Processes/PEB - Part 1.md

https://bowtiedcrawfish.substack.com/p/understanding-the-peb-and-teb

https://www.travismathison.com/posts/PEB_TEB_TIB-Structure-Offsets/

https://metehan-bulut.medium.com/understanding-the-process-environment-block-peb-for-malware-analysis-26315453793f

Why matter?

Thread Environment Block (TEB) contains a pointer to PEB. PEB stores the base address of ntdll.dll. By access PEB, ntdll.dll dynamic memory address, System Service Number (SSN) can be located.

Example:
Run notepad.
> WinDbgx.exe -pn notepad.exe
From Windbg, type !teb and location PEB Address

TEB at 00000076ce2ed000
    ExceptionList:        0000000000000000
    StackBase:            00000076cfe00000
    StackLimit:           00000076cfdfc000
    SubSystemTib:         0000000000000000
    FiberData:            0000000000001e00
    ArbitraryUserPointer: 0000000000000000
    Self:                 00000076ce2ed000
    EnvironmentPointer:   0000000000000000
    ClientId:             0000000000004148 . 00000000000036e8
    RpcHandle:            0000000000000000
    Tls Storage:          0000000000000000
    PEB Address:          00000076ce2b6000
    LastErrorValue:       0
    LastStatusValue:      0
    Count Owned Locks:    0
    HardErrorMode:        0

From Windbg, type !peb, we can see that ntdll.dll is dynamically located at 7ffd83120000

0:026> !peb
PEB at 00000076ce2b6000
    InheritedAddressSpace:    No
    ReadImageFileExecOptions: No
    BeingDebugged:            Yes
    ImageBaseAddress:         00007ff6ee2f0000
    NtGlobalFlag:             0
    NtGlobalFlag2:            0
    Ldr                       00007ffd832f08a0
    Ldr.Initialized:          Yes
    Ldr.InInitializationOrderModuleList: 00000194fe1059b0 . 00000194887280b0
    Ldr.InLoadOrderModuleList:           00000194fe105b40 . 0000019488728090
    Ldr.InMemoryOrderModuleList:         00000194fe105b50 . 00000194887280a0
                       Base TimeStamp                     Module
            7ff6ee2f0000 67ab5982 Feb 11 09:06:58 2025 C:\Program Files\WindowsApps\Microsoft.WindowsNotepad_11.2412.16.0_x64__8wekyb3d8bbwe\Notepad\Notepad.exe
            7ffd83120000 facafff0 May 02 23:56:32 2103 C:\WINDOWS\SYSTEM32\ntdll.dll

What is the PEB (Process Environment Block)?

The Process Environment Block (PEB) is a user-mode structure in Windows that stores information about the currently running process. It contains process-level metadata that helps manage execution, including module lists, process parameters, heap information, and OS version details.

It is a data structure stored in process memory and exists in every running process


1. Where is the PEB Located?


2. Key Fields in the PEB

Field Name Description
OSMajorVersion Major OS version (e.g., 10 for Windows 10, 6 for Windows 7).
OSMinorVersion Minor OS version (e.g., 1 for Windows 7, 0 for Windows 10).
OSBuildNumber Windows build number (e.g., 19044 for Windows 10 21H2).
BeingDebugged Set to 1 if the process is being debugged. Used for anti-debugging techniques.
Ldr Pointer to PEB_LDR_DATA, which contains the loaded module list (DLLs).
ProcessParameters Pointer to RTL_USER_PROCESS_PARAMETERS, which stores command-line arguments, environment variables, etc.

3. Example: Reading OS Version from the PEB (C++)

This example retrieves the OS major, minor, and build numbers from the PEB.

#include <windows.h>
#include <iostream>

typedef struct _PEB {
    BYTE Reserved1[4];
    BYTE BeingDebugged;
    BYTE Reserved2[23];
    PVOID Ldr;
    PVOID ProcessParameters;
    BYTE Reserved3[112];
    ULONG OSMajorVersion;
    ULONG OSMinorVersion;
    ULONG OSBuildNumber;
} PEB, *PPEB;

int main() {
    PPEB peb = (PPEB)__readgsqword(0x60); // Get PEB address in x64

    std::cout << "OS Major Version: " << peb->OSMajorVersion << std::endl;
    std::cout << "OS Minor Version: " << peb->OSMinorVersion << std::endl;
    std::cout << "OS Build Number: " << peb->OSBuildNumber << std::endl;

    return 0;
}

How It Works

  1. Accesses the PEB structure using __readgsqword(0x60), which retrieves the PEB address from GS:[0x60] in x64.
  2. Reads OS version details (OSMajorVersion, OSMinorVersion, OSBuildNumber).
  3. Prints the Windows version information.

4. Anti-Debugging with PEB

Many malware and security tools check PEB->BeingDebugged to detect debuggers.

Example: Checking If Being Debugged

#include <windows.h>
#include <iostream>

bool IsDebuggerPresentPEB() {
    return *(BYTE*)(__readgsqword(0x60) + 2); // BeingDebugged is at offset 0x02 in PEB
}

int main() {
    if (IsDebuggerPresentPEB()) {
        std::cout << "Debugger detected!" << std::endl;
    } else {
        std::cout << "No debugger detected." << std::endl;
    }
    return 0;
}

5. How to View PEB in WinDbg

You can inspect the PEB structure using WinDbg or x64dbg.

Using WinDbg

  1. Attach to a process:
    windbg -pn target.exe
    
  2. Display the PEB:
    !peb
    
    This will show details like the OS version, loaded DLLs, and process parameters.

Using x64dbg

  1. Attach to a process.
  2. Open the memory viewer.
  3. Navigate to:
    • x86: fs:[0x30]
    • x64: gs:[0x60]
  4. Inspect the PEB fields manually.