Now that we have covered how payloads work,
we need to understand this concept of reflective DLL injection,
which is used extensively by Meterpreter.
DLL files are normally located on the file system.
They can be loaded at runtime by calling a load library API or
dynamically by the operating system's loader and dynamic linker
when a process is being created.
The operating system does not officially support loading DLLs from memory.
The Reflective DLL injection library used by
Meterpreter provides this functionality.
It will perform all the operations required to load a DLL file
into a process, just as the operating system's loader would if
it was loading a DLL from disk.
This makes the payload very evasive because the only way to detect the
actual payload is by analyzing memory at runtime,
since it is never written to disk.
You may recall that when we compiled the payload,
there were multiple DLL files in the output folder.
These files are all extensions which can expand
Meterpreter's functionality at runtime.
These extensions are also reflectively loaded and are sent over by the handler.
If we go back to the Meterpreter solution in Visual Studio,
we can take a closer look at what this reflective
DLL injection library looks like.
The code is located in the ReflectiveLoader.c file in the
external dependencies of the metsrv project.
If we open the file and scroll down to the ReflectiveLoader function,
we will see several variable declarations.
If we scroll down, we can view the first step,
which consists of obtaining the caller's address.
You may recall that the caller was the stub of shell code which
was patched in the DOS header by the handler.
The code then iterates backwards to find the payload's base address
in memory by using the well‑known MZ ASCII string,
which corresponds to the first two bytes of all DLL files.
The library then uses the same technique which is used by the
stager shell code we saw earlier to locate APIs.
It accesses the Process Environment Block and iterates over the linked list
of modules, computing the ROT13 hash of each module until it finds one which
matches the precomputed ROT13 hash of kernel32.
Once the base address of kernel32 has been found,
it accesses its export table and loops over all the module's exported functions,
computing the ROT13 and comparing it with the
precomputed hashes of APIs which are required.
It keeps iterating until all the APIs have been found.
The APIs in question are LoadLibraryA, GetProcAddress, and VirtualAlloc.
It repeats this process for APIs in Ntdll,
which are also required, and then moves on to allocate some memory,
in which it will load the payload.
It does this using the pointer to the VirtualAlloc API,
which was resolved previously.
It then starts to copy the headers and sections into the allocated memory.
It then imports the payload's dependencies using the LoadLibraryA API, similar
to how the stager loaded the WinINet module so that it could communicate over
HTTP. Once all the dependencies have been loaded,
it moves on to process relocations.
When the DLL file is compiled, a preferred base address is added to the file.
If the memory of the DLL's preferred base address is already in use,
the operating system will load the DLL at a different base address,
which means that some parts of the code need to be
updated to account for this change.
Once these relocations have been processed,
the entry point,
which, in the case of our DLL, is DllMain, is returned to the caller,
which, if you recall,
is the stub of shell code which was patched in the DOS
head of the payload by the handler.
And as we saw previously,
it does indeed call DllMain, which initializes the Meterpreter shell.