Skip to content

Analysis of Process Environment Block and Thread Environment Block

The Process Environment Block (PEB) and Thread Environment Block (TEB) are key data structures in the Windows operating system, used to store the running environment and state information of processes and threads, respectively. PEB contains process related information, such as startup parameters, module loading information, etc., while TEB contains thread context, local storage, and other information. They provide interfaces to access the running status of processes and threads, which are crucial for program execution and system resource management.

Analysis of PEB structure

This code obtains the PEB structure of the process by accessing the debugger and provides a series of functions to obtain the values of various fields in the PEB, such as whether the process has been debugged, process heap address, LDR address, process base address, system version information, etc. By calling these functions, it is convenient to obtain the running environment and status information of the process, which is helpful for process analysis and debugging.

python
from x32dbg import Debugger
import struct

class _PEB():
    def __init__(self, dbg):
        # Get the PEB address of the process
        self.base = dbg.get_peb_address(dbg.get_process_id())
        self.PEB = bytearray()

        # Fill the first 488 bytes of PEB
        for index in range(0, 488):
            readbyte = dbg.get_memory_byte(self.base + index)
            self.PEB.append(readbyte)

        # Initialize various fields in PEB
        index = 0x000
        # Inherit address space or not
        self.InheritedAddressSpace = self.PEB[index]
        index = 0x001
        # Read image file execution options
        self.ReadImageFileExecOptions = self.PEB[index]
        index = 0x002
        # Is it being debugged
        self.BeingDebugged = self.PEB[index]
        index = 0x003
        # Backup Boolean
        self.SpareBool = self.PEB[index]
        index = 0x004
        # mutex
        self.Mutant = self.PEB[index:index + 4]
        index = 0x008
        # Image base address
        self.ImageBaseAddress = self.PEB[index:index + 4]
        index = 0x00c
        # loader
        self.Ldr = self.PEB[index:index + 4]
        index = 0x010
        # Process parameters
        self.ProcessParameters = self.PEB[index:index + 4]
        index = 0x014
        # Subsystem data
        self.SubSystemData = self.PEB[index:index + 4]
        index = 0x018
        # Process Heap
        self.ProcessHeap = self.PEB[index:index + 4]
        index = 0x01c
        # Fast PEB lock
        self.FastPebLock = self.PEB[index:index + 4]
        index = 0x020
        # Quick PEB lock routine
        self.FastPebLockRoutine = self.PEB[index:index + 4]
        index = 0x024
        # Quick PEB unlocking routine
        self.FastPebUnlockRoutine = self.PEB[index:index + 4]
        index = 0x028
        # Environment update count
        self.EnviromentUpdateCount = self.PEB[index:index + 4]
        index = 0x02c
        # Kernel callback table
        self.KernelCallbackTable = self.PEB[index:index + 4]
        index = 0x030

        self.SystemReserved = []
        for i in range(0, 2):
            # System retention
            self.SystemReserved.append(self.PEB[index:index + 4])
            index += 4

        index = 0x038
        # free list
        self.FreeList = self.PEB[index:index + 4]
        index = 0x03c
        # TLS extension counter
        self.TlsExpansionCounter = self.PEB[index:index + 4]
        index = 0x040
        # TLS Bitmap
        self.TlsBitmap = self.PEB[index:index + 4]
        index = 0x044

        self.TlsBitmapBits = []
        for i in range(0, 2):
            # TLS Bitmap Bit
            self.TlsBitmapBits.append(self.PEB[index:index + 4])
            index += 4
        index = 0x04c
        # Read only shared memory base address
        self.ReadOnlySharedMemoryBase = self.PEB[index:index + 4]
        index = 0x050
        # Read only shared memory heap
        self.ReadOnlySharedMemoryheap = self.PEB[index:index + 4]
        index = 0x054
        # Read only static server data
        self.ReadOnlyStaticServerData = self.PEB[index:index + 4]
        index = 0x058
        # ANSI code page data
        self.AnsiCodePageData = self.PEB[index:index + 4]
        index = 0x05c
        # OEM code page data
        self.OemCodePageData = self.PEB[index:index + 4]
        index = 0x060
        # Unicode capitalization table data
        self.UnicodeCaseTableData = self.PEB[index:index + 4]
        index = 0x064
        # Number of processors
        self.NumberOfProcessors = self.PEB[index:index + 4]
        index = 0x068
        # Nt Global Flag
        self.NtGlobalFlag = self.PEB[index:index + 4]
        index = 0x070
        # Critical section timeout low section
        self.CriticalSectionTimeout_LowPart = self.PEB[index:index + 4]
        index = 0x074
        # Key parts timeout high part
        self.CriticalSectionTimeout_HighPart = self.PEB[index:index + 4]
        index = 0x078
        # Stockpile reserve
        self.HeapSegmentReserve = self.PEB[index:index + 4]
        index = 0x07c
        # Segment submission
        self.HeapSegmentCommit = self.PEB[index:index + 4]
        index = 0x080
        # Total idle threshold for heap cancellation
        self.HeapDeCommitTotalFreeThreshold = self.PEB[index:index + 4]
        index = 0x084
        # Free block threshold for heap cancellation
        self.HeapDeCommitFreeBlockThreshold = self.PEB[index:index + 4]
        index = 0x088
        # Number of piles
        self.NumberOfHeaps = self.PEB[index:index + 4]
        index = 0x08c
        # Maximum number of piles
        self.MaximumNumberOfHeaps = self.PEB[index:index + 4]
        index = 0x090
        # Process Heap
        self.ProcessHeaps = self.PEB[index:index + 4]
        index = 0x094
        # GDI Shared Handle Table
        self.GdiSharedHandleTable = self.PEB[index:index + 4]
        index = 0x098
        # Process Launcher Assistant
        self.ProcessStarterHelper = self.PEB[index:index + 4]
        index = 0x09c
        # GDI DC attribute list
        self.GdiDCAttributeList = self.PEB[index:index + 4]
        index = 0x0a0
        # Loader lock
        self.LoaderLock = self.PEB[index:index + 4]
        index = 0x0a4
        # Operating system major version number
        self.OSMajorVersion = self.PEB[index:index + 4]
        index = 0x0a8
        # Operating system minor version number
        self.OSMinorVersion = self.PEB[index:index + 4]
        index = 0x0ac
        # Operating system generation number
        self.OSBuildNumber = self.PEB[index:index + 2]
        index = 0x0ae
        # Operating System CSD Version
        self.OSCSDVersion = self.PEB[index:index + 2]
        index = 0x0b0
        # Operating System Platform ID
        self.OSPlatformId = self.PEB[index:index + 4]
        index = 0x0b4
        # Image subsystem
        self.ImageSubsystem = self.PEB[index:index + 4]
        index = 0x0b8
        # Image subsystem main version number
        self.ImageSubsystemMajorVersion = self.PEB[index:index + 4]
        index = 0x0bc
        # Image subsystem sub version number
        self.ImageSubsystemMinorVersion = self.PEB[index:index + 4]
        index = 0x0c0
        # Image process association mask
        self.ImageProcessAffinityMask = self.PEB[index:index + 4]
        index = 0x0c4
        # uint32 GdiHandleBuffer[34]
        self.GdiHandleBuffer = []
        for i in range(0, 34):
            # GDI Handle Buffer
            self.GdiHandleBuffer.append(self.PEB[index:index + 4])
            index += 4
        index = 0x14c
        # Post processing initialization routine
        self.PostProcessInitRoutine = self.PEB[index:index + 4]
        index = 0x150
        # TLS Extended Bitmap
        self.TlsExpansionBitmap = self.PEB[index:index + 4]
        index = 0x154

        # uint32 TlsExpansionBitmapBits[32]
        self.TlsExpansionBitmapBits = []
        for i in range(0, 32):
            # TLS Extended Bitmap Bit
            self.TlsExpansionBitmapBits.append(self.PEB[index:index + 4])
            index += 4
        index = 0x1d4
        # Session ID
        self.SessionId = self.PEB[index:index + 4]
        index = 0x1d8
        # Compatibility information
        self.AppCompatInfo = self.PEB[index:index + 4]
        index = 0x1dc
        # struct _UNICODE_STRING CSDVersion
        # CSD version string length
        self.CSDVersion_Length = self.PEB[index:index + 2]
        index += 2
        # CSD version string maximum length
        self.CSDVersion_MaximumLength = self.PEB[index:index + 2]
        index += 2
        # CSD version string buffer address
        self.CSDVersion_Buffer = self.PEB[index:index + 2]
        index += 2

    def get_BeingDebugged(self):
        return self.BeingDebugged

    def get_ProcessHeaps(self):
        pack = struct.unpack('<L', bytes(self.ProcessHeap))
        return hex(pack[0])

    def get_Ldr(self):
        pack = struct.unpack('<L', bytes(self.Ldr))
        return hex(pack[0])

    def get_NumberOfHeaps(self):
        pack = struct.unpack('<L', bytes(self.NumberOfHeaps))
        return pack[0]

    def get_ImageBaseAddress(self):
        pack = struct.unpack('<L', bytes(self.ImageBaseAddress))
        return hex(pack[0])

    def get_NumberOfProcessors(self):
        pack = struct.unpack('<L', bytes(self.NumberOfProcessors))
        return pack[0]

    def get_OSVersion(self):
        major_version = struct.unpack('<L', bytes(self.OSMajorVersion))
        minor_version = struct.unpack('<L', bytes(self.OSMinorVersion))
        build_number = struct.unpack('<H', bytes(self.OSBuildNumber))
        csd_version = struct.unpack('<H', bytes(self.OSCSDVersion))
        return f"{major_version}.{minor_version}.{build_number}.{csd_version}"

    def get_GdiHandleBuffer(self):
        handle_buffer = []
        for handle in self.GdiHandleBuffer:
            handle_value = struct.unpack('<L', bytes(handle))
            handle_buffer.append(hex(handle_value[0]))
        return handle_buffer

    def get_FastPebLock(self):
        pack = struct.unpack('<L', bytes(self.FastPebLock))
        return hex(pack[0])

    def get_HeapSegmentReserve(self):
        pack = struct.unpack('<L', bytes(self.HeapSegmentReserve))
        return hex(pack[0])

    def get_GdiSharedHandleTable(self):
        pack = struct.unpack('<L', bytes(self.GdiSharedHandleTable))
        return hex(pack[0])

    def get_AppCompatInfo(self):
        pack = struct.unpack('<L', bytes(self.AppCompatInfo))
        return hex(pack[0])

    def get_CSDVersion(self):
        length = struct.unpack('<H', bytes(self.CSDVersion_Length))
        maximum_length = struct.unpack('<H', bytes(self.CSDVersion_MaximumLength))
        buffer_address = struct.unpack('<H', bytes(self.CSDVersion_Buffer))
        return f"Length: {length}, Maximum Length: {maximum_length}, Buffer Address: {buffer_address}"

if __name__ == "__main__":
    dbg = Debugger(address="127.0.0.1", port=6589)
    if dbg.connect() == True:

        # Initialize PEB structure
        peb_struct = _PEB(dbg)

        # Has the process been debugged
        is_debug = peb_struct.get_BeingDebugged()
        print("Has the process been debugged = {}".format(is_debug))

        # Get the entry address of the heap in the process
        heap_address = peb_struct.get_ProcessHeaps()
        print("Process heap address = {}".format(heap_address))

        # Process LDR address
        ldr_address = peb_struct.get_Ldr()
        print("Process LDR address = {}".format(ldr_address))

        # Get process base address
        base_address = peb_struct.get_ImageBaseAddress()
        print("Process base address = {}".format(base_address))

        # Obtain system version number information
        version = peb_struct.get_OSVersion()
        print("System version = {}".format(version))

        dbg.close_connect()

After the program runs successfully, it will output the following content:

python
Has the process been debugged = 1
Process heap address = 0xfd0000
Process LDR address = 0x77bf0c40
Process base address = 0xe80000
System version = (10,).(0,).(17763,).(0,)

Analysis of TEB structure

This code defines a class called TEB to retrieve information about the Thread Environment Block (TEB). By communicating with the debugger, it can access the TEB of the current thread in the process and provide a series of methods to obtain the values of various fields in the TEB, such as thread ID, last error value, current region setting, etc. In the main program, it first connects to the debugger, then instantiates the TEB class and calls its methods to obtain the current region setting information of the thread and print it out.

python
from x32dbg import Debugger
import struct

class _TEB():
    def __init__(self, dbg):
        # Get the base address of the Thread Environment Block (TEB)
        self.base = dbg.get_teb_address(dbg.get_thread_id())
        # Create a byte array to store the content of TEB
        self.TEB = bytearray()

        # Read the content of TEB
        for index in range(0, 512):
            readbyte = dbg.get_memory_byte(self.base + index)
            self.TEB.append(readbyte)

        # Initialize the various fields of the TEB structure
        index = 0x000
        # The NtTib pointer points to the address of the thread environment block
        self.NtTib = self.TEB[index:index+4]
        index = 0x004
        # EnvironmentPointer points to the address of the environment block
        self.EnvironmentPointer = self.TEB[index:index+4]
        index = 0x008
        # ClientId represents the thread ID
        self.ClientId = self.TEB[index:index+8]
        # ActiveRpcHandle represents the RPC handle of the current thread
        index = 0x010
        self.ActiveRpcHandle = self.TEB[index:index+4]
        # ThreadLocalStoragePointer pointer to the thread's local storage
        index = 0x014
        self.ThreadLocalStoragePointer = self.TEB[index:index+4]
        # ProcessEnvironmentBlock points to the address of the process environment block
        index = 0x018
        self.ProcessEnvironmentBlock = self.TEB[index:index+4]
        # LastErrorValue represents the last error value of the thread
        index = 0x01c
        self.LastErrorValue = self.TEB[index:index+4]
        # CountOfOwnedCriticalSections represents the number of critical sections owned by a thread
        index = 0x020
        self.CountOfOwnedCriticalSections = self.TEB[index:index+4]
        # CsrClientThread represents a CSRSS thread
        index = 0x024
        self.CsrClientThread = self.TEB[index:index+4]
        # Win32ThreadInfo pointer to the information of the Win32 thread
        index = 0x028
        self.Win32ThreadInfo = self.TEB[index:index+4]
        # User32Reserved is a field reserved for user space
        index = 0x02c
        self.User32Reserved = self.TEB[index:index+4]
        # UserReserved is a field reserved for users
        index = 0x030
        self.UserReserved = self.TEB[index:index+4]
        # WOW32Reserved is a field reserved for WOW64 threads
        index = 0x034
        self.WOW32Reserved = self.TEB[index:index+4]
        # Current Locale
        index = 0x038
        self.CurrentLocale = self.TEB[index:index+4]
        # FpSoftwareStatusRegister floating-point software status register
        index = 0x03c
        self.FpSoftwareStatusRegister = self.TEB[index:index+4]
        # SystemReserved1 is a field reserved by the system
        index = 0x040
        self.SystemReserved1 = self.TEB[index:index+4]
        # ExceptionCode represents the current exception code of the thread
        index = 0x044
        self.ExceptionCode = self.TEB[index:index+4]
        # ActivationContextStackPointer represents the pointer to the activated context stack
        index = 0x048
        self.ActivationContextStackPointer = self.TEB[index:index+4]
        # SpareBytes1 is a reserved field
        index = 0x04c
        self.SpareBytes1 = self.TEB[index:index+36]
        # TxFsContext Transaction File System (TxF) Context
        index = 0x070
        self.TxFsContext = self.TEB[index:index+4]
        # GdiTebBatch for TEB in GDI batch processing
        index = 0x074
        self.GdiTebBatch = self.TEB[index:index+124]
        # RealClientId
        index = 0x0f0
        self.RealClientId = self.TEB[index:index+8]
        # GDI process handle cached by GdiCachedProcessHandle
        index = 0x0f8
        self.GdiCachedProcessHandle = self.TEB[index:index+4]
        # GdiClientPID GDI client process ID
        index = 0x0fc
        self.GdiClientPID = self.TEB[index:index+4]
        # GdiClientTID GDI client thread ID
        index = 0x100
        self.GdiClientTID = self.TEB[index:index+4]
        # GdiThreadLocalInfo GDI thread local information
        index = 0x104
        self.GdiThreadLocalInfo = self.TEB[index:index+4]
        # Win32ClientInfo
        index = 0x108
        self.Win32ClientInfo = self.TEB[index:index+36]
        # GlDispatchTable OpenGL's scheduling table
        index = 0x12c
        self.glDispatchTable = self.TEB[index:index+488]
        # GlReserved1 is a field reserved for OpenGL
        index = 0x324
        self.glReserved1 = self.TEB[index:index+104]
        # GlReserved2 is a field reserved for OpenGL
        index = 0x394
        self.glReserved2 = self.TEB[index:index+8]
        # GlSectionInfo OpenGL Section Information
        index = 0x39c
        self.glSectionInfo = self.TEB[index:index+4]
        # GlSection OpenGL
        index = 0x3a0
        self.glSection = self.TEB[index:index+4]
        # GlTable OpenGL Table
        index = 0x3a4
        self.glTable = self.TEB[index:index+4]
        # GlCurrentRC's current OpenGL context
        index = 0x3a8
        self.glCurrentRC = self.TEB[index:index+4]
        # GlContext OpenGL Context
        index = 0x3ac
        self.glContext = self.TEB[index:index+4]
        # The last status value of LastStatusValue
        index = 0x3b0
        self.LastStatusValue = self.TEB[index:index+4]
        # StaticUnicode String
        index = 0x3b4
        self.StaticUnicodeString = self.TEB[index:index+68]
        # StaticUnicode Buffer
        index = 0x3f8
        self.StaticUnicodeBuffer = self.TEB[index:index+68]
        # Padding is used to fill in fields
        index = 0x43c
        self.padding = self.TEB[index:index+8]

    def get_ThreadId(self):
        pack = struct.unpack('<LL', bytes(self.ClientId))
        return pack[0]

    def get_LastErrorValue(self):
        pack = struct.unpack('<L', bytes(self.LastErrorValue))
        return pack[0]

    def get_CurrentLocale(self):
        pack = struct.unpack('<L', bytes(self.CurrentLocale))
        return hex(pack[0])

    def get_GdiTebBatch(self):
        return self.GdiTebBatch

    def get_glDispatchTable(self):
        return self.glDispatchTable

if __name__ == "__main__":
    dbg = Debugger(address="127.0.0.1", port=6589)
    if dbg.connect() == True:

        # Initialize TEB structure
        teb_struct = _TEB(dbg)

        # Read thread ID
        thread_id = teb_struct.get_ThreadId()
        print("ThreadID = {}".format(hex(thread_id)))

        # Read get_LastErrorValue
        lasterror_value = teb_struct.get_LastErrorValue()
        print("LastErrorValue = {}".format(lasterror_value))

        dbg.close_connect()

After the program runs successfully, it will output the following content:

python
ThreadID = 0xf8c000
LastErrorValue = 0