Mildly Hurtful Sarcasm

Meaningless ranting, just like everybody else.

Friday, November 24, 2006

Code injection at process creation

Many articles have talked about DLL injection using CreateRemoteThread() and LoadLibrary(). But this technique does not always work with newly created processes when some system DLLs have not fully initialized. After some digging, I realized that the proper way to achieve such is to use the technique of overwriting entry point of a the newly created process, let me lay it out here.

First, the new process is created with the CREATE_SUSPEND flag:

CreateProcess(lpApplicationName, lpCommandLine,
lpProcessAttributes,
lpThreadAttributes, bInheritHandles,
CREATE_SUSPENDED,
lpEnvironment, lpCurrentDirectory,
lpStartupInfo, lpProcessInformation);

Next step is to obtain the entry point of the process. When a process is newly created and suspended, EAX points to its entry point. To obtain EAX, get the suspended thread context:

HANDLE hThread = lpProcessInformation->hThread;
CONTEXT context;
ZeroMemory(&context, sizeof(context));
context.ContextFlags = CONTEXT_FULL;
GetThreadContext(hThread, &context);

Voi la, context.Eax now holds the entry address. But before we can detour the execution flow to our code, we have to write our stub in this process' address space: allocate executable memory (or DEP will complain!) and write your stub to it, your stub that does your stuff, load a DLL, what not:

HANDLE hProcess = lpProcessInformation->hProcess;
LPVOID pMem = VirtualAllocEx(hProcess, 0, dwStubSize,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, pMem, pStub,
dwStubSize, ...);

Now, we are ready to overwrite the entry point, first, save the first few instructions:

BYTE savedBuf[6];
ReadProcessMemory(hProcess, context.Eax,
savedBuf, sizeof(savedBuf), ...);

Then overwrite the entry point with these instructions:

__asm jmp pMemOffset
WriteProcessMemory(hProcess, context.Eax, insts, sizeof(insts), ...);

where insts is the byte code of the jump instruction and pMemOffset is offset into your stub's address (pMem).

Lastly, you want this process to execute as originally intended, for that you need to restore the saved instructions, and jump back, so your stub will end with something like:

WriteProcessMemory(GetCurrentProcess(), context.Eax,
savedBuf, sizeof(savedBuf), ...);
__asm push context.Eax
__asm ret

There you have it, a piece of code that will do something before process execution. Sure your stub is still preceeded with some system intialization code; this technique does not gaurantee that your stub is executed absolutely before everything, but it's reasonably simple and a good start.

Labels: ,

0 Comments:

Post a Comment

<< Home