Friday, 19 June 2009

Retrieving Kernel32's Base Address

For shellcode, a common method to resolve the addresses of library functions needed, is to get the base address of the kernel32.dll image in memory and retrieve the addresses of GetProcAddress and LoadLibraryA by parsing the kernel32 images Export Address Table (EAT). These two functions can then be used to resolve the remaining functions needed by the shellcode. To retrieve the kernel32.dll base address most shellcodes use the Process Environment Block (PEB) structure to retrieve a list of modules currently loaded in the processes address space. The InInitializationOrder module list pointed to by the PEB's Ldr structure holds a linked list of modules. Typically the second entry in this list has always been that of kernel32.dll. The code used to retrieve the kernel32 base address based on this method is shown below:

This method has worked for all versions of Windows from Windows 2000 up to and including Windows Vista. The introduction of Windows 7 (rc1) has broken this method of retrieving the kernel32 base address due to the new MinWin kernel structure employed by Windows 7. A new module kernelbase.dll is loaded before kernel32.dll and as such appears in the second entry of the InInitializationOrder module list.

To retrieve the kernel32.dll base address in a generic manner on all versions of Windows from Windows 2000 up to and including Windows 7 (rc1) a slightly modified approach can be used. Instead of parsing the PEB's InInitializationOrder module list, the InMemoryOrder module list can be parsed instead. The third entry in this list will always be that of kernel32.dll (The first being that of the main module and the second being that of ntdll.dll). The code used to retrieve the kernel32 base address based on this method is shown below:

Update: Their appears to be some cases on Windows 2000 whereby the above method will not yield the correct result. A more robust method, albeit a more lengthy one, can be seen below. We search the InMemoryOrder module list for the kernel32 module using a hash of the module name for comparison. We also normalise the module name to uppercase as some systems store module names in uppercase and some in lowercase.

cld // clear the direction flag for the loop xor edx, edx // zero edx

mov edx, [fs:edx+0x30] // get a pointer to the PEB mov edx, [edx+0x0C] // get PEB->Ldr mov edx, [edx+0x14] // get the first module from the InMemoryOrder module listnext_mod: mov esi, [edx+0x28] // get pointer to modules name (unicode string) push byte 24 // push down the length we want to check pop ecx // set ecx to this length for the loop xor edi, edi // clear edi which will store the hash of the module nameloop_modname: xor eax, eax // clear eax lodsb // read in the next byte of the name cmp al, 'a' // some versions of Windows use lower case module names jl not_lowercase sub al, 0x20 // if so normalise to uppercasenot_lowercase: ror edi, 13 // rotate right our hash value add edi, eax // add the next byte of the name to the hash loop loop_modname // loop until we have read enough cmp edi, 0x6A4ABC5B // compare the hash with that of KERNEL32.DLL mov ebx, [edx+0x10] // get this modules base address mov edx, [edx] // get the next module jne next_mod // if it doesn't match, process the next module

Yup each of the 3 lists (PEB->Ldr.InLoadOrderLinks, PEB->Ldr.InMemoryOrderLinks and PEB->Ldr.InInitializationOrderLinks) point into a _LDR_DATA_TABLE_ENTRY structure[1]. But they point into the same structure at a different place:

The PEB->Ldr.InLoadOrderLinks entries point to the beginning of the InLoadOrderModuleList list in the _LDR_DATA_TABLE_ENTRY structure, the PEB->Ldr.InMemoryOrderLinks entries point to the begining of the InMemoryOrderModuleList list in the _LDR_DATA_TABLE_ENTRY structure and the PEB->Ldr.InInitializationOrderLinks entries point to the beginning of the InInitializationOrderModuleList list in the _LDR_DATA_TABLE_ENTRY structure, so using each different PEB->Ldr list puts the offset to the base address off by a certain ammount.

This is why the offset for the base address is 0x10 when we reference the _LDR_DATA_TABLE_ENTRY structure via the PEB->Ldr.InMemoryOrderModuleList and 0x8 when we go via PEB->Ldr.InInitializationOrderModuleList.