AceInfinity
Emeritus, Contributor
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
Here's some demo code I wrote in C++ using GCC and its version of inline ASM (AT&T):
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.
The result:
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:
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: