9

I'm injecting native x86 code into a .NET application, via a TLS callback in the binary image. Unfortunately, .NET executables only import mscorlib.dll within the PE, and have kernel32.dll mapped automatically into the memory space at runtime. I don't want to inject any imports. This makes finding the addresses of LoadLibrary and GetProcAddress awkward.

My first thought for a solution was to find the PEB via mov eax, dword ptr [fs:030h], then load PEB->Ldr and PEB->Ldr.InMemoryOrderModuleList.Flink from there, and use them to find the module in memory. From there I could grab the base address of kernel32.dll in memory, and offset to the PE header and export directory RVA. From there I could scan through the import entries for the export by name, and find the address.

This has the following problems:

  • Lots of code for something as trivial as an API call.
  • ASCII string that represents the API name would have to be embedded in the code.
  • Caching the found addresses across a long runtime / multiple threads is awkward.

Is there a cleaner way of doing this that solves these problems, without resorting to import injection? Keep in mind that I can't change the EP of the image, because the CLR relies on the EP being a jump to the managed IL.

Polynomial
  • 132,208
  • 43
  • 298
  • 379

1 Answers1

3

There's a good way to solve problems (2) and (3) in the described manual binding process but I'm reluctant to publish good and elaborated public tutorial about designing shellcode here on StackExchange. ^_^ So, there's a hint for you of using crcs for (2) and indirect calls for (3) - you seem familiar enough with the manual binding process to deduce the exact way of applying these two concepts.

Regarding (1), is not a problem but an incorrect assumption: binding exports to imports and providing a programmer ability of calling system API is not trivial by whatever mean. The question is just who'll do all the dirty work. As soon as you reject Windows linker's service for its reliance on plaintext names, you're on your own to implement the dirty details by your own way.

Import injection seems to be the most clean way as you modify the binary anyway, but it adds plaintext API names, so I guess because of that is out of your consideration.

Van Jone
  • 406
  • 2
  • 5