Load native C++ .dll from RAM in debugger friendly manner

Viewed 429

Question concerns only Windows for now - other OS's are not so relevant right now.

Just by quick googling - it's possible to load native .dll from RAM, there are for example following libraries:

https://www.joachim-bauch.de/tutorials/loading-a-dll-from-memory/

=>

https://github.com/fancycode/MemoryModule

https://forum.nim-lang.org/t/7943

But all of them requires:

  1. in-depth knowledge of PE file format
  2. mostly those approaches are not debugger friendly.

What I have checked - windows's LoadLibraryA / LoadLibraryW are directed to ntdll.dll / LdrLoadDll - and best picture of how things works can be found from here: https://github.com/hlldz/RefleXXion

And even thus I don't have windows source code - I've checked same functionality from Wine:

LdrLoadDll: https://source.winehq.org/source/dlls/ntdll/loader.c#3169

load_dll: https://source.winehq.org/source/dlls/ntdll/loader.c#3083

load_native_dll: https://source.winehq.org/source/dlls/ntdll/loader.c#2564

NtMapViewOfSection: https://source.winehq.org/source/dlls/ntdll/unix/virtual.c#4469

find_dll_file: https://source.winehq.org/source/dlls/ntdll/loader.c#3021

open_dll_file: https://source.winehq.org/source/dlls/ntdll/loader.c#2467

Suspect loading dll happens via following function calls: NtOpenFile, NtQueryAttributesFile, NtCreateSection/NtOpenSection, NtMapViewOfSection (*)

(More information could be found in

https://github.com/Hagrid29/PELoader

https://gist.github.com/bats3c/59932dfa1f5bb23dd36071119b91af0f

https://www.octawian.ro/fisiere/situri/asor/build/html/_downloads/122f95f9a032396603a837c53b125bb8/Russinovich_M_WinInternals_part1_7th_ed.pdf )

I was also thinking if I could just override NtOpenFile and just redirect file open (in https://github.com/SegaraRai/PathRedirector manner) to different path - but main question what is the alternative location where to store file?

I was thinking if NtOpenFile can open even device, then maybe just replace file with some sort of named pipe (https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipe-client) - but then in maps on how well this will work with NtMapViewOfSection.

Since I was not able to find any working example of such hook or operation (E.g. LoadLibary("\\.\pipe\mynamedpipe_as_dll")) - there is always a risk that such combination is not simply supported.

Is it possible to load native .dll purely from RAM:

  1. Without using file system (not to store .dll e.g. in temporary folder)
  2. Without involving custom drivers (like Dokan) ?
  3. So loaded .dll would be still debugger friendly ?
  4. Not tightly bound to PE file format structures (or use PE structures as less as possible)

If you miss bit more information, check also my own experiments with native dll loading (maybe can give some hints on solving the issue):

https://github.com/tapika/test_native_dll_loading https://github.com/tapika/test_native_dll_loading/discussions/2

4 Answers

Distinguish between debug and release use cases. In debug, save the DLL in a temp file and load with LoadLibrary, which will enable debugging. In release, run from memory with no capability for debugging.

Here's another idea, from considering the linked Guthub issue. If the purpose is to let the users provide their own compression/decompression logic while building a ReadyToRun executable, let them provide that as a static library (object) as opposed to a DLL. The larger project is already about packaging stuff into a single executable, might do some linking while at it.

Yet another idea would be to let the users provide the codec in some kind of interpreted language and optionally plug in the interpreter that supports debugging. Windows comes with a built-in JavaScript interpreter, look up Active Scripting, and debugging those is a free bonus. The performance probably won't be on par with a native code implementation, though.

I think you could probably do something similar with Frida. Hook the functions LoadLibraryA / LoadLibraryW and reimplement them in Frida. but I don't believe this is something that would be stable for production.

For some reference

Could you create a ramdisk to put your DLL there? What exactly is the use-case for this? There are a couple ways to spin up a file in RAM, C#'s MemoryMappedFile for example. I'm not sure if this would be debugger friendly.

Related