Bootstrapping
Last updated
Last updated
Before, we used GetModuleHandle and GetProcAddress to find the addresses of functions and call them. It is possible to not call any of these and get the address of the functions like how bootstrappers in shellcode work.
This is possible due to the fact that:
The PEB address is located at theGS
register: GS:[0x60]
_PEB_LDR_DATA
structure which contains information about the loade modules is located at $PEB:[0x18]
The loader structure contains a pointer to InMemoryOrderModuleList
at offset 0x20
InMemoryOrderModuleList
is a doubly linked list of LDR_DATA_TABLE_ENTRY
structures, each contains BaseDllName
and DllBase
of a single module
We can then browser all loaded modules and find kernel32.dll and it's base address
with the knowledge of its location in memory, we can find its export directory and browser for the GetProcAddress function
We can then use GetProcAddress to find the addresses of other functions
Let's open notepad in windbg and see how this works.
Let's first get the address of the PEB with:
And then overlay the PEB structure to the memory pointed to be peb to see the what the structures members are holding/pointing to:
The field we are interested in is the Ldr field:
Lets click in the highlighted Ldr, which should take you to a structure called PEB_LDR_DATA:
We then have 3 lists listed in the structure, we are interested in the second called, "InMemoryOrderModuleList". Let's click the highlighted name:
And we see that its a double linked list, this is a chain of pointers which points backwards and forwards to structures. This will loop, meaning if we parse the pointers going from structure to structure, we will end up going back to where we started.
To parse this, we can use the LDR DATA TABLE ENTRY structure to do so:
And now lets issue the following command in windbg
The most important thing about this structure is the DllBase field in which we will see later on.
Lets now take the Flink address of the LIST ENTRY structure and parse that in the LDR DATA TABLE ENTRY structure
Now, lets click the hext highlighted flink address, copy that address and parse it like how we did in the above:
As we can see, the FullDllName field holds the string, "ntdll.dll"
...
If we keep on getting the flink addreses, and parsing it through this structure, we will eventually get back to the first structure which in our case had the FullDllName to be "notepad.exe".
The above showed us how to parse through the PEB and get the DllBase addresses of the modules.
We can also see a pointer to the PEB in the TEB structure like so(which is located at offset 0x060):
Note that if is 32 bit, it would be at offset 30, but since we are using 64 bit, the offset is 60.
Lets make a code implementation of the above.
The above code retrieves a pointer to the PEB by retrieving what's at offset 0x60 from the gs register. Note that it is offset 0x30 for 32 bit and we read from the fs register instead of the gs register. We store the address of PEB at ProcEnvBlk.
We can then start parsing the structures to search for memory addresses and such.
The above stores pointer to the necessary data structures we are going to use to resolve the address of the functions.
The above iterates though all entries of the double linked list and compares the BaseDLLName to kernel32.dll, if it matches, it will assign kernel32Address to the DLLBase address of the dll.
After we get our dll base address, we will have to find its export directory, we will use some pointer arithmetic to get pointers to certain data structures.
We find ImageDosHeader from the base address
With a pointer to the DosHeader, we can then find the e_lfanew field which stores the RVA address of NtHeader, we then add that to the Base address to find its actual location in memory
With a pointer to NtHeader, we can find OptionalHeader
With the OptionalHeader, we can find the DataDirectory which holds a pointer to the export directory's virtual address, we then add that to the base address to find its actual location in memory
We then get the pointer to the EAT's
AddressOfFunctions
AddressOfNames
AddressOfNameOrdinals
We will then go through the Export directory using a for loop, and compare their function names extracted from the table with GetProcAddress and GetModulehandle.
Here, we get the function's virtual address by adding the RVA and the base address of the function and store it in a pointer if the comparison succeeds.
Now that we have the addresses of the GetModuleHandle and GetProcAddress functions, we can use them to call any function we want. Lets do this by calling VirtualAlloc:
This was a simple lab which showed you how to call functions without GetProcAddress, GetModuleHandle or the actual function showing up in the import table by using the PEB structure.