using System.ComponentModel;
using System.Runtime.InteropServices;
using WhiteMagic.Memory;
using WhiteMagic.Native;
namespace WhiteMagic.Injection;
///
/// Injects raw machine code into a process's memory.
///
public static class CodeInjector
{
///
/// Injects code at a specific address.
///
/// The memory accessor.
/// The target address.
/// The machine code bytes to write.
/// The address the code was written to (same as ).
/// is empty.
/// Write fails.
public static IntPtr InjectAtAddress(MemoryBase memory, IntPtr address, byte[] code)
{
ArgumentNullException.ThrowIfNull(memory);
ArgumentNullException.ThrowIfNull(code);
if (code.Length == 0)
throw new ArgumentException("Code cannot be empty.", nameof(code));
if (address == IntPtr.Zero)
throw new ArgumentException("Address cannot be zero.", nameof(address));
// Write the code to the target address
int written = memory.WriteBytes(address, code);
if (written != code.Length)
{
int error = Marshal.GetLastPInvokeError();
throw new Win32Exception(error,
$"WriteProcessMemory failed at {address} (wrote {written} of {code.Length} bytes).");
}
return address;
}
///
/// Allocates executable memory and injects code into it.
///
/// The memory accessor.
/// The machine code bytes to inject.
///
/// The memory protection. Defaults to .
///
///
/// The base address of the allocated memory containing the code.
/// The caller is responsible for freeing this memory (e.g., via ).
///
/// is empty.
/// Allocation or write fails.
public static AllocatedMemory Inject(
MemoryBase memory,
byte[] code,
MemoryProtectionType protection = MemoryProtectionType.ExecuteReadWrite)
{
ArgumentNullException.ThrowIfNull(memory);
ArgumentNullException.ThrowIfNull(code);
if (code.Length == 0)
throw new ArgumentException("Code cannot be empty.", nameof(code));
// Allocate memory with the specified protection
var allocated = new AllocatedMemory(memory, code.Length, protection);
// Write the code to the allocated memory
int written = memory.WriteBytes(allocated.BaseAddress, code);
if (written != code.Length)
{
int error = Marshal.GetLastPInvokeError();
allocated.Dispose();
throw new Win32Exception(error,
$"WriteProcessMemory failed (wrote {written} of {code.Length} bytes).");
}
return allocated;
}
}