diff --git a/ManagedInjector.Lib/InjectableProcess.cs b/ManagedInjector.Lib/InjectableProcess.cs index 1af5739..e57b3d5 100644 --- a/ManagedInjector.Lib/InjectableProcess.cs +++ b/ManagedInjector.Lib/InjectableProcess.cs @@ -140,8 +140,7 @@ public IInjector GetInjector() ProcessArchitecture.NetFrameworkV2 => new FrameworkV2Injector(), ProcessArchitecture.NetFrameworkV4 => new FrameworkV4Injector(), ProcessArchitecture.Mono => throw new NotImplementedException("Mono injector is not yet implemented"), - ProcessArchitecture.NetCore => throw new NotImplementedException( - ".NET Core injector is not yet implemented"), + ProcessArchitecture.NetCore => new CoreClrInjector(), ProcessArchitecture.Unknown => throw new Exception( "Tried to inject into process with unknown architecture"), _ => throw new NotSupportedException($"No injector found for architecture {arch}"), diff --git a/ManagedInjector.Lib/Injectors/CoreClrInjector.cs b/ManagedInjector.Lib/Injectors/CoreClrInjector.cs new file mode 100644 index 0000000..c4bb6e1 --- /dev/null +++ b/ManagedInjector.Lib/Injectors/CoreClrInjector.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Iced.Intel; +using static Iced.Intel.AssemblerRegisters; + +namespace HoLLy.ManagedInjector.Injectors +{ + public class CoreClrInjector : IInjector + { + private static readonly Guid iidIclrRuntimeHost = new Guid(0x90F1A06C, 0x7712, 0x4762, 0x86, 0xB5, 0x7A, 0x5E, 0xBA, 0x6B, 0xDB, 0x02); + + public EntryPointType EntryPoint => EntryPointType.TakesStringReturnsInt; + + public void Inject(InjectableProcess process, string dllPath, string typeName, string methodName) + { + bool x86 = !process.Is64Bit; + + var getClrRuntimeHostAddr = FindGetCLRRuntimeHost(process.Pid, process.FullHandle, x86); + var callStub = CreateCallStub(process.FullHandle, dllPath, typeName, methodName, null, getClrRuntimeHostAddr, x86); + + var hThread = CodeInjectionUtils.RunRemoteCode(process.FullHandle, callStub, x86); + Console.WriteLine("Thread handle: " + hThread.ToInt32().ToString("X8")); + } + + private static IntPtr FindGetCLRRuntimeHost(uint pid, IntPtr hProc, bool x86) + { + var mods = NativeHelper.GetModules(pid); + var mod = mods.SingleOrDefault(x => x.moduleName.Equals("coreclr.dll", StringComparison.InvariantCultureIgnoreCase)); + + if (mod == default) + throw new Exception("Couldn't find coreclr.dll, arch mismatch?"); + + int fnAddr = CodeInjectionUtils.GetExportAddress(hProc, mod.baseAddress, "GetCLRRuntimeHost", x86); + + return mod.baseAddress + fnAddr; + } + + private static IReadOnlyList CreateCallStub(IntPtr hProc, string asmPath, string typeFullName, string methodName, string? args, IntPtr fnAddr, bool x86) + { + var ppv = alloc(IntPtr.Size); + var riid = allocBytes(iidIclrRuntimeHost.ToByteArray()); + + var pReturnValue = alloc(4); + var pwzArgument = allocString(args); + var pwzMethodName = allocString(methodName); + var pwzTypeName = allocString(typeFullName); + var pwzAssemblyPath = allocString(asmPath); + + var c = new Assembler(x86 ? 32 : 64); + + void AddCallReg(Register r, params object[] callArgs) => CodeInjectionUtils.AddCallStub(c, r, callArgs, x86); + void AddCallPtr(IntPtr fn, params object[] callArgs) => CodeInjectionUtils.AddCallStub(c, fn, callArgs, x86); + + if (x86) { + // call GetCLRRuntimeHost + AddCallPtr(fnAddr, riid, ppv); + + // call ICLRRuntimeHost::Start + c.mov(edx, __[ppv.ToInt32()]); + c.mov(eax, __[edx]); + c.mov(eax, __[eax + 0x0C]); + AddCallReg(eax, edx); + + // call ICLRRuntimeHost::ExecuteInDefaultAppDomain + c.mov(edx, __[ppv.ToInt32()]); + c.mov(eax, __[edx]); + c.mov(eax, __[eax + 0x2C]); + AddCallReg(eax, edx, pwzAssemblyPath, pwzTypeName, pwzMethodName, pwzArgument, pReturnValue); + + c.ret(); + } else { + const int maxStackIndex = 3; + const int stackOffset = 0x20; + c.sub(rsp, stackOffset + maxStackIndex * 8); + + // call GetCLRRuntimeHost + AddCallPtr(fnAddr, riid, ppv); + + // call pClrHost->Start(); + c.mov(rcx, ppv.ToInt64()); + c.mov(rcx, __[rcx]); + c.mov(rax, __[rcx]); + c.mov(rdx, __[rax + 0x18]); + AddCallReg(rdx, rcx); + + // call pClrHost->ExecuteInDefaultAppDomain() + c.mov(rcx, ppv.ToInt64()); + c.mov(rcx, __[rcx]); + c.mov(rax, __[rcx]); + c.mov(rax, __[rax + 0x58]); + AddCallReg(rax, rcx, pwzAssemblyPath, pwzTypeName, pwzMethodName, pwzArgument, pReturnValue); + + c.add(rsp, stackOffset + maxStackIndex * 8); + + c.ret(); + } + + return c.Instructions; + + IntPtr alloc(int size, int protection = 0x04) => Native.VirtualAllocEx(hProc, IntPtr.Zero, (uint)size, 0x1000, protection); + void writeBytes(IntPtr address, byte[] b) => Native.WriteProcessMemory(hProc, address, b, (uint)b.Length, out _); + void writeString(IntPtr address, string str) => writeBytes(address, new UnicodeEncoding().GetBytes(str)); + + IntPtr allocString(string? str) + { + if (str is null) return IntPtr.Zero; + + IntPtr pString = alloc(str.Length * 2 + 2); + writeString(pString, str); + return pString; + } + + IntPtr allocBytes(byte[] buffer) + { + IntPtr pBuffer = alloc(buffer.Length); + writeBytes(pBuffer, buffer); + return pBuffer; + } + } + } +}