PHP Accessing Process PEB

AceInfinity

Emeritus, Contributor
Joined
Feb 21, 2012
Posts
1,728
Location
Canada
The FS register is one of 6 segment registers in x86. It has no processor-defined purpose, but instead is given purpose by the OS using them, thus it really depends on how the OS wants to use it. Specifically, FS (and GS) are commonly used by the kernel to access thread specific memory.

While in user-mode, the FS segment register which was introduced in 80386 points to a Thread Information Block, and to the Processor Control Region (KPCR) in kernel-mode. Here's a link describing more about some of the Windows NT TIB: Under The Hood -- MSJ, May 1996

The 30h PVOID* pProcess field contains a linear address for the process database representing the process that owns the thread. However, this is not the same as a process handle or process ID.

Here's some demo code I wrote in C++ using GCC and its version of inline ASM (AT&T):
Code:
[NO-PARSE]#include <iostream>
#include <Windows.h>

typedef struct _PEB
{
  UCHAR InheritedAddressSpace;
  UCHAR ReadImageFileExecOptions;
  UCHAR BeingDebugged;
  UCHAR Spare;
  PVOID Mutant;
  PVOID ImageBaseAddress;
  struct PEB_LDR_DATA *Ldr;
  void *ProcessParameters;
  PVOID SubSystemData;
  PVOID ProcessHeap;
  PVOID FastPebLock;
  void *FastPebLockRoutine;
  void *FastPebUnlockRoutine;
  ULONG EnvironmentUpdateCount;
  PVOID *KernelCallbackTable;
  PVOID EventLogSection;
  PVOID EventLog;
  void *FreeList;
  ULONG TlsExpansionCounter;
  PVOID TlsBitmap;
  ULONG TlsBitmapBits[0x2];
  PVOID ReadOnlySharedMemoryBase;
  PVOID ReadOnlySharedMemoryHeap;
  PVOID *ReadOnlyStaticServerData;
  PVOID AnsiCodePageData;
  PVOID OemCodePageData;
  PVOID UnicodeCaseTableData;
  ULONG NumberOfProcessors;
  ULONG NtGlobalFlag;
  UCHAR Spare2[0x4];
  ULARGE_INTEGER CriticalSectionTimeout;
  ULONG HeapSegmentReserve;
  ULONG HeapSegmentCommit;
  ULONG HeapDeCommitTotalFreeThreshold;
  ULONG HeapDeCommitFreeBlockThreshold;
  ULONG NumberOfHeaps;
  ULONG MaximumNumberOfHeaps;
  PVOID **ProcessHeaps;
  PVOID GdiSharedHandleTable;
  PVOID ProcessStarterHelper;
  PVOID GdiDCAttributeList;
  PVOID LoaderLock;
  ULONG OSMajorVersion;
  ULONG OSMinorVersion;
  ULONG OSBuildNumber;
  ULONG OSPlatformId;
  ULONG ImageSubSystem;
  ULONG ImageSubSystemMajorVersion;
  ULONG ImageSubSystemMinorVersion;
  ULONG GdiHandleBuffer[0x22];
  PVOID ProcessWindowStation;
} PEB;

PEB GetProcPEB()
{
  PEB *x;
  __asm__ __volatile__(
    "movl %0, %%fs:0x30;"
    :"=r"(x)
  );
  return *x;
}

int main()
{
  PEB peb = GetProcPEB();
  std::cout << "Image Base Address: "
            << std::hex
            << peb.ImageBaseAddress << std::endl;
}[/NO-PARSE]

Typically, the base address is 0x400000 a high percentage of the time, but it isn't required to be. This definition of the PEB data-structure is defined within a header file (winternl.h), but this is not how it appears in memory... Nowhere near a close representation. In fact: undocumented.ntinternals.net/UserMode/Undocumented Functions/NT Objects/Process/PEB.html

This header contains lots of interesting, and undocumented content. For a decent reason code-wise, but I don't see why an explanation of it's contents aren't very well documented.

To further clarify, fs:0x30 points to the parent PEB structure. If you were debugging a program in OllyDbg, you could see this by clicking in the dump window to make it active, pressing Ctrl+G (goto expression), and typing in fs:[30]. The OS links the PEB_LDR_DATA to the PEB, and in here, you would see how each entry is cyclically linked together for the debugged process; each entry structure contains forward and backward links to every other element. The PEB_LDR_DATA contains information about modules loaded by the current process, in various forms; load order, memory order, init order. We can use the PEB to walk this information and retrieve some nice information:

Here's some more code I wrote for demonstration.
Code:
[NO-PARSE]#include <iostream>
#include <iomanip>
#include <Windows.h>

struct PEB
{
  UCHAR InheritedAddressSpace;
  UCHAR ReadImageFileExecOptions;
  UCHAR BeingDebugged;
  UCHAR Spare;
  PVOID Mutant;
  PVOID ImageBaseAddress;
  struct PEB_LDR_DATA *Ldr;
  void *ProcessParameters;
  PVOID SubSystemData;
  PVOID ProcessHeap;
  PVOID FastPebLock;
  void *FastPebLockRoutine;
  void *FastPebUnlockRoutine;
  ULONG EnvironmentUpdateCount;
  PVOID *KernelCallbackTable;
  PVOID EventLogSection;
  PVOID EventLog;
  void *FreeList;
  ULONG TlsExpansionCounter;
  PVOID TlsBitmap;
  ULONG TlsBitmapBits[0x2];
  PVOID ReadOnlySharedMemoryBase;
  PVOID ReadOnlySharedMemoryHeap;
  PVOID *ReadOnlyStaticServerData;
  PVOID AnsiCodePageData;
  PVOID OemCodePageData;
  PVOID UnicodeCaseTableData;
  ULONG NumberOfProcessors;
  ULONG NtGlobalFlag;
  UCHAR Spare2[0x4];
  ULARGE_INTEGER CriticalSectionTimeout;
  ULONG HeapSegmentReserve;
  ULONG HeapSegmentCommit;
  ULONG HeapDeCommitTotalFreeThreshold;
  ULONG HeapDeCommitFreeBlockThreshold;
  ULONG NumberOfHeaps;
  ULONG MaximumNumberOfHeaps;
  PVOID **ProcessHeaps;
  PVOID GdiSharedHandleTable;
  PVOID ProcessStarterHelper;
  PVOID GdiDCAttributeList;
  PVOID LoaderLock;
  ULONG OSMajorVersion;
  ULONG OSMinorVersion;
  ULONG OSBuildNumber;
  ULONG OSPlatformId;
  ULONG ImageSubSystem;
  ULONG ImageSubSystemMajorVersion;
  ULONG ImageSubSystemMinorVersion;
  ULONG GdiHandleBuffer[0x22];
  PVOID ProcessWindowStation;
};

typedef struct _UNICODE_STRING
{
  USHORT Length;
  USHORT MaximumLength;
  PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

struct LDR_MODULE
{
  /* 0x00 */ LIST_ENTRY InLoadOrder;
  // 4 byte forward link
  // 4 byte backward link
  /* 0x08 */ LIST_ENTRY InMemOrder;
  /* 0x10 */ LIST_ENTRY InInitOrder;
  /* 0x18 */ uint32_t DllBase;
  /* 0x1C */ uint32_t EntryPoint;
  /* 0x1F */ uint32_t Reserved;
  /* 0x24 */ UNICODE_STRING FullDllName;
  // 2 byte Length
  // 2 byte MaxLength
  // 4 byte pointer to unicode string
  /* 0x2C */ UNICODE_STRING BaseDllName;
};

struct PEB_LDR_DATA
{
  /* 0x00 */ uint32_t Length;
  /* 0x04 */ uint8_t Initialized[4];
  /* 0x08 */ uint32_t SsHandle;
  /* 0x0C */ LIST_ENTRY InLoadOrder;
  /* 0x14 */ LIST_ENTRY InMemOrder;
  /* 0x1C */ LIST_ENTRY InInitOrder;
  /* 0x24 */ uint8_t EntryInProgress;
};

// typedef struct _LIST_ENTRY {
//   struct _LIST_ENTRY  *Flink;
//   struct _LIST_ENTRY  *Blink;
// } LIST_ENTRY, *PLIST_ENTRY;

PEB GetProcPEB()
{
  PEB *x;
  __asm__ __volatile__(
    "movl %0, %%fs:0x30;"
    :"=r"(x)
  );
  return *x;
}

int main()
{
  ::PEB peb = GetProcPEB();
  std::cout << "Image Base Address: "
            << std::hex
            << peb.ImageBaseAddress << std::endl;

  std::endl(std::cout);

  PEB_LDR_DATA *peb_ldr_data = peb.Ldr;
  LIST_ENTRY mem_list = peb_ldr_data->InMemOrder;

  // loop through doubly linked list until beginning
  LDR_MODULE *module = (LDR_MODULE *)mem_list.Flink;
  uintptr_t first_ptr = (uintptr_t)module;
  do
  {
    std::wcout << std::setw(20)
               << module->FullDllName.Buffer
               << std::uppercase << std::hex
               << " [EntryPoint: 0x" << module->EntryPoint << "]" << std::endl;
    module = (LDR_MODULE *)((LIST_ENTRY *)module)->Flink;
  }
  while ((uintptr_t)module != first_ptr);
}[/NO-PARSE]

The result:
Code:
[NO-PARSE]Image Base Address: 0x400000

            main.exe [EntryPoint: 0x4A0048]
           ntdll.dll [EntryPoint: 0x3C003A]
        KERNEL32.DLL [EntryPoint: 0x420040]
      KERNELBASE.dll [EntryPoint: 0x460044]
          msvcrt.dll [EntryPoint: 0x3E003C]
  libgcc_s_dw2-1.dll [EntryPoint: 0x40003E]
     libstdc++-6.dll [EntryPoint: 0x3A0038][/NO-PARSE]

The poor part about this is the fact that lots of this is undocumented. LDR_MODULE was extended in Windows 8. Here is another definition that I haven't tested:
Code:
[NO-PARSE]typedef struct _LDR_MODULE
{
  LIST_ENTRY              InLoadOrderModuleList;
  LIST_ENTRY              InMemoryOrderModuleList;
  LIST_ENTRY              InInitializationOrderModuleList;
  PVOID                   BaseAddress;
  PVOID                   EntryPoint;
  ULONG                   SizeOfImage;
  UNICODE_STRING          FullDllName;
  UNICODE_STRING          BaseDllName;
  ULONG                   Flags;
  SHORT                   LoadCount;
  SHORT                   TlsIndex;
  LIST_ENTRY              HashTableEntry;
  ULONG                   TimeDateStamp;
} LDR_MODULE, *PLDR_MODULE;[/NO-PARSE]
 
Last edited:
Read More:

More Informative than any other TIB article IMO.
Since most stuff here is even wrong or missing, https://en.wikipedia.org/wiki/Win32_Thread_Information_Block
 
Read More:

More Informative than any other TIB article IMO.
Since most stuff here is even wrong or missing, https://en.wikipedia.org/wiki/Win32_Thread_Information_Block

Actually that doesn't say much at all about the TIB, and the structure in memory looks much different.
 
Read More:

More Informative than any other TIB article IMO.
Since most stuff here is even wrong or missing, https://en.wikipedia.org/wiki/Win32_Thread_Information_Block

Actually that doesn't say much at all about the TIB, and the structure in memory looks much different.
I don't mean that this can replace whole TIB article, it is just more informative because now you can check all file segments yourself and play with them.
For example I had hard time finding Thread ID, UniqueThread.
The way this was docummented in TIB article was,
[TABLE="class: wikitable"]
[TR]
[TD="align: right"]FS:[0x1FC][/TD]
[TD="align: right"]1248[/TD]
[TD="align: right"]NT, Wine[/TD]
[TD]GDI TEB Batch (OS), vm86 private data (Wine)[/TD]
[/TR]
[/TABLE]
But turned out to be wrong after taking a look at generated header, which neatly showed,
GDI_TEB_BATCH GdiTebBatch; /* 0x1d4 / 0x2f0 */
CLIENT_ID RealClientId; /* 0x6b4 / 0x7d8 */

typedef struct _CLIENT_ID {
/* These are numeric ids */
HANDLE UniqueProcess;
HANDLE UniqueThread;
}
CLIENT_ID;
typedef CLIENT_ID *PCLIENT_ID;

So UniqueThread was not even a part of GdiTebBatch to begin with, but RealClientId.

If you already have basic understanding of TIB, reading this might save you alot of time, give you a basic concept about the structure, and better understanding of TIB.
 
I understand now what you meant, and you seem to know what you're talking about so that's great! Maybe I can learn some stuff from you if you stick around on this forum and I don't doubt it. :)

Cheers
 

Has Sysnative Forums helped you? Please consider donating to help us support the site!

Back
Top