Click here to Skip to main content
15,880,651 members
Articles / Programming Languages / ASM
Tip/Trick

Loading Win32/64 DLLs "manually" without LoadLibrary()

Rate me:
Please Sign up or sign in to vote.
4.97/5 (31 votes)
8 Mar 2014CPOL5 min read 254.9K   8.1K   107   61
How to load DLLs by allocating memory and loading the DLL from file/memory and then relocating/importing.

Introduction

Sooner or later many people start thinking about loading a DLL without LoadLibrary(). OK, maybe not so many... It has only a few advantages and can introduce lots of inconvenience problems when coding the DLL (depending on what your DLL does) compared to a situation where you load the DLL with an ordinary LoadLibrary() call, so this technique has limited use. (I will aim the inconvenience problems below.) Still this tip can make good service as a tutorial if you want to understand what's going on behind the curtains... I myself used this stuff to write DLLs in C/C++ instead of coding offset independent assembly (in an anticheat engine), but that is another story.

Implementation

The most important steps of DLL loading are:

  1. Mapping or loading the DLL into memory.
  2. Relocating offsets in the DLL using the relocating table of the DLL (if present).
  3. Resolving the dependencies of the DLL, loading other DLLs needed by this DLL and resolving the offset of the needed functions.
  4. Calling its entrypoint (if present) with the DLL_PROCESS_ATTACH parameter.

I wrote the code that performed these steps but then quickly found out something is not OK: This loaded DLL doesn't have a valid HMODULE/HINSTANCE handle and many windows functions expect you to specify one (for example, GetProcAddress(), CreateDialog(), and so on...). Actually the HINSTANCE handle of a module is nothing more than the address of the DOS/PE header of the loaded DLL in memory. I tried to pass this address to the functions but it didn't work because windows checks whether this handle is really a handle and not only the contents of memory! This makes using manually loaded DLLs a bit harder! I had to write my own GetProcAddress() because the windows version didn't work with my DLLs. Later I found out that I want to use dialog resources in the DLL and CreateDialog() also requires a module handle to get the dialog resources from the DLL. For this reason I invented my custom FindResource() function that works with manually loaded DLLs and it can be used to find dialog resources that can be passed to the CreateDialogIndirect() function. You can use other types of resources as well in manually loaded DLLs if you find a function for that resource that cooperates with FindResource(). In this tip you get the code for the manual DLL loader and GetProcAddress(), but I post here the resource related functions in another tip.

Limitations

  1. The loaded DLL doesn't have a HMODULE so it makes life harder especially when its about resources.
  2. The DllMain() doesn't receive DLL_THREAD_ATTACH and DLL_THREAD_DETACH notifications. You could simulate this by creating a small DLL that you load with normal LoadLibrary() and from the DllMain() of this normally loaded DLL you could call the entrypoint of your manually loaded DLLs in case of DLL_THREAD_ATTACH/DLL_THREAD_DETACH.
  3. If your DLL imports other DLLs, then the other DLLs are loaded with the WinAPI LoadLibrary(). This is actually not a limitation, just mentioned it for your information. Actually it would be useless to start loading for example kernel32.dll with manual dll loading, most system DLLs would probably disfunction/crash!
  4. DLLs that make use of SEH *may* fail. The fact that the DLL contains SEH related code alone isn't a problem but the __try blocks in the loaded DLL won't be able to catch the exceptions because the ntdll.dll!RtlIsValidHandler() doesn't accept exception handler routines from the memory area of our manually loaded DLL (because this memory area isn't mapped from a PE file). This is a problem only if an exception is raised inside a __try block of the DLL (because windows can't run the exception handler of the DLL and raises another exception that escapes the exception handler of the DLL - the result is usually a crash).
  5. Whether the CRT works with manual DLL loading or not depends on several things. It depends on the actual CRT version you are using and the functions you call from the CRT. If you are using just a few simple functions (like printf) then the CRT may work. I've written my DLLs with /NODEFAULTLIB linker option that means you can't reach CRT functions and it reduces your DLL size considerably (like with 4K intros). But then you have to go with pure WinAPI! This can be quite inconvenient but you can overcome this by writing your own mini CRT. I've provided one such mini CRT in my C++ example without attempting to be comprehensive but it at least allows you to use the most basic C++ features: automatically initialized static variables, new/delete operators. BTW, if you are about to use this code then you should understand most of these problems and you should appreciate that writing C/C++ DLL without CRT is still much more convenient than writing something as an offset independent or relocatable assembly patch.

Using the code

Write your DLL in C/C++. If you don't want to use the default CRT then link with /NODEFAULTLIB. It's a good practice to write your code so that a define can switch between original windows LoadLibrary() and the manual loader I provided (in my example solution you can do this by switching to the "Debug LoadLibrary" configuration). This way you can easily test whether a bug or crash happens because of manual loading or not and an additional benefit of being able to switch to the original windows LoadLibrary() is that you can debug the code of the DLL only that way (as the debugger identifies code only inside normally loaded modules).

With my manual loader you have to use my custom GetProcAddress() on the loaded DLL. If you want to use dialog resources (or some other kind of resource, but dialog is the most common) then you can use the FindResource() function I provided in one of my other tips (and the CreateDialogIndirect WinAPI function) because that works with both normally and manually loaded DLLs: The inner working of FindResource() and LoadString() Win32 functions. Download the attached VC++2010 solution that contains a sample program that loads and uses 2 DLLs. One DLL has been written in C and the other in C++.

History:

  • Around 2000: The first spaghetti version of this code has born (Win32-only).
  • 2012 July: Publication on codeproject in its original form.
  • 2013 Sept: De-spaghettization of the code, adding 64 bit support and an example VC++2010 solution with DLL loading example with DLLs written in C and C++. The C++ DLL contains a mini home grown CRT.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
AnswerRe: for getprocaddress etc to work Pin
pasztorpisti19-Apr-14 5:37
pasztorpisti19-Apr-14 5:37 
AnswerRe: for getprocaddress etc to work Pin
Elmue4-Jul-20 19:45
Elmue4-Jul-20 19:45 
GeneralRe: for getprocaddress etc to work Pin
evlncrn84-Jul-20 23:01
evlncrn84-Jul-20 23:01 
AnswerRe: for getprocaddress etc to work Pin
Elmue5-Jul-20 3:37
Elmue5-Jul-20 3:37 
QuestionRe: for getprocaddress etc to work Pin
Elmue9-Jul-20 9:26
Elmue9-Jul-20 9:26 
QuestionIt doesn't work on 64bit Pin
TinnyTonny16-Apr-14 15:51
TinnyTonny16-Apr-14 15:51 
AnswerRe: It doesn't work on 64bit Pin
pasztorpisti17-Apr-14 4:44
pasztorpisti17-Apr-14 4:44 
AnswerRe: It doesn't work on 64bit Pin
Elmue4-Jul-20 19:47
Elmue4-Jul-20 19:47 
I did not test this project.
But the code for loading 64 DLLs may require an additional step which is not implemented in this project. This is required when the DLL contains the directory IMAGE_DIRECTORY_ENTRY_EXCEPTION for safe Structured Exception Handling (SafeSEH).

But this project works also for 64 bit: GitHub - fancycode/MemoryModule: Library to load a DLL from memory.[^]

modified 5-Jul-20 9:40am.

BugLoadDLL_ResolveImports Pin
jiang.jianjun7-Mar-14 15:33
jiang.jianjun7-Mar-14 15:33 
GeneralRe: LoadDLL_ResolveImports Pin
pasztorpisti8-Mar-14 1:04
pasztorpisti8-Mar-14 1:04 
Questionhow to debug the crash dll Pin
396963622-Nov-13 1:09
396963622-Nov-13 1:09 
AnswerRe: how to debug the crash dll Pin
pasztorpisti22-Nov-13 4:30
pasztorpisti22-Nov-13 4:30 
Question16 bits dlls Pin
Member 1024821724-Sep-13 12:11
Member 1024821724-Sep-13 12:11 
AnswerRe: 16 bits dlls Pin
pasztorpisti24-Sep-13 12:19
pasztorpisti24-Sep-13 12:19 
GeneralInteresting - well done Pin
Espen Harlinn22-Sep-13 13:40
professionalEspen Harlinn22-Sep-13 13:40 
QuestionLoading from memory Pin
Michael Haephrati22-Sep-13 9:24
professionalMichael Haephrati22-Sep-13 9:24 
AnswerRe: Loading from memory Pin
Randor 23-Sep-13 20:37
professional Randor 23-Sep-13 20:37 
GeneralRe: Loading from memory Pin
pasztorpisti23-Sep-13 22:29
pasztorpisti23-Sep-13 22:29 
GeneralMy vote of 5 Pin
Michael Haephrati22-Sep-13 9:17
professionalMichael Haephrati22-Sep-13 9:17 
QuestionExcellent Article Pin
KozmoKAllDay4-Sep-13 19:10
KozmoKAllDay4-Sep-13 19:10 
AnswerRe: Excellent Article Pin
pasztorpisti4-Sep-13 21:39
pasztorpisti4-Sep-13 21:39 
AnswerRe: Excellent Article Pin
pasztorpisti5-Sep-13 22:43
pasztorpisti5-Sep-13 22:43 
QuestionWarning Pin
Shawn-USA5-Aug-13 14:30
Shawn-USA5-Aug-13 14:30 
AnswerRe: Warning Pin
pasztorpisti5-Aug-13 22:19
pasztorpisti5-Aug-13 22:19 
GeneralRe: Warning Pin
Shawn-USA6-Aug-13 7:41
Shawn-USA6-Aug-13 7:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.