Sunday 4 January 2015

Win32 Hacks: Loading API functions from a process' Process Environment Block (PEB) Part 1 of 2

Win32 Hacks: Loading API functions from a process' Process Environment Block (PEB)

Preliminary

Part 2 is here.

Edit: The original post became a bit too long and elaborate, so I split it into two posts. This first part shows how to get the base address of a persistently loaded library, and part 2 shows how to parse a module given its base addres and access its imports (get function pointers).
Occasionally one might have good reasons to make a program without any imports. That is, you want to make an executable or have pure code without any dependencies at link time (freestanding code) Or maybe one has code which is injected into another process (debuggers, game trainers, diagnostic tools, etc), in which case one needs a way to find the function pointers to the API functions one wants to use.

This can be done by reading the Process Environment Block, and it can be found via a pointer entry inside the Thread Information Block. Once you have the PEB, one can find module information for kernel32.dll, kernelbase.dll, and ntdll.dll in a linked list there, as these libraries are shared between all processes. They are always loaded into userspace memory when a new process is created.
There's plenty of information in the module list, but the only thing you'll need to load a library's functions is the image base address. When you have the base address, the only thing left to do is to parse the library image and its export address table.

A word of warning here. The TIB and PEB structures are highly susceptible for change between Windows versions. Memory layouts explained later might be completely different between my machine and your machine. If someone have definite information on the TIB and PEB layouts for all Windows versions, I'd be grateful for information on that. I would also appreciate more information on the layouts for 64-bit processes.
If you already are familiar with the TIB and PEB, skip the two following headings.

What is the Thread Information Block?

The TIB is an internal and undocumented/unofficial Windows data structure that contains information about a process' currently running thread, and which lives in user space memory.  For a general layout, Wikipedia has a nice overview. In 32-bit executables, the selector fs with some offset is used to access the TIB, while in 64-bit executables one uses the gs selector. (From now on, I assume 32-bit x86 executables unless specifically said otherwise.) If one wants to do some more heavy work, it might be more convenient with a direct pointer to the TIB. A direct pointer can be found by reading fs:[0x18]. Other information you can access via the TIB is the current thread ID, the current locale, the last error reported by GetLastError(), and so on. Remember that there is one TIB per thread. We're only interested in one field though, and that is the pointer to the Process Environment Block, and the PEB pointer in the TIBs is the same for all TIBs, as there is only one PEB per process. You can get that pointer by reading fs:[0x30].


;NASM x86 functions for getting the TIB
GLOBAL _GetTIB
SECTION .text;

_GetTIB: mov eax, [fs:0x18]
ret

What is the Process Environment Block?

The PEB is an internal and undocumented (but more documented than TIB) data structure that contains information about the process itself, and which lives in user space memory. Notably it contains information about modules the process shares with other processes. Again, Wikipedia to the rescue. To get the address to the PEB, access the PEB pointer field of any TIB. Its offset from the TIB start is 48 bytes. So fs:[0x30] gets you the pointer to the PEB.

;NASM x86 functions for getting the PEB
GLOBAL _GetPEB
SECTION .text;

_GetPEB: mov eax, [fs:0x30]
ret

Once we have the PEB, the Ldr field is the interesting one. It is itself a structure and contains a InMemoryOrderModuleList member. InMemoryOrderModuleList is of type LIST_ENTRY from winnt.h, line 1049. The list is an intrusive list, i.e the forward and back pointers are a part of the objects themselves. The larger structure is of type LDR_DATA_TABLE_ENTRY. MSDN has more information.

/* Direct member of the PEB */
typedef struct _PEB_LDR_DATA {
 BYTE       Reserved1[8];
 PVOID      Reserved2[3];
 LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

/* Already defined in wint.h (1049) */
/*
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
*/

/* Not used by us, but you need it to have a complete PEB structure */
typedef struct _RTL_USER_PROCESS_PARAMETERS {
 BYTE           Reserved1[16];
 PVOID          Reserved2[10];
 UNICODE_STRING ImagePathName;
 UNICODE_STRING CommandLine;
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;

/* The actual module structure which is a part of the intrusive list */
typedef struct _LDR_DATA_TABLE_ENTRY {
 PVOID Reserved1[2];
 LIST_ENTRY InMemoryOrderLinks;
 PVOID Reserved2[2];
 PVOID DllBase;
 PVOID EntryPoint;
 PVOID Reserved3;
 UNICODE_STRING FullDllName;
 BYTE Reserved4[8];
 PVOID Reserved5[3];
 union {
  ULONG CheckSum;
  PVOID Reserved6;
 };
 ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

typedef void(__stdcall * PPS_POST_PROCESS_INIT_ROUTINE)(void);

typedef struct _PEB32 {
 BYTE                          Reserved1[2];
 BYTE                          BeingDebugged;
 BYTE                          Reserved2[1];
 PVOID                         Reserved3[2];
 PPEB_LDR_DATA                 Ldr;
 PRTL_USER_PROCESS_PARAMETERS  ProcessParameters;
 BYTE                          Reserved4[104];
 PVOID                         Reserved5[52];
 PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
 BYTE                          Reserved6[128];
 PVOID                         Reserved7[1];
 ULONG                         SessionId;
} PEB32, *PPEB32;
Because the list is intrusive, you have to iterate over the LIST_ENTRY list and cast the pointer to PLDR_DATA_TABLE_ENTRY each time. Also, the list is a circular list, so make sure you compare the current pointer against a reference point.
LDR_DATA_TABLE_ENTRY is the structure which contains information about a loaded module, for example kernel32.dll. We can now finally write a function which gives us the base address of a library.

//Look up base address for an module (for example "kernel32.dll")
void* slGetModuleBase(LPCWSTR moduleName){
 //Get Pointer to PEB structure
 void* pebPtr = slGetPEB(); 
 PPEB32 peb = (PPEB32)pebPtr;
 //Reference point / tail to compare against, since the list is circular
 PLIST_ENTRY moduleListTail = &peb->Ldr->InMemoryOrderModuleList;
 PLIST_ENTRY moduleList = moduleListTail->Flink;
 //Traverse the list until moduleList gets back to moduleListTail
 do {
  char* modulePtrWithOffset = (char*)moduleList;
  //List is intrusive, a part of a larger LDR_DATA_TABLE structure,
  //so cast the pointer
  PLDR_DATA_TABLE_ENTRY module = (PLDR_DATA_TABLE_ENTRY)modulePtrWithOffset;
  //Compare the name of the entry against our parameter name
  //Note that the name is a wide string
  if (!slWStrCompare(module->FullDllName.Buffer, moduleName)){
   //The actual position of the image base address inside
   //the LDR_DATA_TABLE_ENTRY seems to change *a lot*.
   //Apparently on Windows 8.1 it wasn't located in the
   //correct place according to my structures defined above.
   //It should have been "DllBase", but apparently it
   //was 8 bytes back, inside Reserved2[0]
   void* DllBase = module->Reserved2[0];
   return DllBase;
  }
  moduleList = moduleList->Flink;
 } while (moduleList != moduleListTail);
 return NULL;
}

//Now we can get the base address like so:
int main(void){
 void* kernel32Base = slGetModuleBase(L"KERNEL32.DLL");
 /* ... */
 return 0;
}
This post became longer than I expected, which is why I split it up. In part 2 I'll show you how to get the address of a function from the DLLs export table, by parsing the image DOS and PE headers from the beginning of the base address. Part 2 is here.

No comments:

Post a Comment

Subscribe to RSS feed