6

I have written a DLL-Injector in C++ recently, for which the requirements were the following

  • The INJECTING PROCESS (let's call it the 'Injector') as well as the DLL TO BE INJECTED (Injection) exist in 64 and 32 bit variants. Depending on the target, the matching version of the injection is tried to be injected.
  • It must be possible to inject target processes that are 32 bit (WOW64) even with the Injector running in 64 bit

It came quickly to my notice, that the call of GetProcAddress("LoadLibraryA") in the Injector returns an "unusable" handle as the 32 bit target has another kernel32.dll loaded and the address of the function there is different, so injection fails (The remote thread can not be started using the returned address/handle). Furthermore, the 32 bit process has the kernel32.dll loaded at a different base address, which makes creation of the remote thread even more impossible.

To make clear what I mean, the following happens:

  • Injector has 64 bit version of kernel32.dll loaded at 0x12340000
  • Injector retrieves handle for LoadLibraryA 0x00005678 from this kernel32.dll
  • Target has 32 bit version of kernel32.dll loaded at 0xABCD0000
  • The handle for LoadLibrary of this kernel32.dll is expected to be 0x0000EFAB
  • Injector tries to start remote thread in target with function 0x12345678, but 0xABCDEFAB is expected

When injecting a 64 bit process from a 64 bit process, and 32 bit from 32 bit, there is usually no problem, as the kernel32.dll is (most likely) loaded at the same base address and the same function address can be used - that's my unterstanding so far. In this case however the conditions differ.

To overcome the problem I did the following steps:

  • 64 bit Injector retrieves address of kernel32.dll loaded by 32 bit target using EnumProcessModulesEx() (should be 0xABCD000)
  • Get filename of that kernel32.dll, parse the PE header and get the RVA of LoadLibraryA (should be 0x000EFAB)
  • At this point, we know where kernel32.dll is loaded in the 32 bit target and the address of the function from this DLL.
  • 64 bit Injector starts remote thread in 32 bit target with ImageBase + Function RVA, in this case the magical 0xABCDEFAB

This approach actually works very well, but I can't get rid of the thought that this is total overhead and there must be a more simpler solution to inject 32 bit targets from 64 bit injectors.

I have two questions, for which I am very grateful if they could be answered here:

  1. Is there a more simple way to achieve this kind of injection?
  2. Are there possible problems with the approach I've been taking that I haven't thought about?

Any answers are very much appreciated, thanks!

EDIT: Oh my gosh... I just realized, that I described the situation wrong in my initial post. The INJECTOR is 64 bit, and the TARGET is 32 bit (Initially it was the other way around, but I already corrected it). Ben Voigt's comments down below are totally true, the call to EnumProcessModulesEx would fail. A big big BIG sorry for that confusion :(

PuerNoctis
  • 1,364
  • 1
  • 15
  • 34

3 Answers3

6

I stumbled upon this thread looking for a solution for the same problem.

So far I'm inclined to use another simpler solution. To obtain a 32-bit kernel proc address, the 64-bit process can just execute a 32-bit program that will look up the proc addresses for us:

#include <Windows.h>

int main(int argc, const char**)
{
    if(argc > 1)
        return (int) LoadLibraryA;
    else
        return (int) GetProcAddress;
}
zah
  • 5,314
  • 1
  • 34
  • 31
  • That's pretty much the only way to do it in user mode. It's the same thing on MacOS as a matter of fact. Anyway, unless you do the injection from a kernel module, you will have to deal with this since the 32bit process will have both the kernel32.dll for 64bit and 32bit loaded and unless you are able to access the PEB, you will have a hard time getting the address that you are looking for. – E.T Oct 19 '16 at 19:22
0

This answer addresses an earlier version of the question, it is mostly irrelevant to the case of a 64-bit injector.


Are you saying that approach works? Because according to the documentation, you can't get information about 64-bit processes from WOW64:

If the function is called by a 32-bit application running under WOW64, the dwFilterFlag option is ignored and the function provides the same results as the EnumProcessModules function.

(EnumProcessModules explains the restriction further)

If this function is called from a 32-bit application running on WOW64, it can only enumerate the modules of a 32-bit process. If the process is a 64-bit process, this function fails and the last error code is ERROR_PARTIAL_COPY (299).

But you really do need to find the base address where kernel32.dll loaded, because of ASLR.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Yes, it works perfectly. The EnumProcessModules (which the *Ex variante behaves as on WOW64 according to the documentation) is perfectly sufficient for the procedure. I still use it in my code because if the 64 bit version of the Injector is running I explicitly retrieve the 32/64 module handles to be more "clean", but in the 32 bit version it doesn't actually hurt. I may get ALL the handles, but there's only one kernel32.dll in the modulelist which is always the correct one. – PuerNoctis Jan 08 '12 at 09:48
  • Hm, your edit is intersting. I must have overseen this passage saying that only handles of a 32-bit process can be retrieved, but for some reason it works without failure on several machines... weird. As for the base address of kernel32.dll: From my unterstanding (which may be wrong, that's why I am asking) the CreateRemoteThread function needs the full address of the function within the process's memory to be called. The RVA of the LoadLibrary alone isn't sufficien, so there needs to be an offset, which in this case, is the base address of the loaded kernel32. – PuerNoctis Jan 08 '12 at 09:53
  • @PuerNoctis: I'm also not seeing any sort of `CreateRemoteThread64` function, so how are you passing the 64-bit address of `LoadLibrary` as the thread procedure? – Ben Voigt Jan 08 '12 at 09:53
  • I add the base address and the RVA of the function together as a DWORD and cast it to LPTHREAD_START_ROUTINE. In the end, it's just a number, an address, and it is guaranteed (I read it somewhere, but I don't know exactly where anymore) that any functions provided by the WinAPI have no address larger than 32-bit. So I reckoned passing the address like that is valid. – PuerNoctis Jan 08 '12 at 09:56
  • @PuerNoctis: If `kernel32.dll` is always loading within the first 2G of address space, then that's likely why everything is working. But it may not work on future versions of Windows. – Ben Voigt Jan 08 '12 at 09:59
  • Hmm, then I guess to take this into account when it's not working anymore. Any suggestions how (and if) it is possible to inject 64 bit targets from 32 bit processes another way? – PuerNoctis Jan 08 '12 at 10:06
  • Please read the edit in my inital post at the bottom. I sincerely apologize for that. – PuerNoctis Jan 08 '12 at 10:28
  • @BenVoigt ASLR has no impact on shared DLL such as Kernel32 or ntdll. The address of LoadLibraryA/W will always be the same across processes. Once you determine what the address of the function is from any 32bit process, the address will be the same for any 32bit processes during that session, until reboot. – E.T Sep 23 '16 at 22:08
  • @E.T.: The part of my answer you are referring to was written in consideration of injection across different bitness. The question changed afterward. – Ben Voigt Sep 23 '16 at 22:25
0

I think you could use the debug symbols API to save yourself parsing the PE header and export table. This route should yield the required information for the 32-bit injector; 64-bit target case as well, although I still don't see how you're going to pass a 64-bit address to CreateRemoteThread.

Normally these debug symbol functions require a .pdb or .sym file to operate, however I'm pretty sure they also get information from a DLL export table (just going from experience of what a debugger shows for files where I don't have symbols present).

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Thanks for the hints, I'll try to work with these functions tomorrow and post the results then. – PuerNoctis Jan 08 '12 at 18:01
  • As for the 64-bit address: From `x64->WOW64` there's no problem as the pointer will be truncated properly. But from `WOW64->x64` as of now I would rely on the fact that in most cases an address of a 64-bit function (including the loaded module base) is not loaded at 0x80000000 or higher. Unless the loader (or the programmer) decide to do so everything should be okay (FYI, the 64-bit kernel32.dll on my machine has a default image base of 0x0000000078D20000 at the moment). But I can see that this is kinda... errorprone. Hope my sight of things makes sense. – PuerNoctis Jan 08 '12 at 18:01
  • With `SymFromName` and `ImageRvaToVa` I was able to get the same results as when parsing the PE entries myself - thanks, that simplified my solution tremendously! :) – PuerNoctis Jan 11 '12 at 15:13
  • 1
    @PuerNoctis i have the same problem, could you please provide me a snippet of code for this? I need to know how to use SymFromName and IMageRvaToVa to obtain the RVA of LoadLibraryA :) tnx – Simone Margaritelli Dec 14 '12 at 14:53