Injecting DLL into process on load
If you ever had the need to inject DLL into a process right before it starts executing, you should have experienced many headaches in the process.
You should have tried injecting the DLL by creating the process CREATE_SUSPENDED, and have failed miserably. There are a few reasons to this. When a process is CREATE_SUSPENDED, many process state and environment structures arent initialized yet. The process main thread is supposed to initialize them and by introducing your thread at this early stage, there are many things that can go wrong. You, or the Windows API youve called might be reading structures that doesnt exist yet. You might run into a deadlock as your thread and process main thread each trying to fight for the loader lock. But the end result is same, you cant successfully inject your DLL into the process.
There are quite a few others have came up with the solution, but I guess my workaround is much easier.
1. Create your target process CREATE_SUSPENDED.
2. Patch the process entry point with 0xEBFE (JMP $-2, infinite jump to itself). Dont forget to save the original bytes of course.
3. Resume the main thread.
4. Poll the main thread EIP and see if it reached the EP already. If not, wait for a while and poll again.
5. Inject your DLL.
6. Suspend the main thread, restore the original EP bytes, resume.
Here is a snippet from my unreleased injector.
void Inject_Loader( const DllPayload& Payload, const std::string& Path )
{
STARTUPINFOA StartupInfo = {0};
PROCESS_INFORMATION ProcessInformation;
// initialize the structures
StartupInfo.cb = sizeof(StartupInfo);
// attempt to load the specified target
if ( CreateProcessA(
Path.c_str(),
NULL,
NULL,
NULL,
FALSE,
CREATE_SUSPENDED,
NULL,
NULL,
&StartupInfo,
&ProcessInformation
) )
{
Handle hProcess( ProcessInformation.hProcess );
// wait for the process to done
try
{
// locate the entry point
OptionalHeader optionalheader = PortableExecutable::FromFile( Path.c_str() ).NtHeaders.OptionalHeader;
LPVOID entry = (LPVOID)(optionalheader.ImageBase + optionalheader.AddressOfEntryPoint);
// patch the entry point with infinite loop
PageProtect protect( hProcess, entry, 2, PAGE_EXECUTE_READWRITE );
std::string oep = VMemory::Read( hProcess, entry, 2 );
VMemory::Write( hProcess, entry, "\xEB\xFE" ); // JMP $-2
// resume the main thread
ResumeThread( ProcessInformation.hThread );
// wait until the thread stuck at entry point
CONTEXT context;
for ( unsigned int i = 0; i < 50 && context.Eip != (DWORD)entry; ++i )
{
// patience.
Sleep(100);
// read the thread context
context.ContextFlags = CONTEXT_CONTROL;
GetThreadContext( ProcessInformation.hThread, &context );
}
if ( context.Eip != (DWORD)entry )
{
// wait timed out
throw "entry point blockade timed out";
}
// inject DLL payload into remote process
Inject_CreateRemoteThread( Payload, hProcess );
// pause and restore original entry point
SuspendThread( ProcessInformation.hThread );
VMemory::Write( hProcess, entry, oep );
// you are ready to go
ResumeThread( ProcessInformation.hThread );
}
catch ( ... )
{
// terminate the newly spawned process
TerminateProcess( hProcess, -1 );
// rethrow the exception to top-level handler
throw;
}
}
else
{
// are you sure this is a valid target ?
throw "unable to load the specified executable";
}
}
I wasnt the first one to figure this out, but Matt had gone AWOL for so long I had to suspect California actually passed a law banning all WEPs. :/
This looks like a great idea…
When are you going to release the full source code?
I’d be interested in the “VMemory” and “PortableExecutable” classes…
They are just glorified and overengineered (think RAII and CRTP
) wrapper for usual stuff like ReadProcessMemory, WriteProcessMemory, and PE header parsing.
It will be released when I clean them up a bit and migrate all my code to github.