Reloaded-Project / Reloaded.Injector

C# DLL Injection Library capable of injecting x86 DLLs to x86 process from x64 processes.
GNU Lesser General Public License v3.0
159 stars 32 forks source link

Error when injecting into CREATE_SUSPENDED process #13

Open TinkerWorX opened 1 year ago

TinkerWorX commented 1 year ago

I get the following exception when I create an Injector for a suspended process:

System.ComponentModel.Win32Exception: 'Only part of a ReadProcessMemory or WriteProcessMemory request was completed'

This is the code I use to create the process and inject.

if (CreateProcess(location, new StringBuilder($@"""{location}"" {arguments}"), null, null, false, CREATE_PROCESS.CREATE_SUSPENDED, null, null, new STARTUPINFO(), out var processInformation))
{
    var process = Process.GetProcessById((int)processInformation.dwProcessId);
    var injector = new Injector(process);
}

If I create the process without the CREATE_SUSPENDED flag, it launches without any errors.

For context, I'm coming from EasyHook and was looking for a more updated alternative and found Reloaded.Injector/Reloaded.Hooks. In EasyHook, injecting into a suspended thread works as expected, where I then resume the thread from inside when I'm ready.

Sewer56 commented 1 year ago

Hi,

This is unfortunately a limitation of the design of this library; it can't be used on a process that has started suspended. It is also the reason why I haven't really been updating this library ever since I released it; as I myself don't actively use it.

I originally wrote this library for use with Reloaded-II around 3 years ago but had to go back to the drawing board because I unfortunately ran across this exact same issue.

The exact specific reason comes down to EnumProcessModulesEx (and its friends). On Windows, you can't enumerate the modules of a process that was started suspended because they haven't been loaded in yet. This in turn means you can't get the address of kernel32.dll in an x86 process from a x64 process; and kernel32 is necessary for LoadLibraryW to in turn inject your DLLs.

What I wound up doing is creating a much, much more basic DLL Injector that spawns an x86 process to pass its address of Kernel32 to the main code using a memory mapped file process, injector, and reuse that in the DLL injection operation. Technically speaking, I could have done the same with this library; albeit the idea of using shellcode and PE parsing used here was pretty cool; and novel; so I kept it around. Unpacking and running a binary would also raise a lot of red flags from AV software.

TinkerWorX commented 1 year ago

I appreciate the detailed description. I'll take a look at your other solution. I assume I can still use Reloaded.Hooks once injected?

Sewer56 commented 1 year ago

Yeah that'll work perfectly fine.

Sewer56 commented 1 year ago

I was thinking about this a bit when writing the spec for Reloaded3. I actually found a workaround for this; so I'll probably release version 2.X.X sometime in the future and use it in Reloaded-II, thus resurrecting this library from limbo.

I just gotta get rid of some bloat (namely remove the need for PeNet, and parse the PE header in memory of target process directly. Most of the code for that already exists actually; I just need to move it out to a separate NuGet package.

TinkerWorX commented 1 year ago

Let me know when you do, then I can try and test it with my project.

Albeoris commented 3 months ago

@Sewer56, hello, how are you? :) Do you have the time and motivation to finish this task?

Sewer56 commented 3 months ago

I started working on 2.X, but decided to abandon it mid way through. Instead I forked OpenByteDev/dll-syringe (Rust), which is available here. My fork: Sewer56/dll-syringe.

On the add-c-exports branch (note: Ignore legacy readme), you can build the Rust library with C# bindings. Those bindings aren't finalized, but they do work, and I made them work with suspended processes.

Reloaded-II had to switch over to using this fork/branch due to Defender suddenly disliking the old DLL Injection code, and me receiving around 200 user reports in the span of 2 days.

That API is here and a prebuilt package is here. But do note, they were made over a weekend in an emergency and are not final.

Main thing that's left to do with that code is some cleanup. I've been stripping dependencies etc. to decrease the DLL size, hope to get to around 1/5th of what the original Rust library was.

I wouldn't get to work on this for some months though. I need to finish the Reloaded3 spec first, and then Reloaded.Hooks-rs.