Skip to content

Commit

Permalink
Some initial 32-bit porting in the injector.
Browse files Browse the repository at this point in the history
Part of #5.
  • Loading branch information
alexrp committed Jul 5, 2022
1 parent 1ed4f12 commit 85139f5
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 57 deletions.
136 changes: 87 additions & 49 deletions src/injection/AssemblyInjector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public AssemblyInjector(TargetProcess process, AssemblyInjectorOptions options)
{
ArgumentNullException.ThrowIfNull(process);
ArgumentNullException.ThrowIfNull(options);
_ = process.IsCompatible ? true : throw new PlatformNotSupportedException();
_ = process.IsSupported ? true : throw new PlatformNotSupportedException();

_process = process;
_options = options;
Expand All @@ -68,7 +68,8 @@ void DisposeCore()

string GetModulePath()
{
var path = Path.Combine(_options.ModuleDirectory, "ruptura-x64.dll");
var path = Path.Combine(
_options.ModuleDirectory, $"ruptura-{_process.Architecture.ToString().ToLowerInvariant()}.dll");

return File.Exists(path) ? path : throw new InjectionException("Could not locate the Ruptura native module.");
}
Expand All @@ -87,8 +88,16 @@ void ForceLoaderInitialization()
{
var initializeShell = _process.CreateFunction(asm =>
{
asm.mov(eax, 0);
asm.ret();
if (asm.Bitness == 32)
{
asm.mov(eax, 0);
asm.ret();
}
else
{
asm.mov(eax, 0);
asm.ret(4);
}
});

try
Expand Down Expand Up @@ -141,7 +150,7 @@ nuint GetExport(string name)
_getLastError = GetExport("GetLastError");
}

unsafe nuint CreateParametersArea()
unsafe nuint CreateParameterArea()
{
// Keep in sync with src/module/main.h.

Expand All @@ -155,13 +164,13 @@ unsafe nuint CreateParametersArea()
return _process.AllocMemory((nuint)size, PAGE_PROTECTION_FLAGS.PAGE_READWRITE);
}

unsafe void PopulateParametersArea(nuint area)
unsafe void PopulateParameterArea(nuint parameterArea)
{
// Keep in sync with src/module/main.h.

PopulateMemoryArea(area, (stream, writer) =>
PopulateMemoryArea(parameterArea, (stream, writer) =>
{
writer.WritePointer(0);
writer.WriteSize(0);
writer.WritePointer(0);
writer.Write(0u);
writer.Write(0u);
Expand All @@ -188,37 +197,37 @@ unsafe void PopulateParametersArea(nuint area)
stream.Position = 0;
writer.WritePointer((uint)sizeof(RupturaParameters));
writer.WritePointer(area + argvOff);
writer.WriteSize((uint)sizeof(RupturaParameters));
writer.WritePointer(parameterArea + argvOff);
writer.Write((uint)argOffs.Length);
writer.Write(Environment.ProcessId);
writer.Write((uint)(_process.MainThreadId ?? 0));
writer.Write(0); // Padding.
foreach (var argOff in argOffs)
writer.WritePointer(area + argOff);
writer.WritePointer(parameterArea + argOff);
});
}

async Task InjectModuleAsync(string modulePath, nuint parameters, MemoryMappedViewAccessor accessor)
async Task InjectModuleAsync(string modulePath, nuint parameterArea, MemoryMappedViewAccessor accessor)
{
var size = Encoding.Unicode.GetByteCount(modulePath) + sizeof(char) +
Encoding.ASCII.GetByteCount(NativeEntryPoint) + sizeof(byte);
var area = _process.AllocMemory((uint)size, PAGE_PROTECTION_FLAGS.PAGE_READWRITE);
var nameArea = _process.AllocMemory((uint)size, PAGE_PROTECTION_FLAGS.PAGE_READWRITE);

try
{
nuint modulePathPtr = 0;
nuint entryPointPtr = 0;

PopulateMemoryArea(area, (stream, writer) =>
PopulateMemoryArea(nameArea, (stream, writer) =>
{
modulePathPtr = area + (nuint)stream.Position;
modulePathPtr = nameArea + (nuint)stream.Position;
// Write the module path first to ensure correct alignment.
writer.WriteUtf16String(modulePath);
entryPointPtr = area + (nuint)stream.Position;
entryPointPtr = nameArea + (nuint)stream.Position;
writer.WriteAsciiString(NativeEntryPoint);
});
Expand All @@ -228,40 +237,69 @@ async Task InjectModuleAsync(string modulePath, nuint parameters, MemoryMappedVi
var done = asm.CreateLabel("done");
var failure = asm.CreateLabel("failure");
asm.push(rbx);
asm.sub(rsp, 32);
asm.mov(rbx, rcx);
asm.mov(rcx, modulePathPtr);
asm.mov(rax, _loadLibraryW);
asm.call(rax);
asm.cmp(rax, 0);
asm.je(failure);
asm.mov(rcx, rax);
asm.mov(rdx, entryPointPtr);
asm.mov(rax, _getProcAddress);
asm.call(rax);
asm.cmp(rax, 0);
asm.je(failure);
asm.mov(rcx, rbx);
asm.call(rax);
asm.jmp(done);
asm.Label(ref failure);
asm.mov(rax, _getLastError);
asm.call(rax);
asm.Label(ref done);
asm.add(rsp, 32);
asm.pop(rbx);
asm.ret();
if (asm.Bitness == 32)
{
asm.push((uint)modulePathPtr);
asm.mov(eax, (uint)_loadLibraryW);
asm.call(eax);
asm.cmp(eax, 0);
asm.je(failure);
asm.push((uint)entryPointPtr);
asm.push(eax);
asm.mov(eax, (uint)_getProcAddress);
asm.call(eax);
asm.cmp(eax, 0);
asm.je(failure);
asm.push(__dword_ptr[esp + 4]);
asm.call(eax);
asm.jmp(done);
asm.Label(ref failure);
asm.mov(eax, (uint)_getLastError);
asm.call(eax);
asm.Label(ref done);
asm.ret(4);
}
else
{
asm.push(rbx);
asm.sub(rsp, 32);
asm.mov(rbx, rcx);
asm.mov(rcx, modulePathPtr);
asm.mov(rax, _loadLibraryW);
asm.call(rax);
asm.cmp(rax, 0);
asm.je(failure);
asm.mov(rcx, rax);
asm.mov(rdx, entryPointPtr);
asm.mov(rax, _getProcAddress);
asm.call(rax);
asm.cmp(rax, 0);
asm.je(failure);
asm.mov(rcx, rbx);
asm.call(rax);
asm.jmp(done);
asm.Label(ref failure);
asm.mov(rax, _getLastError);
asm.call(rax);
asm.Label(ref done);
asm.add(rsp, 32);
asm.pop(rbx);
asm.ret();
}
});

try
{
var threadHandle = _process.CreateThread(injectShell, parameters);
var threadHandle = _process.CreateThread(injectShell, parameterArea);

try
{
Expand Down Expand Up @@ -315,7 +353,7 @@ async Task InjectModuleAsync(string modulePath, nuint parameters, MemoryMappedVi
}
finally
{
_process.FreeMemory(area);
_process.FreeMemory(nameArea);
}
}

Expand All @@ -338,11 +376,11 @@ public Task InjectAssemblyAsync()
ForceLoaderInitialization();
RetrieveKernel32Exports();
var paramsArea = CreateParametersArea();
var paramsArea = CreateParameterArea();
try
{
PopulateParametersArea(paramsArea);
PopulateParameterArea(paramsArea);
await InjectModuleAsync(modulePath, paramsArea, accessor).ConfigureAwait(false);
}
Expand Down
5 changes: 5 additions & 0 deletions src/injection/IO/InjectionBinaryWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ public void WritePointer(nuint value)
Write((uint)value);
}

public void WriteSize(nuint value)
{
WritePointer(value);
}

public void WriteAsciiString(string value)
{
foreach (var b in Encoding.ASCII.GetBytes(value))
Expand Down
34 changes: 26 additions & 8 deletions src/injection/TargetProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ public sealed unsafe class TargetProcess : IDisposable

public SafeHandle Handle { get; }

public Architecture Architecture { get; }

internal int? MainThreadId { get; }

internal bool IsCompatible { get; }
internal bool IsSupported => Architecture == Architecture.X64;

TargetProcess(int id, SafeHandle handle, int? mainThreadId)
{
Expand All @@ -25,14 +27,25 @@ public sealed unsafe class TargetProcess : IDisposable

IMAGE_FILE_MACHINE os;

if (!IsWow64Process2(Handle, out var emu, &os))
if (!IsWow64Process2(Handle, out var proc, &os))
throw new Win32Exception();

// x64 process on an x64 machine or x64 process on an Arm64 machine.
if ((os, emu) is
(IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_ARM64, IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_AMD64) or
(IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_UNKNOWN))
IsCompatible = true;
Architecture = (os, proc) switch
{
(IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_I386, IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_UNKNOWN) or
(_, IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_I386) =>
Architecture.X86,
(IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_UNKNOWN) or
(_, IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_AMD64) =>
Architecture.X64,
(IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_ARM, IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_UNKNOWN) or
(_, IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_ARM) =>
Architecture.Arm,
(IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_ARM64, IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_UNKNOWN) or
(_, IMAGE_FILE_MACHINE.IMAGE_FILE_MACHINE_ARM64) =>
Architecture.Arm64,
_ => throw new UnreachableException(),
};
}

~TargetProcess()
Expand Down Expand Up @@ -208,7 +221,12 @@ internal void FlushCache(nuint address, nuint length)

internal nuint CreateFunction(Action<Assembler> action)
{
var asm = new Assembler(64);
var asm = new Assembler(Architecture switch
{
Architecture.X86 or Architecture.Arm => 32,
Architecture.X64 or Architecture.Arm64 => 64,
_ => throw new UnreachableException(),
});

action(asm);

Expand Down

0 comments on commit 85139f5

Please sign in to comment.