5b. From Memory - PEB_TEB
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/
Process and Thread Environment Blocks
Where the TEB and PEB are located
The Thread Environment Block (TEB) and Process Environment Block (PEB) exist in memory, not inside the PE file itself.
1.1 PEB - Process Environment Block
- Located in user-mode memory at
fs:0x30(x86) orgs:0x60(x64) - Holds information about the process, such as:
- ImageBaseAddress (where PE is loaded)
- Loaded DLLs list
- Process heap
- Flags and debugging information
graph TD A[PEB] --> B[Image Base Address] A --> C[LDR Loaded Modules] A --> D[Process Parameters] A --> E[Heap Information]
1.2 TEB - Thread Environment Block
- Located at
fs:0x18(x86) orgs:0x30(x64) - Holds per-thread data, including:
- Exception handling information
- Stack limits
- Thread ID
- Pointer to PEB (to access process-wide information)
graph TD A[TEB] --> B[Thread ID] A --> C[Stack Base] A --> D[Stack Limit] A --> E[Pointer to PEB]
2. Relationship Between PE, PEB, and TEB
- PE is the file on disk.
- PEB is the process-wide data structure in memory.
- TEB is per-thread and contains a pointer to PEB.
- EAT is part of the PE file and is used to locate exported functions.
graph TD A[PE File] -->|Loaded by Windows Loader| B[PEB] B -->|Pointer to| C[Export Address Table EAT] B -->|Pointer to| D[LDR Loaded DLLs] B -->|Pointer to| E[Process Heap] F[Thread] -->|Each thread has| G[TEB] G -->|Points to| B
Why matter?
The Thread Environment Block (TEB) contains a pointer to the PEB. The PEB does not expose a dedicated ntdll.dll base field; its loader data leads to the process's loaded-module lists, from which a debugger or diagnostic tool can locate ntdll.dll. A system-service number must then be derived from the matching stub for that Windows build and architecture; it is not stored in the PEB.
TEB has a pointer to PEB, but PEB does not contain the TEB
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
3. Where is the PEB Located?
- In x86 (32-bit) Windows, the PEB is located at:
fs:[0x30] ; In the TEB (Thread Environment Block) - In x64 (64-bit) Windows, the PEB is located at:
gs:[0x60] ; In the TEB (Thread Environment Block) - The PEB address can be retrieved using the
NtQueryInformationProcessAPI or accessed directly via inline assembly.
4. 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. |
5. 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>
// Simplified/approximate PEB struct for illustration (x64).
// Real PEB layout (Windows SDK winternl.h / ntddk.h):
// Offset 0x00: InheritedAddressSpace (BYTE)
// Offset 0x01: ReadImageFileExecOptions (BYTE)
// Offset 0x02: BeingDebugged (BYTE)
// Offset 0x03: BitField / spare (BYTE)
// Offset 0x08: Mutant (HANDLE = 8 bytes on x64)
// Offset 0x10: ImageBaseAddress (PVOID = 8 bytes)
// Offset 0x18: Ldr (PPEB_LDR_DATA = 8 bytes)
// Fields below are approximate offsets; use offsetof() or WinDbg dt _PEB for exact values.
typedef struct _PEB {
BYTE InheritedAddressSpace; // offset 0x00
BYTE ReadImageFileExecOptions; // offset 0x01
BYTE BeingDebugged; // offset 0x02
BYTE BitField; // offset 0x03
BYTE Reserved1[4]; // padding to 0x08
HANDLE Mutant; // offset 0x08, 8 bytes on x64
PVOID ImageBaseAddress; // offset 0x10
PVOID Ldr; // offset 0x18 (PEB_LDR_DATA*)
PVOID ProcessParameters; // offset 0x20
BYTE Reserved2[400]; // approximate gap to OS version fields
ULONG OSMajorVersion;
ULONG OSMinorVersion;
USHORT OSBuildNumber; // 16-bit in actual _PEB (offset +0x120); ULONG would corrupt adjacent fields
USHORT OSCSDVersion; // follows OSBuildNumber in actual layout
} PEB, *PPEB; // NOTE: This is a simplified struct for learning purposes.
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
- Accesses the PEB structure using
__readgsqword(0x60), which retrieves the PEB address fromGS:[0x60]in x64. - Reads OS version details (
OSMajorVersion,OSMinorVersion,OSBuildNumber). - Prints the Windows version information.
6. 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;
}
7. How to View PEB in WinDbg
You can inspect the PEB structure using WinDbg or x64dbg.
Using WinDbg
- Attach to a process:
windbg -pn target.exe - Display the PEB:
This will show details like the OS version, loaded DLLs, and process parameters.!peb
Using x64dbg
- Attach to a process.
- Open the memory viewer.
- Navigate to:
- x86:
fs:[0x30] - x64:
gs:[0x60]
- x86:
- Inspect the PEB fields manually.