Category: Windbg

Navigating the Thread Environment Block _TEB

The Thread Environment Block is memory structure that lives in user mode address space which is unique to each thread that stores useful information about the thread. Looking at the 64bit structure in windbg we see its quite a large structure with a lot of cryptic fields. There are however some nice academic knowledge is gained by reviewing these fields for example SetLastError() and GetLastError() stores and retrieves and error states from here via the LastErrorValue field.

You can however notice the structure at offset 0x60 is the PEB and from here you can access such information as the dlls loaded into the process space and the address ranges of each dll.

0:000> dt _TEB
   +0x000 NtTib            : _NT_TIB
   +0x038 EnvironmentPointer : Ptr64 Void
   +0x040 ClientId         : _CLIENT_ID
   +0x050 ActiveRpcHandle  : Ptr64 Void
   +0x058 ThreadLocalStoragePointer : Ptr64 Void
   +0x060 ProcessEnvironmentBlock : Ptr64 _PEB
   +0x068 LastErrorValue   : Uint4B
   +0x06c CountOfOwnedCriticalSections : Uint4B
   +0x070 CsrClientThread  : Ptr64 Void
   +0x078 Win32ThreadInfo  : Ptr64 Void
   +0x080 User32Reserved   : [26] Uint4B
   +0x0e8 UserReserved     : [5] Uint4B
   +0x100 WOW32Reserved    : Ptr64 Void
   +0x108 CurrentLocale    : Uint4B
   +0x10c FpSoftwareStatusRegister : Uint4B
   +0x110 SystemReserved1  : [54] Ptr64 Void
   +0x2c0 ExceptionCode    : Int4B
   +0x2c8 ActivationContextStackPointer : Ptr64 _ACTIVATION_CONTEXT_STACK
   +0x2d0 SpareBytes       : [24] UChar
   +0x2e8 TxFsContext      : Uint4B
   +0x2f0 GdiTebBatch      : _GDI_TEB_BATCH
   +0x7d8 RealClientId     : _CLIENT_ID
   +0x7e8 GdiCachedProcessHandle : Ptr64 Void
   +0x7f0 GdiClientPID     : Uint4B
   +0x7f4 GdiClientTID     : Uint4B
   +0x7f8 GdiThreadLocalInfo : Ptr64 Void
   +0x800 Win32ClientInfo  : [62] Uint8B
   +0x9f0 glDispatchTable  : [233] Ptr64 Void
   +0x1138 glReserved1      : [29] Uint8B
   +0x1220 glReserved2      : Ptr64 Void
   +0x1228 glSectionInfo    : Ptr64 Void
   +0x1230 glSection        : Ptr64 Void
   +0x1238 glTable          : Ptr64 Void
   +0x1240 glCurrentRC      : Ptr64 Void
   +0x1248 glContext        : Ptr64 Void
   +0x1250 LastStatusValue  : Uint4B
   +0x1258 StaticUnicodeString : _UNICODE_STRING
   +0x1268 StaticUnicodeBuffer : [261] Wchar
   +0x1478 DeallocationStack : Ptr64 Void
   +0x1480 TlsSlots         : [64] Ptr64 Void
   +0x1680 TlsLinks         : _LIST_ENTRY
   +0x1690 Vdm              : Ptr64 Void
   +0x1698 ReservedForNtRpc : Ptr64 Void
   +0x16a0 DbgSsReserved    : [2] Ptr64 Void
   +0x16b0 HardErrorMode    : Uint4B
   +0x16b8 Instrumentation  : [11] Ptr64 Void
   +0x1710 ActivityId       : _GUID
   +0x1720 SubProcessTag    : Ptr64 Void
   +0x1728 EtwLocalData     : Ptr64 Void
   +0x1730 EtwTraceData     : Ptr64 Void
   +0x1738 WinSockData      : Ptr64 Void
   +0x1740 GdiBatchCount    : Uint4B
   +0x1744 CurrentIdealProcessor : _PROCESSOR_NUMBER
   +0x1744 IdealProcessorValue : Uint4B
   +0x1744 ReservedPad0     : UChar
   +0x1745 ReservedPad1     : UChar
   +0x1746 ReservedPad2     : UChar
   +0x1747 IdealProcessor   : UChar
   +0x1748 GuaranteedStackBytes : Uint4B
   +0x1750 ReservedForPerf  : Ptr64 Void
   +0x1758 ReservedForOle   : Ptr64 Void
   +0x1760 WaitingOnLoaderLock : Uint4B
   +0x1768 SavedPriorityState : Ptr64 Void
   +0x1770 SoftPatchPtr1    : Uint8B
   +0x1778 ThreadPoolData   : Ptr64 Void
   +0x1780 TlsExpansionSlots : Ptr64 Ptr64 Void
   +0x1788 DeallocationBStore : Ptr64 Void
   +0x1790 BStoreLimit      : Ptr64 Void
   +0x1798 MuiGeneration    : Uint4B
   +0x179c IsImpersonating  : Uint4B
   +0x17a0 NlsCache         : Ptr64 Void
   +0x17a8 pShimData        : Ptr64 Void
   +0x17b0 HeapVirtualAffinity : Uint4B
   +0x17b8 CurrentTransactionHandle : Ptr64 Void
   +0x17c0 ActiveFrame      : Ptr64 _TEB_ACTIVE_FRAME
   +0x17c8 FlsData          : Ptr64 Void
   +0x17d0 PreferredLanguages : Ptr64 Void
   +0x17d8 UserPrefLanguages : Ptr64 Void
   +0x17e0 MergedPrefLanguages : Ptr64 Void
   +0x17e8 MuiImpersonation : Uint4B
   +0x17ec CrossTebFlags    : Uint2B
   +0x17ec SpareCrossTebBits : Pos 0, 16 Bits
   +0x17ee SameTebFlags     : Uint2B
   +0x17ee SafeThunkCall    : Pos 0, 1 Bit
   +0x17ee InDebugPrint     : Pos 1, 1 Bit
   +0x17ee HasFiberData     : Pos 2, 1 Bit
   +0x17ee SkipThreadAttach : Pos 3, 1 Bit
   +0x17ee WerInShipAssertCode : Pos 4, 1 Bit
   +0x17ee RanProcessInit   : Pos 5, 1 Bit
   +0x17ee ClonedThread     : Pos 6, 1 Bit
   +0x17ee SuppressDebugMsg : Pos 7, 1 Bit
   +0x17ee DisableUserStackWalk : Pos 8, 1 Bit
   +0x17ee RtlExceptionAttached : Pos 9, 1 Bit
   +0x17ee InitialThread    : Pos 10, 1 Bit
   +0x17ee SpareSameTebBits : Pos 11, 5 Bits
   +0x17f0 TxnScopeEnterCallback : Ptr64 Void
   +0x17f8 TxnScopeExitCallback : Ptr64 Void
   +0x1800 TxnScopeContext  : Ptr64 Void
   +0x1808 LockCount        : Uint4B
   +0x180c SpareUlong0      : Uint4B
   +0x1810 ResourceRetValue : Ptr64 Void

The base address of the Thread Environment Block structure is stored in the FS segment register (and GS on x64), so with a bit of code similar to what is in wikipedia we can access it. Here (on a 64bit system) we access the contents at address of segment register GS plus offset 0x60 which should contain the address of the PEB.

   1: PEB* mrpeb;


   3: _asm

   4: {

   5:    MOV RAX,GS:[60H]

   6:    MOV mrpeb, RAX

   7: }

Windbg has a pseudo register that can be used to access the teb.
0:003> dt _TEB @$teb
You can navigate to access the PEB if you wish as a offset inside the teb
0:003> dt _PEB @$teb+60
Windbg comes with two built in functions which displays the most useful information from the teb or the peb via the !teb and the !peb command,

0:003> !teb
TEB at 000007fffffd7000
    ExceptionList:        0000000000000000
    StackBase:            00000000041f0000
    StackLimit:           00000000041ec000
    SubSystemTib:         0000000000000000
    FiberData:            0000000000001e00
    ArbitraryUserPointer: 0000000000000000
    Self:                 000007fffffd7000
    EnvironmentPointer:   0000000000000000
    ClientId:             0000000000002ea8 . 0000000000003658
    RpcHandle:            0000000000000000
    Tls Storage:          0000000000000000
    PEB Address:          000007fffffdf000
    LastErrorValue:       1
    LastStatusValue:      0
    Count Owned Locks:    0
    HardErrorMode:        0

Viewing import table from windbg

Viewing the functions that a particular dll or executable imports can be useful in understanding more about how a component behaves.There are tools such a dependency walker which can show you this information if you point it to a static binary which I won’t go into here. Here we will look at how to get this information when using windbg.

  1. List loaded modules and note their base address. (In this example I attached to notepad and want to see what modules notepad imports)


2. Dump the header from the dll your interested in !dh <module> –f




3. From the output from !dh look for the the “Import Address Table Directory”. In this example you see that it is at offset “C000” from the base address.

4. Use the “dps” command to dump the address at that offset and try to resolve them to symbols. You will likely need to run dps a number of times to cycle through the entire import table. You could also see that the size of the import table is “7F0” and tell dps to display until that address [ dps ffa30000+C000   ffa30000+C000+7F0 ]