internal T CallRoutine <T>(IntPtr routineAddress, params dynamic[] arguments) where T : unmanaged { var returnAddress = Process.AllocateMemory(Unsafe.SizeOf <T>()); // Create the shellcode used to call the routine Span <byte> shellcodeBytes; if (Process.GetArchitecture() == Architecture.X86) { var callDescriptor = new CallDescriptor32(routineAddress, Array.ConvertAll(arguments, argument => (int)argument), returnAddress); shellcodeBytes = CallAssembler.AssembleCall32(callDescriptor); } else { var routineDescriptor = new CallDescriptor64(routineAddress, Array.ConvertAll(arguments, argument => (long)argument), returnAddress); shellcodeBytes = CallAssembler.AssembleCall64(routineDescriptor); } try { // Write the shellcode bytes into the process var shellcodeBytesAddress = Process.AllocateMemory(shellcodeBytes.Length, true); try { Process.WriteArray(shellcodeBytesAddress, shellcodeBytes); // Create a thread to execute the shellcode Process.CreateThread(shellcodeBytesAddress); } finally { Process.FreeMemory(shellcodeBytesAddress); } return(Process.ReadStructure <T>(returnAddress)); } finally { Process.FreeMemory(returnAddress); } }
internal void CallRoutine(IntPtr routineAddress, params dynamic[] arguments) { // Create the shellcode used to call the routine Span <byte> shellcodeBytes; if (Process.GetArchitecture() == Architecture.X86) { var callDescriptor = new CallDescriptor32(routineAddress, Array.ConvertAll(arguments, argument => (int)argument), IntPtr.Zero); shellcodeBytes = CallAssembler.AssembleCall32(callDescriptor); } else { var routineDescriptor = new CallDescriptor64(routineAddress, Array.ConvertAll(arguments, argument => (long)argument), IntPtr.Zero); shellcodeBytes = CallAssembler.AssembleCall64(routineDescriptor); } // Write the shellcode bytes into the process var shellcodeBytesAddress = Process.AllocateMemory(shellcodeBytes.Length, true); try { Process.WriteArray(shellcodeBytesAddress, shellcodeBytes); // Create a thread to execute the shellcode Process.CreateThread(shellcodeBytesAddress); } finally { Process.FreeMemory(shellcodeBytesAddress); } }
internal static Span <byte> AssembleCall64(CallDescriptor64 callDescriptor) { var instructions = new List <byte>(); var shadowSpaceSize = Constants.ShadowSpaceSize + Math.Max(0, callDescriptor.Arguments.Count - 4); // sub rsp, shadowSpaceSize instructions.AddRange(new byte[] { 0x48, 0x83, 0xEC, (byte)shadowSpaceSize }); if (callDescriptor.Arguments.Count > 0) { var argument = callDescriptor.Arguments[0]; switch (argument) { case 0: { // xor ecx, ecx instructions.AddRange(new byte[] { 0x31, 0xC9 }); break; } case <= uint.MaxValue: { // mov ecx, argument instructions.Add(0xB9); instructions.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov rcx, argument instructions.AddRange(new byte[] { 0x48, 0xB9 }); instructions.AddRange(BitConverter.GetBytes(argument)); break; } } } if (callDescriptor.Arguments.Count > 1) { var argument = callDescriptor.Arguments[1]; switch (argument) { case 0: { // xor edx, edx instructions.AddRange(new byte[] { 0x31, 0xD2 }); break; } case <= uint.MaxValue: { // mov edx, argument instructions.Add(0xBA); instructions.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov rdx, argument instructions.AddRange(new byte[] { 0x48, 0xBA }); instructions.AddRange(BitConverter.GetBytes(argument)); break; } } } if (callDescriptor.Arguments.Count > 2) { var argument = callDescriptor.Arguments[2]; switch (argument) { case 0: { // xor r8, r8 instructions.AddRange(new byte[] { 0x4D, 0x31, 0xC0 }); break; } case <= uint.MaxValue: { // mov r8d, argument instructions.AddRange(new byte[] { 0x41, 0xB8 }); instructions.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov r8, argument instructions.AddRange(new byte[] { 0x49, 0xB8 }); instructions.AddRange(BitConverter.GetBytes(argument)); break; } } } if (callDescriptor.Arguments.Count > 3) { var argument = callDescriptor.Arguments[3]; switch (argument) { case 0: { // xor r9, r9 instructions.AddRange(new byte[] { 0x4D, 0x31, 0xC9 }); break; } case <= uint.MaxValue: { // mov r9d, argument instructions.AddRange(new byte[] { 0x41, 0xB9 }); instructions.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov r9, argument instructions.AddRange(new byte[] { 0x49, 0xB9 }); instructions.AddRange(BitConverter.GetBytes(argument)); break; } } } if (callDescriptor.Arguments.Count > 4) { foreach (var argument in callDescriptor.Arguments.Skip(4).Reverse()) { switch (argument) { case <= sbyte.MaxValue: { // push argument instructions.AddRange(new byte[] { 0x6A, (byte)argument }); break; } case <= uint.MaxValue: { // push argument instructions.Add(0x68); instructions.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov rax, argument instructions.AddRange(new byte[] { 0x48, 0xB8 }); instructions.AddRange(BitConverter.GetBytes(argument)); // push rax instructions.Add(0x50); break; } } } } // mov rax, Address instructions.AddRange(new byte[] { 0x48, 0xB8 }); instructions.AddRange(BitConverter.GetBytes(callDescriptor.Address.ToInt64())); // call rax instructions.AddRange(new byte[] { 0xFF, 0xD0 }); if (callDescriptor.ReturnAddress != IntPtr.Zero) { // mov ReturnAddress, rax instructions.AddRange(new byte[] { 0x48, 0xA3 }); instructions.AddRange(BitConverter.GetBytes(callDescriptor.ReturnAddress.ToInt64())); } // xor eax, eax instructions.AddRange(new byte[] { 0x31, 0xC0 }); // add rsp, shadowSpaceSize instructions.AddRange(new byte[] { 0x48, 0x83, 0xC4, (byte)shadowSpaceSize }); // ret instructions.Add(0xC3); return(CollectionsMarshal.AsSpan(instructions)); }
internal static Span <byte> AssembleCall64(CallDescriptor64 callDescriptor) { var instructions = new List <byte>(); // sub rsp, 0x28 instructions.AddRange(new byte[] { 0x48, 0x83, 0xEC, 0x28 }); foreach (var(argument, argumentIndex) in callDescriptor.Arguments.Select((argument, argumentIndex) => (argument, argumentIndex))) { switch (argumentIndex) { case 0: { switch (argument) { case 0: { // xor ecx, ecx instructions.AddRange(new byte[] { 0x31, 0xC9 }); break; } case <= uint.MaxValue: { // mov ecx, argument instructions.Add(0xB9); instructions.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov rcx, argument instructions.AddRange(new byte[] { 0x48, 0xB9 }); instructions.AddRange(BitConverter.GetBytes(argument)); break; } } break; } case 1: { switch (argument) { case 0: { // xor edx, edx instructions.AddRange(new byte[] { 0x31, 0xD2 }); break; } case <= uint.MaxValue: { // mov edx, argument instructions.Add(0xBA); instructions.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov rdx, argument instructions.AddRange(new byte[] { 0x48, 0xBA }); instructions.AddRange(BitConverter.GetBytes(argument)); break; } } break; } case 2: { switch (argument) { case 0: { // xor r8d, r8d instructions.AddRange(new byte[] { 0x45, 0x31, 0xC0 }); break; } case <= uint.MaxValue: { // mov r8d, argument instructions.AddRange(new byte[] { 0x41, 0xB8 }); instructions.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov r8, argument instructions.AddRange(new byte[] { 0x49, 0xB8 }); instructions.AddRange(BitConverter.GetBytes(argument)); break; } } break; } case 3: { switch (argument) { case 0: { // xor r9d, r9d instructions.AddRange(new byte[] { 0x45, 0x31, 0xC9 }); break; } case <= uint.MaxValue: { // mov r9d, argument instructions.AddRange(new byte[] { 0x41, 0xB9 }); instructions.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov r9, argument instructions.AddRange(new byte[] { 0x49, 0xB9 }); instructions.AddRange(BitConverter.GetBytes(argument)); break; } } break; } } } // mov rax, Address instructions.AddRange(new byte[] { 0x48, 0xB8 }); instructions.AddRange(BitConverter.GetBytes(callDescriptor.Address.ToInt64())); // call rax instructions.AddRange(new byte[] { 0xFF, 0xD0 }); if (callDescriptor.ReturnAddress != IntPtr.Zero) { // mov ReturnAddress, rax instructions.AddRange(new byte[] { 0x48, 0xA3 }); instructions.AddRange(BitConverter.GetBytes(callDescriptor.ReturnAddress.ToInt64())); } // xor eax, eax instructions.AddRange(new byte[] { 0x31, 0xC0 }); // add rsp, 0x28 instructions.AddRange(new byte[] { 0x48, 0x83, 0xC4, 0x28 }); // ret instructions.Add(0xC3); return(instructions.ToArray()); }
internal static Span <byte> AssembleCall64(CallDescriptor64 callDescriptor) { var instructions = new List <byte>(); // sub rsp, 0x28 instructions.AddRange(new byte[] { 0x48, 0x83, 0xEC, 0x28 }); if (callDescriptor.Arguments.Length > 0) { var argument = callDescriptor.Arguments[0]; switch (argument) { case 0: { // xor ecx, ecx instructions.AddRange(new byte[] { 0x31, 0xC9 }); break; } case <= uint.MaxValue: { // mov ecx, argument instructions.Add(0xB9); instructions.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov rcx, argument instructions.AddRange(new byte[] { 0x48, 0xB9 }); instructions.AddRange(BitConverter.GetBytes(argument)); break; } } } if (callDescriptor.Arguments.Length > 1) { var argument = callDescriptor.Arguments[1]; switch (argument) { case 0: { // xor edx, edx instructions.AddRange(new byte[] { 0x31, 0xD2 }); break; } case <= uint.MaxValue: { // mov edx, argument instructions.Add(0xBA); instructions.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov rdx, argument instructions.AddRange(new byte[] { 0x48, 0xBA }); instructions.AddRange(BitConverter.GetBytes(argument)); break; } } } if (callDescriptor.Arguments.Length > 2) { var argument = callDescriptor.Arguments[2]; switch (argument) { case 0: { // xor r8d, r8d instructions.AddRange(new byte[] { 0x45, 0x31, 0xC0 }); break; } case <= uint.MaxValue: { // mov r8d, argument instructions.AddRange(new byte[] { 0x41, 0xB8 }); instructions.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov r8, argument instructions.AddRange(new byte[] { 0x49, 0xB8 }); instructions.AddRange(BitConverter.GetBytes(argument)); break; } } } if (callDescriptor.Arguments.Length > 3) { var argument = callDescriptor.Arguments[3]; switch (argument) { case 0: { // xor r9d, r9d instructions.AddRange(new byte[] { 0x45, 0x31, 0xC9 }); break; } case <= uint.MaxValue: { // mov r9d, argument instructions.AddRange(new byte[] { 0x41, 0xB9 }); instructions.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov r9, argument instructions.AddRange(new byte[] { 0x49, 0xB9 }); instructions.AddRange(BitConverter.GetBytes(argument)); break; } } } if (callDescriptor.Arguments.Length > 4) { foreach (var argument in callDescriptor.Arguments[4..].Reverse())