11

So i just read a little bit about how one would go about for injecting a dll into a running program on Wikipedia (the CreateRemoteThread idea). I followed the steps described and eventually got it working. The thing i found interesting though which took some time to figure out are the following: When creating my remote thread and sending in the function i would like to be run as the first/starting one i hit a snag, when it was run it failed to call the proper functions, they seemed to turn into rubbish when i looked at them in OllyDBG which in turn resulted in the program crashing down on me. The code i used then was something along these lines:

static DWORD __stdcall inject(LPVOID threadParam)
{
    MessageBoxA(NULL, "test", "test", NULL);
    LoadLibrary("my.dll");
    return 0;
}

And somewhere else:

CreateRemoteThreadEx(hProcess, NULL, 0, LPTHREAD_START_ROUTINE(fnPageBase), dbPageBase, 0, NULL, &threadId);

Where fnPageBase is the memory I've allocated in the to be injected process for my function and dbPageBase the memory I've allocated for a struct that is passed as the LPVOID threadParam.

Something like that, the problem was that both MessageBoxA and LoadLibrary didn't get a proper address it would seem, when i checked them in OllyDBG they always pointed to something that didn't exist. I googled around a little and found out that i should be using GetProcAddr to get a address to ie: LoadLibrary which i could later use by sending in some data via the LPVOID threadParam in my inject() call. So my question is: Why does it work when i use the GetProcAddr and not when I just try to use it "normally"? Do I get some specific address that's always mapped in for everyone in the same region in memory when using that?

Also, what happens to my strings in the inject() function? Are they moved to some other place during compile which makes them unavailable to the program i'm injecting since it's in a totally different place of the memory (i.e., it's not mapped to there?)? I worked that around by sending that along in a struct with the LPVOID threadParam aswell in a struct that i had copied over to memory available to the .exe I was injecting.

If you need more info on how I did the other parts please do tell and I'll update.

1 Answer 1

9

One thing you need to keep in mind is that code in your process and the code in the target process reside in different address spaces. So any address in your program is not necessary valid in the target process and vice versa.

This means the code that you inject cannot make any assumptions about addresses of functions or variables. Even your inject function's address is valid only in your process; to make it available in the target process you'd have to: 1) copy the code there; and 2) make sure any functions or memory addresses it refers to are valid in the new address space.

That's why the normal approach used with CreateRemoteThreadEx is to copy the DLL name to the target process and create the thread using the address of the LoadLibrary function:

// 1. Allocate memory in the remote process for szLibPath
pLibRemote = ::VirtualAllocEx( hProcess, NULL, sizeof(szLibPath),
                               MEM_COMMIT, PAGE_READWRITE );

// 2. Write szLibPath to the allocated memory
::WriteProcessMemory( hProcess, pLibRemote, (void*)szLibPath,
                      sizeof(szLibPath), NULL );    

// Load "LibSpy.dll" into the remote process
// (via CreateRemoteThread & LoadLibrary)
hThread = ::CreateRemoteThread( hProcess, NULL, 0,
            (LPTHREAD_START_ROUTINE) ::GetProcAddress( hKernel32,
                                       "LoadLibraryA" ),
             pLibRemote, 0, NULL );

(snippet from Code Project)

You can see that pLibRemote (with the address of the DLL name in the target process) is passed as the parameter to the thread routine. So the result of this is equivalent to:

LoadLibraryA(pLibRemote);

executed in the target process.

Strictly speaking, this is not guaranteed to work because the address of LoadLibraryA in your process is not necessarily the same as LoadLibraryA in the other process. However, in practice it does work because system DLLs like kernel32 (where LoadLibraryA resides) are mapped to the same address in all processes, so LoadLibraryA also has the same address in both processes.

8
  • So theoretically we could go wrong and not get a proper address even when using GetProcAddr? Also, why wouldn't &LoadLibraryA give a good address aswell if they're all mapped into the same region? Should this be posted somewhere else as a separate question or is it okay to ask here?
    – lfxgroove
    Sep 15, 2013 at 8:46
  • &LoadLibraryA may return a pointer to the import stub in your process and not the final address in kernel32.dll.
    – Igor Skochinsky
    Sep 15, 2013 at 12:30
  • I thought that nowadays with process space randomization you cannot even count on kernel32 being mapped at the same address... does it still work in win7/8 too? Sep 16, 2013 at 13:18
  • @LorenzoDematté: AFAIK load address of kernel32 is randomized on each boot but it remains constant afterwards.
    – Igor Skochinsky
    Sep 16, 2013 at 13:38
  • 1
    @0xC0000022L: I did not set out to write a detailed guide to DLL injection covering every single corner case, I just tried to answer the question stated (and added a little background). If I made any specific mistakes, please point them out.
    – Igor Skochinsky
    Sep 17, 2013 at 20:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.