InjectDotnet 0.4.0

Requires NuGet 3.2 or higher.

dotnet add package InjectDotnet --version 0.4.0
NuGet\Install-Package InjectDotnet -Version 0.4.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="InjectDotnet" Version="0.4.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add InjectDotnet --version 0.4.0
#r "nuget: InjectDotnet, 0.4.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install InjectDotnet as a Cake Addin
#addin nuget:?package=InjectDotnet&version=0.4.0

// Install InjectDotnet as a Cake Tool
#tool nuget:?package=InjectDotnet&version=0.4.0

InjectDotnet

Inject a .NET Core dll into a native Win32 or Win64 process. InjectDotnet is a library, not a standalone application. This allows developers/hackers to pass any argument to the injected dll, not just a string. There are two complementary libraries:

  • InjectDotnet: Injects a managed dll into a native process.
  • InjectDotnet.NativeHelper: Referenced by the injected dll and provides methods for hooking native functions.

Add InjectDotnet to your injector, and add InjectDotnet.NativeHelper to your injected dll.

**IMPORTANT** Your projects myst be configures x64 or x86, not AnyCPU!!

No Unmanaged Libraries

Unlike other dotnet dll injectors, this one does not rely on a native dll to load the runtime in the target process. Loading and executing the injected dll is accomplished by hand-written assembly instructions that are written directly into the target process' memory space and executed.

Inject Into Running Processes

InjectDotnet Supports injecting managed Dlls into running processes using the traditional CreateRemoteThread() method.

It's as simple as the following example.

var target = Process.GetProcessesByName("target");

target.Inject(
	"InjectedDll.runtimeconfig.json",
	"InjectedDll.dll",
	"InjectedDll.HookDemo, InjectedDll, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
	"Bootstrap",
	"this is an argument passed to the injected dll's Bootstrap() method");

You may optionally wait for bootstrap method to return to receive it's return code, and the injector supports passing structs with additional data to the injected dll (see the samples prokjects).

Injecting into .NET Targets

InjectDotnet supports injecting into managed target processes, but there are some limitations.

  1. The injected Dll's required frameworks must be compatible with the frameworks loaded by the runtime already in the target process. For instance, you cannot inject a .NET 7 dll into a .NET 6 target process.
  2. When injected into a .NET Core process, the injected Dll will be loaded into that process' AppDomain.
  3. You can inject into a .NET Framwork process, but the injected .NET Core Dll cannot access the target's AppDomain.
  4. Injecting into self-contained apps is supported, but single-file apps are not supported. Self-contained apps are more strict about which frameworks can be loaded. Portable apps can run code from older frameworks, but self-contained apps can only run code from the framework version that published it.
  5. Injecting into a new managed process at startup is not supported.

If Inject() fails to load the CLR in the target process, it returns the host fxr error code. Inject() must be called with waitForReturn: true for the error code to be returned.

Inject Into a New Process at Startup

InjectDotnet supports injecting managed Dlls at the entry point of a process using its built-in debugger.

It's as simple as the following example.

var debugger = new Debugger("target.exe", arguments: null);

debugger.InjectStartup(
	"InjectedDll.runtimeconfig.json",
	"InjectedDll.dll",
	"InjectedDll.HookDemo, InjectedDll, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
	"Bootstrap",
	"this is an argument passed to the injected dll's Bootstrap() method");

await debugger.ResumeProcessAsync();

The debugger supports all win32 debug events, and you may use them to, for example, receive data from the injected dll via OutputDebugString.

Hooking Native Functions

InjectDotnet.NativeHelper supports three different hooking methods:

Hook Type Description
ImportHook Replaces the hooked function's import address table entry in the target module with a pointer to the hooking function.
JumpHook Overwrite's the first instruction(s) of the hooked function with a jump to a Trampoline.
BreakpointHook Sets a hardware breakpoint at the hooked function's first instruction and uses a vectored exception handler to intercept execution.

See the samples for useage.

There are two sample projects:

  • InjectedDll - A .NET 7.0 dll to be injected into a native process and uses InjectDotnet.NativeHelper to hook native functions.
  • InjectIntoRunning - The program that uses InjectDotnet to inject InjectedDll into HxD.exe and pass it two strings and a png image as arguments. Executes InjectedDll.HookDemo.Bootstrap after injection.
  • InjectAtStartup - The program that uses InjectDotnet to debug HxD.exe and inject InjectedDll at its entry point. Executes InjectedDll.HookDemo.Bootstrap after injection.

SampleInjected.Program.Bootstrap loads the two strings and the png image from native memory, frees the native memory, and then opens a System.Windows.Forms.Form to display the strings and image.

It also hooks the WriteFile function imported by notepad.exe from kernel32.dll and the CreateFileW function exported by kernel32.dll.

Debugging Injected Dlls with Visual Studio

Ther are two ways to debug injected .NET dlls in Visual Studio

Method 1: Set a Native Executable as the Debug Target

  1. In Visual Studio, navigate to the injected dll's properties > Debug > Open debug launch profiles UI
  2. Create a new "Executable" profile.
  3. Enter the native executable into which this dll will be injected and any command line arguments. Save.
  4. Choose the newly-created debug profile and launch the debugger.
  5. Execute SampleInjector.exe to perform the injection

Method 2: Attaching to Injected Process

  1. Build SampleInjected and SampleInjector targeting the platform of your Windows PC
  2. Start the target native process (notepad.exe in the sample)
  3. With the SampleInjected project open, attach the Visual Studio debugger the target process
    1. Debug > Attach to Process of Ctrl+Alt+P
    2. Select the .NET Core Debugger
      1. Next to "Attach to:", click "Select"
      2. Select "Debug these code types"
      3. Choose "Managed (.NET Core, .NET 5+)"
    3. Choose the target process. (easiest accomplished by clicking "Select Window" and clicking on the target's window)
    4. Click "Attach"
  4. Execute SampleInjector.exe to perform the injection
Product Compatible and additional computed target framework versions.
.NET net6.0-windows7.0 is compatible.  net7.0-windows was computed.  net8.0-windows was computed.  net8.0-windows7.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net6.0-windows7.0

    • No dependencies.
  • net8.0-windows7.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.4.0 200 12/13/2023
0.3.3 76 12/10/2023
0.3.2 467 11/14/2023
0.3.1 77 11/10/2023
0.3.0 75 11/10/2023
0.2.1 98 10/30/2023
0.2.0 128 6/8/2023
0.1.2.2 116 6/8/2023
0.1.2.1 116 6/5/2023
0.1.2 108 6/5/2023
0.1.1.2 112 6/5/2023
0.1.1.1 109 6/5/2023
0.1.1 107 6/5/2023
0.1.0 111 6/4/2023