diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e4f3d1e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.cs] +csharp_new_line_before_open_brace = all +end_of_line = crlf \ No newline at end of file diff --git a/Source/Reloaded.Injector/CompatUtils.cs b/Source/Reloaded.Injector/CompatUtils.cs new file mode 100644 index 0000000..fc8e2b9 --- /dev/null +++ b/Source/Reloaded.Injector/CompatUtils.cs @@ -0,0 +1,154 @@ +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using Reloaded.Memory; +using Reloaded.Memory.Buffers; +using Reloaded.Memory.Buffers.Structs; +using Reloaded.Memory.Structs; +using Reloaded.Memory.Utilities; +//using static Reloaded.Injector.Kernel32.Kernel32; +using K32 = Reloaded.Memory.Native.Windows.Kernel32; + +namespace Reloaded.Injector { + internal static class CompatUtils { //for back compat with other reload modules + + public static unsafe long AddBytes(this CircularBuffer buffer, Span bytes) { + fixed (byte* ptr = bytes) { + return (long)buffer.Add(ptr, (uint)bytes.Length); + } + + } + } + + public class PrivateMemoryBufferCompat : IDisposable { + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MemoryAllocation Allocate(Process proc, nuint length, nuint baseStartAddy = 0) { + + nuint num = K32.VirtualAllocEx(proc.Handle, baseStartAddy, length, K32.MEM_ALLOCATION_TYPE.MEM_COMMIT | K32.MEM_ALLOCATION_TYPE.MEM_RESERVE, K32.MEM_PROTECTION.PAGE_EXECUTE_READWRITE); + if (num != 0) { + return new MemoryAllocation(num, length); + } + throw new Exception(); + + } + + + + public PrivateMemoryBufferCompat(Process proc, nuint size, bool isPrivateAlloc = false) { + IsPrivateAlloc = isPrivateAlloc; + memory = new ExternalMemory(proc); + if (!IsPrivateAlloc) + alloc = memory.Allocate(size); + else + palloc = Buffers.AllocatePrivateMemory(new Reloaded.Memory.Buffers.Structs.Params.BufferAllocatorSettings { TargetProcess = proc, MinAddress = Shellcode.minimumAddress, MaxAddress = (nuint)UInt64.MaxValue, Size = (uint)size, RetryCount = Shellcode.retryCount }); + } + private bool IsPrivateAlloc; + public nuint BaseAddress => IsPrivateAlloc ? palloc.BaseAddress : alloc.Address; + public nuint AllocSize => IsPrivateAlloc ? palloc.Size : alloc.Length; + private MemoryAllocation alloc; + private PrivateAllocation palloc; + //private PrivateAllocation memory; + private ExternalMemory memory; + private bool disposedValue; + private nuint nextFreeBlockOffset = 0; + + protected virtual void Dispose(bool disposing) { + if (!disposedValue) { + if (disposing) { + + } + if (IsPrivateAlloc) + palloc.Dispose(); + else + memory.Free(alloc); + + disposedValue = true; + } + } + + private object writeLock = new(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static nuint GetSize(bool marshalElement) { + if (!marshalElement) + return (nuint)Unsafe.SizeOf(); + + + return (nuint)Marshal.SizeOf(); + } + + // + // Summary: + // Writes your own structure address into process' memory and gives you the address + // to which the structure has been directly written to. + // + // Parameters: + // bytesToWrite: + // A structure to be converted into individual bytes to be written onto the buffer. + // + // marshalElement: + // Set this to true to marshal the given parameter before writing it to the buffer, + // else false. + // + // alignment: + // The memory alignment of the item to be added to the buffer. + // + // Returns: + // Pointer to the newly written structure in memory. Null pointer, if it cannot + // fit into the buffer. + public nuint Add(ref TStructure bytesToWrite, bool marshalElement = false) where TStructure : unmanaged { + if (marshalElement) + return AddMarshalled(bytesToWrite); + var writePos = SecureWriteMemLoc(bytesToWrite, marshalElement); + memory.Write(writePos, bytesToWrite); + return writePos; + } + private nuint AddMarshalled(TStructure bytesToWrite) { + var writePos = SecureWriteMemLoc(bytesToWrite, true); + memory.WriteWithMarshalling(writePos, bytesToWrite); + return writePos; + } + private nuint SecureWriteMemLoc(TStructure bytesToWrite, bool marshalElement) => SecureWriteMemLoc(GetSize(marshalElement)); + + private nuint SecureWriteMemLoc(nuint size) { + lock (writeLock) { + var writePos = nextFreeBlockOffset; + if (size + nextFreeBlockOffset > AllocSize) + throw new InsufficientMemoryException($"Tried to allocate: {size} and total size for our allocation is: {AllocSize} and next free block offset: {nextFreeBlockOffset}"); + nextFreeBlockOffset += size; + var addy = writePos + BaseAddress; + return addy; + } + } + public nuint Add(ref TStructure bytesToWrite) where TStructure : struct { + return AddMarshalled(bytesToWrite); + } + + public nuint Add(Span bytesToWrite) => AddBytes(bytesToWrite); + public nuint AddBytes(Span bytesToWrite) { + var writePos = SecureWriteMemLoc((nuint)bytesToWrite.Length); + memory.WriteRaw(writePos, bytesToWrite); + return writePos; + } + + + + + // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + ~PrivateMemoryBufferCompat() { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: false); + } + + public void Dispose() { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member diff --git a/Source/Reloaded.Injector/Injector.cs b/Source/Reloaded.Injector/Injector.cs index 151389d..bb105bc 100644 --- a/Source/Reloaded.Injector/Injector.cs +++ b/Source/Reloaded.Injector/Injector.cs @@ -1,9 +1,12 @@ -using System; +using System; using System.Diagnostics; using System.IO; +using System.Linq; +using System.Runtime.InteropServices; using Reloaded.Injector.Exceptions; using Reloaded.Injector.Interop; -using Reloaded.Memory.Sources; +using Reloaded.Memory; +using Reloaded.Memory.Structs; using Reloaded.Memory.Utilities; using static Reloaded.Injector.Kernel32.Kernel32; @@ -26,7 +29,9 @@ public class Injector : IDisposable /// public Shellcode ShellCode { get; private set; } /* Call GetProcAddress and LoadLibraryW in remote process. */ - private CircularBuffer _circularBuffer; /* Used for calling foreign functions. */ + private PrivateMemoryBufferCompat _circularBuffer; /* Used for calling foreign functions. */ + //private MemoryAllocation _circularBufferSource; + private Process _process; /* Process to DLL Inject into. */ /// @@ -36,11 +41,12 @@ public class Injector : IDisposable public Injector(Process process) { // Initiate target process. - _process = process; - _circularBuffer = new CircularBuffer(4096, new ExternalMemory(process)); - ShellCode = new Shellcode(process); + _process = process; + _procMemory = new ExternalMemory(process); + _circularBuffer = new(process, Shellcode.CircularBufferSize); + ShellCode = new Shellcode(process); } - + private ExternalMemory _procMemory; ~Injector() { Dispose(); @@ -49,8 +55,9 @@ public Injector(Process process) /// public void Dispose() { - _circularBuffer?.Dispose(); + ShellCode?.Dispose(); + _circularBuffer?.Dispose(); GC.SuppressFinalize(this); } @@ -58,15 +65,16 @@ public void Dispose() /// Injects a DLL into the target process. /// /// The absolute path to your DLL to be injected. + /// Miliseconds to potentially wait for things like module loading on a new proc. /// This function executes LoadLibraryW inside the remote process. /// The target process is not running. /// The address/handle of the loaded in library inside the target process. Zero if the operation failed. - public long Inject(string modulePath) + public long Inject(string modulePath, int msTimeout=3000) { // Error checking. AssertProcessNotRunning(); - var moduleHandle = IsAbsolutePath(modulePath) ? GetModuleHandleFromPath(modulePath) : GetModuleHandleFromName(modulePath); + var moduleHandle = IsAbsolutePath(modulePath) ? GetModuleHandleFromPath(modulePath, msTimeout) : GetModuleHandleFromName(modulePath, msTimeout); if (moduleHandle != IntPtr.Zero) return (long)moduleHandle; @@ -110,10 +118,16 @@ public long GetFunctionAddress(string module, string functionToExecute) /// A parameter must be passed and the target method must expect it. This is a limitation of CreateRemoteThread. /// /// A 32bit truncated exit code/return value. CreateRemoteThread does not support 64bit returns. - public int CallFunction(string module, string functionToExecute, TStruct parameter = default, bool marshalParameter = false) + public unsafe int CallFunction(string module, string functionToExecute, TStruct parameter = default) where TStruct : struct + { + + var parameterPtr = _circularBuffer.Add(ref parameter); + return CallFunctionPtr(module, functionToExecute, (UInt64)parameterPtr); + } + public int CallFunction(string module, string functionToExecute, TStruct parameter = default, bool marshalParameter = false) where TStruct : unmanaged { var parameterPtr = _circularBuffer.Add(ref parameter, marshalParameter); - return CallFunction(module, functionToExecute, (long)parameterPtr); + return CallFunctionPtr(module, functionToExecute, (UInt64)parameterPtr); } /// @@ -123,7 +137,7 @@ public int CallFunction(string module, string functionToExecute, TStruc /// The function of that module to be executed. /// Raw value/pointer to parameter to pass to the target function. /// A 32bit truncated exit code/return value. CreateRemoteThread does not support 64bit returns. - public int CallFunction(string module, string functionToExecute, long parameterPtr) + public int CallFunctionPtr(string module, string functionToExecute, UInt64 parameterPtr) { long methodAddress = GetFunctionAddress(module, functionToExecute); return CallRemoteFunction(_process.Handle, (IntPtr)methodAddress, (IntPtr) parameterPtr); @@ -151,10 +165,10 @@ public bool Eject(string module) /// /// The absolute path of the module (including extension). /// 0 if the operation fails, else an address. - public IntPtr GetModuleHandleFromPath(string modulePath) + public IntPtr GetModuleHandleFromPath(string modulePath, int msTimeout=3000) { string fullPath = Path.GetFullPath(modulePath); - foreach (var module in Safety.TryGetModules(_process)) + foreach (var module in Safety.TryGetModules(_process, msTimeout)) { if (Path.GetFullPath(module.ModulePath) == fullPath) return module.BaseAddress; @@ -168,9 +182,9 @@ public IntPtr GetModuleHandleFromPath(string modulePath) /// /// The name of the module (including extension). /// 0 if the operation fails, else an address. - public IntPtr GetModuleHandleFromName(string moduleName) + public IntPtr GetModuleHandleFromName(string moduleName, int msTimeout=3000) { - foreach (var module in Safety.TryGetModules(_process)) + foreach (var module in Safety.TryGetModules(_process, msTimeout)) { if (Path.GetFileName(module.ModulePath) == moduleName) return module.BaseAddress; diff --git a/Source/Reloaded.Injector/Reloaded.Injector.csproj b/Source/Reloaded.Injector/Reloaded.Injector.csproj index 50d439e..7bc5388 100644 --- a/Source/Reloaded.Injector/Reloaded.Injector.csproj +++ b/Source/Reloaded.Injector/Reloaded.Injector.csproj @@ -1,4 +1,4 @@ - + netstandard2.0; NET472 @@ -13,9 +13,10 @@ https://github.com/Reloaded-Project/Reloaded.Injector git true - 1.2.5 + 1.2.117 LGPLV3 LICENSE + Preview Icon.png true @@ -32,12 +33,12 @@ - - + + All None - + diff --git a/Source/Reloaded.Injector/Shellcode.cs b/Source/Reloaded.Injector/Shellcode.cs index 895fc54..06c8143 100644 --- a/Source/Reloaded.Injector/Shellcode.cs +++ b/Source/Reloaded.Injector/Shellcode.cs @@ -1,15 +1,13 @@ -using System; +using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Text; using PeNet; +using PeNet.Header.Pe; using Reloaded.Injector.Exceptions; -using Reloaded.Injector.Interop; using Reloaded.Injector.Interop.Structures; -using Reloaded.Memory.Buffers; -using Reloaded.Memory.Sources; -using Reloaded.Memory.Utilities; +using Reloaded.Memory; using static Reloaded.Injector.Kernel32.Kernel32; namespace Reloaded.Injector @@ -20,6 +18,11 @@ namespace Reloaded.Injector /// public class Shellcode : IDisposable { + public static nuint CircularBufferSize = 4096; + public static nuint PrivateBufferSize = 4096;//orig did 4096 + public static bool AssemblyLargePtrFix = false;// THIS DOES NOT WORK but if the ptr to our memroy is too high the fasm call will fail so wil need to figure out a work around + internal const nuint minimumAddress = 65536u; + internal const int retryCount = 3; /* Setup/Build Shellcode */ public long Kernel32Handle { get; } /* Address of Kernel32 in remote process. */ public long LoadLibraryAddress { get; private set; } /* Address of LoadLibrary function. */ @@ -31,11 +34,12 @@ public class Shellcode : IDisposable /* Temp Helpers */ private Assembler.Assembler _assembler; /* Provides JIT Assembly of x86/x64 mnemonics. */ - private PrivateMemoryBuffer _privateBuffer; /* Provides us with somewhere to write our shellcode. */ + private PrivateMemoryBufferCompat _privateBuffer; /* Provides us with somewhere to write our shellcode. */ /* Perm Helpers */ private ExternalMemory _memory; /* Provides access to other process' memory. */ - private CircularBuffer _circularBuffer;/* For passing in our parameters to shellcode. */ + private PrivateMemoryBufferCompat _circularBuffer; /* the actual circular buffer no longer works with external process memory */ + private Process _targetProcess; /* The process we will be calling functions in. */ /* Final products. */ /* stdcall for x86, Microsoft for x64 */ @@ -52,16 +56,22 @@ public class Shellcode : IDisposable /// Process inside which to execute. public Shellcode(Process targetProcess) { - _privateBuffer = new MemoryBufferHelper(targetProcess).CreatePrivateMemoryBuffer(4096); + + _privateBuffer = new PrivateMemoryBufferCompat(targetProcess,PrivateBufferSize, true); _assembler = new Assembler.Assembler(); _memory = new ExternalMemory(targetProcess); - _circularBuffer = new CircularBuffer(4096, _memory); + _circularBuffer = new (targetProcess, CircularBufferSize); _targetProcess = targetProcess; // Get arch of target process. PeFile targetPeFile = new PeFile(targetProcess.Modules[0].FileName); _machineType = (MachineType) targetPeFile.ImageNtHeaders.FileHeader.Machine; + if (_machineType == MachineType.I386 && Environment.Is64BitProcess) { //.net auto processes have 32 bit header even though they will end up running as 64 bit + if (IsWow64Process(targetProcess.Handle, out var is32Bit) && is32Bit == false) + _machineType = MachineType.AMD64; + } + // Get Kernel32 load address in target. Module kernel32Module = GetKernel32InRemoteProcess(targetProcess); Kernel32Handle = (long) kernel32Module.BaseAddress; @@ -99,6 +109,9 @@ public Shellcode(Process targetProcess) _assembler.Dispose(); _assembler = null; } + [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool IsWow64Process([In] IntPtr process, [Out] out bool wow64Process); ~Shellcode() { @@ -188,17 +201,22 @@ private void BuildGetProcAddress64() "sub rsp, 40", // Re-align stack to 16 byte boundary +32 shadow space "mov rdx, qword [qword rcx + 8]", // lpProcName "mov rcx, qword [qword rcx + 0]", // hModule - $"call qword [qword {getProcAddressPtr}]", + AssemblyLargePtrFix ? $"mov qword [qword {getProcAddressPtr}], rax": $"call qword [qword {getProcAddressPtr}]",// CreateRemoteThread lpParameter with string already in ECX. + + AssemblyLargePtrFix ? $"call rax" : "", + $"mov qword [qword 0x{_getProcAddressReturnValuePtr.ToString("X")}], rax", "add rsp, 40", // Re-align stack to 16 byte boundary + shadow space. "ret" // Restore stack ptr. (Callee cleanup) }; - - byte[] bytes = _assembler.Assemble(getProcAddress); + var bytes = GetASM("BuildGetProcAddress64", getProcAddress); _getProcAddressShellPtr = (long)_privateBuffer.Add(bytes); } - + private byte[] GetASM(String for_what, params string[] lines) { + byte[] bytes = _assembler.Assemble(lines); + return bytes; + } private void BuildLoadLibraryW86() { // Using stdcall calling convention. @@ -238,13 +256,14 @@ private void BuildLoadLibraryW64() { $"use64", "sub rsp, 40", // Re-align stack to 16 byte boundary + shadow space. - $"call qword [qword {loadLibraryPtr}]", // CreateRemoteThread lpParameter with string already in ECX. + AssemblyLargePtrFix ? $"mov qword [qword {loadLibraryPtr}], rax" : $"call qword [qword {loadLibraryPtr}]", // CreateRemoteThread lpParameter with string already in ECX. //seems to throw an error of value out of range for high bit addresses + AssemblyLargePtrFix ? $"call rax" : "", // CreateRemoteThread lpParameter with string already in ECX. + $"mov qword [qword 0x{_loadLibraryWReturnValuePtr.ToString("X")}], rax", "add rsp, 40", // Re-align stack to 16 byte boundary + shadow space. "ret" // Restore stack ptr. (Callee cleanup) }; - - byte[] bytes = _assembler.Assemble(loadLibraryW); + var bytes = GetASM("BuildLoadLibraryW64", loadLibraryW); _loadLibraryWShellPtr = (long)_privateBuffer.Add(bytes); } @@ -268,7 +287,8 @@ private long WriteNullTerminatedASCIIString(string libraryPath) private long WriteNullTerminatedUnicodeString(string libraryPath) { byte[] libraryNameBytes = Encoding.Unicode.GetBytes(libraryPath + '\0'); - return (long)_circularBuffer.Add(libraryNameBytes); + var adr = _circularBuffer.Add(libraryNameBytes); + return (long)adr; } /* One off construction functions. */