Shellcode commonly uses a method to resolve Windows API functions by traversing through the Portable Environment Block (PEB) to find Kernel32’s base address. This is done so shellcode remains position independent while still having the ability to call LoadLibraryA and GetProcAddress to resolve other dlls and functions. The PEB has a_PEB_LDR_DATA structure that contains three linked lists of DLLs that are loaded in a processes memory space. The three linked lists are
Order a module is loaded into process. *.exe is always first.
Order in which DLLs appear in memory. ASLR changes this.
Order of when the DllMain fires.
The following Volatility Volshell output shows the location of the _PEB_LDR_DATA in the PEB and the three lists for a 32 bit system
The Ldr structure is at offset 0xc in the PEB and the InMemoryOrderModuleList is at offset 0x14 from the _PEB_LDR_DATA.
The InMemoryorderModuleList is the most widely used linked list, that I have seen, to resolve the base address of Kernel32. Harmony Security has a write up of how to traverse the InMemoryorderModuleList on Windows 7 to allow shellcode to be more robust across Windows platforms.
While the method Harmony Security mentions holds true for most applications and Windows environments, if shellcode is injected into a .NET process this method will not work.
Shellcode payloads, especially ones from MetaSploit, rely on Kernel32.dll being the third entry in the InMemoryorderModuleList. However, in a .NET process, Kernel32.dll is the 4th entry as Mscoree, the .NET bootstrapper for the default CLR host, is loaded before Kernel32.
Because of this, shellcode written for .NET applications needs to walk the _LIST_ENTRY structure for the Ldr lists to the 4th entry instead of the 3rd.
Fortunately, this is an easy adjustment in shellcode as the change is a one-liner!
In 32 bit code the traversal is as follows
To perform the same action in a .NET process, go to the 4th entry in the InMemoryOrderModuleList as seen below.
This trick will also work with 64 bit .NET applications, although the offsets are different.
While developing shellcode, be mindful of the platform and application you are injecting into. If you are going up against .NET, it is important to know where Kernel32 is in the Ldr lists so that its base address can be resolved.