It took me quite a lot to get all the code working and running and, even if there are some bugs to be solved yet, the code seems pretty usable. So, here I go explaining the LoadLibrary part of this library: I won’t go too much in details about how this thing work (even if I’ll post here the link to the site I took the documentation from), but I’ll write instead the interesting and problematic points I’ve faced in writing the code.
The most of the documentation I needed to write this was taken from this wonderful article written by Joachim Bauch: here is reported all the needed information needed to handle the PE format when loading a module in memory. Well, the PE loading process can be split in some main points:
- Opening the file
- Allocating enough space in memory, possibly at the image base address specified in the optional header
- Copy all the sections in memory
- If it hasn’t been possible to allocate space at the specified base address, proceed with address relocation
- Resolve all the imports needed by the module
- Adequately protect the different sections
- Call the module entry point
Maybe this list is a little bit scaring at the beginning, but it ain’t so bad and meaningless after beginning to write the code.
Opening the file
This can be simply done by calling the CreateFile function with read permissions and then calling the CreateFileMapping and the MapViewOfFile functions in order to open the content of the file. Once the mapping is finished, the mapping and the file handles can be both closed.
Allocating the memory
By analyzing the ImageBase and the SizeOfImage fields of OptionalHeader, it is possible to discover the address where the module should be loaded and the size of memory needed to contain all the file data. Here came the first problem. Almost all the re-implementations of the LoadLibrary function do this by using the VirtualAlloc function; anyway, there are some little big things called shared sections that would not work when using this method. Shared sections are particular kind of sections shared between all the processes allowing IPC via this area. I’ve solved this problem (for now, as I couldn’t be able to find a better one) by mapping the file using the MapViewOfFileEx API, which is very very similar to the MapViewOfFile function, but it contains a new parameter called lpBaseAddress, which specifies at which address the mapping should be made.
After doing this, the mapping handle can be closed.
First thing to do is to copy the initial headers to the lpBaseAddress address: the size of the region of data to be copied is specified in pNTHeader->OptionalHeader.SizeOfHeaders. The rest of the sections can be easily copied by using a for loop over the section info present in the FileHeader header:
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNTHeader); for(int i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++, pSection++) memcpy((PBYTE)baseDll + pSection->VirtualAddress, (PBYTE)baseAddress + pSection->PointerToRawData, pSection->SizeOfRawData);
If the MapViewOfFileEx function has failed to map the file at the lpBaseAddress and it has been necessary to map the file to another address, then it is necessary to relocate all the addresses’ referements in the code. This has been implemented in the way described in the already cited guide. Anyway, even if there are some type of relocation possible, according to the Matt Pietrek guide to the PE format:
“For PE files that run on Intel CPUs, you’ll only see two types of relocations”
and those are IMAGE_REL_BASED_ABSOLUTE and IMAGE_REL_BASED_HIGHLOW.
Even this one has been implemented almost in the same way of Bauch. But there is one thing to say here, as it represent the second big problem I had to face with. In Windows 7 Microsoft has decided to make a total API code “refactoring” due to the introduction (began in Vista) of the MinWin component. A lot of new DLLs with the name “api-ms-win-core-XXX” has been introduced, but all the functions exported by these DLLs do just one thing: returning TRUE. Though, they are imported by other DLLs to indirectly access to the kernel32.dll functions: in fact, a lot of the functions exported by kernel32 have been regrouped in categories and fakely exported by those DLLs. But, if during the imports resolving phase I resolve the API address with the one specified in the api-ms-win-core DLL, I will just have so many API callings just returning TRUE values. This problem is solved by redirecting this resolving directly to kernel32.dll and using that one as the imported DLL. Maybe it’s a bad solution, but it works.
Third big problem has been “what happens if the loaded DLL is trying to import the LoadLibrary & family function ?”. In fact, if the module tries to dynamically load with the original LoadLibrary one DLL we already loaded, I will have two copies of the same DLL in memory. The same if the module wants to retries the address of a module I already loaded. This means that I had to fix the following functions imports:
They are resolved by letting them to point at my versions. Obviously, if the module tries to load other modules by not using those functions, everything can happen. So, that’s why I advice to use just this method to load DLLs and/or the original one and not mixing a lot of things together.
Nothing special here, implemented in the Bauch way. Just calling the VirtualProtect function to apply the different protection values to the different sections.
Calling the entry point
Even here, nothing special. The entry point address is specified in pNTHeader->OptionalHeader.AddressOfEntryPoint, so this part is made of just a call to that address.
Some flags from the original LoadLibraryEx has been implemented:
- LOAD_LIBRARY_AS_DATAFILE: if this value is used, the system maps the file into the calling process’s virtual address space as if it were a data file. Nothing is done to execute or prepare to execute the mapped file. (from MSDN)
- DONT_RESOLVE_DLL_REFERENCES: if this value is used, and the executable module is a DLL, the system does not call DllMain for process and thread initialization and termination. (from MSDN)
Hope to have said it all. It has not been easy to implement almost everything. I am sure there are a lot of bugs here and there, but in the next post I’ll describe the FreeLibrary implementation and I will insert the link to the github repository where I am hosting the code.
Feel free to comment.