DLL Injection Theory
When a process needs to use an API from a DLL,
it calls the LoadLibrary API to load it into virtual memory space.
In our case, we want the remote process to load our DLL using Win32 APIs.
Unfortunately, LoadLibrary can not be invoked on a remote process,
so we’ll have to perform a few tricks to force a process like explorer.exe
to load our DLL.
The MSDN function prototype of LoadLibrary
reveals that the function only requires one parameter:
the name of the DLL to load.
Our approach will be to try to trick the
remote process into executing LoadLibrary with the correct argument.
When calling CreateRemoteThread,
the fourth argument is the start address of the function
run in the new thread
and the fifth argument is the memory address of a buffer
containing arguments for that function.
The idea is to resolve the address of LoadLibrary inside the remote process
and invoke it while supplying the name of the DLL we want to load.
If the address of LoadLibrary is given as the fourth argument to CreateRemoteThread,
it will be invoked when we call CreateRemoteThread.
In order to supply the name of the DLL to LoadLibrary,
we must allocate a buffer inside the remote process
and copy the name and path of the DLL into it.
The address of this buffer can then be given
as the fifth argument to CreateRemoteThread,
after which it will be used with LoadLibrary.
However, there are several restrictions we must consider.
First, the DLL must be written in C or C++
and must be unmanaged.
The managed C#-based DLL we have been working with so far will not work
because we can not load a managed DLL
into an unmanaged process.
Secondly, DLLs normally contain APIs
that are called after the DLL is loaded.
In order to call these APIs, an application would first have to “resolve”
their names to memory addresses
through the use of GetProcAddress.
Since GetProcAddress cannot resolve an API in a remote process,
we must craft our malicious DLL in a non-standard way.
Let’s take a moment to discuss this approach.
As part of its functionality, LoadLibrary calls the DllMain function inside the DLL,
which initializes variables and signals that the DLL is ready to use.
Typically, DllMain performs different actions based on the reason code argument
that indicates why the DLL entry-point function is being called.
We can see this in this unmanaged DllMain code.
As stated in the MSDN documentation, the DLL_PROCESS_ATTACH reason code
is passed to DllMain
when the DLL is being loaded into the virtual memory address space
as a result of a call to LoadLibrary.
This means that instead of defining our shellcode as a standard API
exported by our malicious DLL,
we could put our shellcode within the DLL_PROCESS_ATTACH switch case,
where it will be executed when LoadLibrary calls DllMain.
To use this technique, we either have to write
and compile a custom unmanaged DLL in C or C++
that will execute shellcode when the DllMain function is called,
or use a framework to generate one.
Since C and C++ programming is outside the scope of this module,
in the next section, we’ll use a framework
to generate a Meterpreter DLL with msfvenom,
leveraging this technique.