//Event Calls internal void PreControllerCheck(HttpRequest request, CallDescriptor desc) { if (OnPreController != null) { OnPreController(request, desc); } }
public ControllerDescriptor(Type t, HttpServer server, bool wrapped = false) { if (server == null || t == null) { throw new ArgumentNullException("Type and Server parameter cannot be null"); } UseWrap = wrapped; Server = server; ControllerType = t; List <MethodInfo> ms = t.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly) .Where(x => !IgnoreControllerMethodAttribute.HasAttribute(x)).ToList(); foreach (MethodInfo m in ms) { CallDescriptor desc = new CallDescriptor(this, m); string name = m.Name.ToLower(); if (desc.Descriptor != null && !string.IsNullOrEmpty(desc.Descriptor.MethodName)) { name = desc.Descriptor.MethodName; } Calls.Add(m.Name.ToLower(), desc); if (desc.Descriptor != null && desc.Descriptor.IsDefault && !Calls.ContainsKey("")) { Calls.Add("", desc); } } }
internal static Span <byte> AssembleCall32(CallDescriptor <int> callDescriptor) { var shellcode = new List <byte>(); foreach (var argument in callDescriptor.Arguments.Reverse()) { switch (argument) { case >= sbyte.MinValue and <= sbyte.MaxValue: { // push argument shellcode.AddRange(new byte[] { 0x6A, unchecked ((byte)argument) }); break; } default: { // push argument shellcode.Add(0x68); shellcode.AddRange(BitConverter.GetBytes(argument)); break; } } } // mov eax, Address shellcode.Add(0xB8); shellcode.AddRange(BitConverter.GetBytes(callDescriptor.Address.ToInt32())); // call eax shellcode.AddRange(new byte[] { 0xFF, 0xD0 }); if (callDescriptor.ReturnAddress != IntPtr.Zero) { // mov ReturnAddress, eax shellcode.Add(0xA3); shellcode.AddRange(BitConverter.GetBytes(callDescriptor.ReturnAddress.ToInt32())); } // xor eax, eax shellcode.AddRange(new byte[] { 0x33, 0xC0 }); // ret shellcode.Add(0xC3); return(CollectionsMarshal.AsSpan(shellcode)); }
internal static byte[] AssembleCallDescriptor(CallDescriptor callDescriptor) { var assembledBytes = new List <byte>(); if (callDescriptor.IsWow64Call) { if (callDescriptor.CallingConvention == CallingConvention.FastCall) { // Move the first parameter into the ECX register if (callDescriptor.Parameters.Length > 0) { var parameter = callDescriptor.Parameters[0]; if (parameter == 0) { // xor ecx, ecx assembledBytes.AddRange(new byte[] { 0x31, 0xC9 }); } else { // mov ecx, parameter assembledBytes.Add(0xB9); assembledBytes.AddRange(BitConverter.GetBytes((int)parameter)); } } // Move the second parameter into the EDX register if (callDescriptor.Parameters.Length > 1) { var parameter = callDescriptor.Parameters[1]; if (parameter == 0) { // xor edx, edx assembledBytes.AddRange(new byte[] { 0x31, 0xD2 }); } else { // mov edx, parameter assembledBytes.Add(0xBA); assembledBytes.AddRange(BitConverter.GetBytes((int)parameter)); } } // Push the remaining parameters onto the stack in reverse order if (callDescriptor.Parameters.Length > 2) { foreach (var parameter in callDescriptor.Parameters[2..].Reverse())
internal T CallRoutine <T>(IntPtr routineAddress, params dynamic[] arguments) where T : unmanaged { var returnAddress = Process.AllocateBuffer(Unsafe.SizeOf <T>(), ProtectionType.ReadWrite); try { // Assemble the shellcode used to call the routine Span <byte> shellcodeBytes; if (Process.GetArchitecture() == Architecture.X86) { var callDescriptor = new CallDescriptor <int>(routineAddress, Array.ConvertAll(arguments, argument => (int)argument), returnAddress); shellcodeBytes = Assembler.AssembleCall32(callDescriptor); } else { var callDescriptor = new CallDescriptor <long>(routineAddress, Array.ConvertAll(arguments, argument => (long)argument), returnAddress); shellcodeBytes = Assembler.AssembleCall64(callDescriptor); } // Write the shellcode into the process var shellcodeAddress = Process.AllocateBuffer(shellcodeBytes.Length, ProtectionType.ExecuteRead); try { Process.WriteSpan(shellcodeAddress, shellcodeBytes); // Create a thread to execute the shellcode Process.CreateThread(shellcodeAddress); } finally { Executor.IgnoreExceptions(() => Process.FreeBuffer(shellcodeAddress)); } return(Process.ReadStruct <T>(returnAddress)); } finally { Executor.IgnoreExceptions(() => Process.FreeBuffer(returnAddress)); } }
internal T CallRoutine <T>(IntPtr routineAddress, params dynamic[] arguments) where T : unmanaged { var returnSize = typeof(T) == typeof(IntPtr) ? Architecture == Architecture.X86 ? sizeof(int) : sizeof(long) : Unsafe.SizeOf <T>(); var returnAddress = Process.AllocateBuffer(returnSize, ProtectionType.ReadWrite); try { // Assemble the shellcode used to call the routine Span <byte> shellcodeBytes; if (Architecture == Architecture.X86) { var descriptor = new CallDescriptor <int>(routineAddress, Array.ConvertAll(arguments, argument => (int)argument), returnAddress); shellcodeBytes = Assembler.AssembleCall32(descriptor); } else { var descriptor = new CallDescriptor <long>(routineAddress, Array.ConvertAll(arguments, argument => (long)argument), returnAddress); shellcodeBytes = Assembler.AssembleCall64(descriptor); } ExecuteShellcode(shellcodeBytes); // Read the return value if (typeof(T) != typeof(IntPtr)) { return(Process.ReadStruct <T>(returnAddress)); } var pointer = Architecture == Architecture.X86 ? UnsafeHelpers.WrapPointer(Process.ReadStruct <int>(returnAddress)) : UnsafeHelpers.WrapPointer(Process.ReadStruct <long>(returnAddress)); return(Unsafe.As <IntPtr, T>(ref pointer)); } finally { Executor.IgnoreExceptions(() => Process.FreeBuffer(returnAddress)); } }
internal void CallRoutine(IntPtr routineAddress, params dynamic[] arguments) { // Assemble the shellcode used to call the routine Span <byte> shellcodeBytes; if (Architecture == Architecture.X86) { var descriptor = new CallDescriptor <int>(routineAddress, Array.ConvertAll(arguments, argument => (int)argument), IntPtr.Zero); shellcodeBytes = Assembler.AssembleCall32(descriptor); } else { var descriptor = new CallDescriptor <long>(routineAddress, Array.ConvertAll(arguments, argument => (long)argument), IntPtr.Zero); shellcodeBytes = Assembler.AssembleCall64(descriptor); } ExecuteShellcode(shellcodeBytes); }
internal static void AssembleStdCallParameters(CallDescriptor callDescriptor, ref List <byte> shellcode) { foreach (var parameter in callDescriptor.Parameters.Select(p => p).Reverse()) { if (parameter <= 0x7F) { // push parameter shellcode.AddRange(new byte[] { 0x6A, (byte)parameter }); } else { // push parameter shellcode.Add(0x68); shellcode.AddRange(BitConverter.GetBytes((int)parameter)); } } }
//Handling private static void HandleRazor(HttpRequest request, CallDescriptor call, object result) { if (call.Descriptor == null) { throw new Exception("MethodDescriptor cannot be null for Razor views"); } if (!call.Descriptor.IsRazor) { throw new ArgumentNullException("RazorView parameter for MethodDescriptor cannot be null"); } string view = call.Descriptor.RazorView; if (!request.Server.RazorCache.Contains(view)) { throw new ArgumentException("Given RazorView does not exist"); } request.Response.ContentType = "text/html"; request.Write(request.Server.RazorCache.Render(view, result)); request.Close(); }
internal T CallRoutine <T>(IntPtr routineAddress, params dynamic[] arguments) where T : unmanaged { var returnAddress = Memory.AllocateBuffer(Unsafe.SizeOf <T>(), ProtectionType.ReadWrite); try { // Assemble the shellcode used to call the routine Span <byte> shellcodeBytes; if (Process.GetArchitecture() == Architecture.X86) { var callDescriptor = new CallDescriptor <int>(routineAddress, Array.ConvertAll(arguments, argument => (int)argument), returnAddress); shellcodeBytes = ShellcodeAssembler.AssembleCall32(callDescriptor); } else { var callDescriptor = new CallDescriptor <long>(routineAddress, Array.ConvertAll(arguments, argument => (long)argument), returnAddress); shellcodeBytes = ShellcodeAssembler.AssembleCall64(callDescriptor); } // Execute the shellcode ExecuteShellcode(shellcodeBytes); return(Memory.ReadStructure <T>(returnAddress)); } finally { Memory.FreeBuffer(returnAddress); } }
public bool ExecuteController(string methodName, HttpRequest request) { string mn = methodName.ToLower(); if (Calls.ContainsKey(methodName)) { if (request.IsClosed) { return(true); } BodyType responseType = Server.DefaultResponseType; //Header override string accept = request.GetHeader("Accept"); if (!string.IsNullOrEmpty(accept)) { switch (accept.ToLower()) { case "text/json": case "application/json": responseType = BodyType.JSON; break; case "text/xml": case "application/xml": responseType = BodyType.XML; break; default: //Not supported ResponseType break; } } try { CallDescriptor call = Calls[methodName]; //Pre-Controller logic request.Server.PreControllerCheck(request, call); if (request.IsClosed) { return(true); } //With descriptor if (call.Descriptor != null) { responseType = call.Descriptor.ResponseType; if (responseType == BodyType.Undefined) { responseType = Server.DefaultResponseType; } if (call.Descriptor.HasParameters) { foreach (string enforced in call.Descriptor.Parameters) { if (!request.Parameters.ContainsKey(enforced)) { throw new ArgumentException($"Missing parameter {enforced}"); } } } } object result = null; Exception ex = null; try { if (call.TokenRequirements != null) { if (!request.Authenticated || call.TokenRequirements.LevelRequired > request.AuthenticationLevel) { throw new ForbiddenException("You don't have permission to access this API method"); } if (call.TokenRequirements.RequestAttributes != null && call.TokenRequirements.RequestAttributes.Length > 0) { foreach (string attr in call.TokenRequirements.RequestAttributes) { if (!request.Attributes.Contains(attr)) { throw new ForbiddenException("You don't have permission to access this API method at this moment"); } } } } //Caching if (call.CacheHeader != null) { switch (call.CacheHeader.Cache) { case CachingType.Cache: request.Response.Headers.Add("cache-control", "max-age=" + call.CacheHeader.Validity.ToString()); break; case CachingType.Prevent: request.Response.Headers.Add("cache-control", "no-cache, no-store, must-revalidate"); request.Response.Headers.Add("Pragma", "no-cache"); request.Response.Headers.Add("Expires", "0"); break; } } //Parse Parameters object[] paras = GetParameters(call, request); //Handling result = call.Info.Invoke(GetInstance(request), paras.ToArray()); if (request.DisableAutoHandling) { return(true); } } catch (TargetInvocationException tx) { ex = tx.InnerException; } catch (Exception x) { ex = x; } if (!request.IsClosed && (result != null || ex != null)) { Type resultType; if (ex != null) { resultType = typeof(Exception); result = new APIWrap(ex); if (!Server.Debug) { ((APIWrap)result).Exception.StackTrace = ""; } if (responseType == BodyType.Raw || responseType == BodyType.Razor) { responseType = Server.DefaultResponseType; } HandleResult(Server, request, call, responseType, result, false); } else { HandleResult(Server, request, call, responseType, result, UseWrap); } } } catch (Exception ex) { if (responseType == BodyType.Razor) { responseType = Server.DefaultResponseType; } request.Write(new APIWrap(ex), responseType); } if (!request.IsClosed) { request.Close(); } return(true); } else { return(false); } }
private static byte[] AssembleShellcode(CallDescriptor callDescriptor) { var shellcode = new List <byte>(); if (callDescriptor.IsWow64Call) { // Assemble the function parameters if (callDescriptor.CallingConvention == CallingConvention.FastCall) { ParameterAssembler.AssembleFastCallParameters(callDescriptor, ref shellcode); } else { ParameterAssembler.AssembleStdCallParameters(callDescriptor, ref shellcode); } // mov eax, functionAddress shellcode.Add(0xB8); shellcode.AddRange(BitConverter.GetBytes((int)callDescriptor.FunctionAddress)); // call eax shellcode.AddRange(new byte[] { 0xFF, 0xD0 }); if (callDescriptor.ReturnAddress != IntPtr.Zero) { // mov [returnAddress], eax shellcode.Add(0xA3); shellcode.AddRange(BitConverter.GetBytes((int)callDescriptor.ReturnAddress)); } // xor eax, eax shellcode.AddRange(new byte[] { 0x33, 0xC0 }); // ret shellcode.Add(0xC3); } else { // Assemble the function parameters ParameterAssembler.AssembleFastCallParameters(callDescriptor, ref shellcode); // mov rax, functionAddress shellcode.AddRange(new byte[] { 0x48, 0xB8 }); shellcode.AddRange(BitConverter.GetBytes((long)callDescriptor.FunctionAddress)); // sub rsp, 0x28 shellcode.AddRange(new byte[] { 0x48, 0x83, 0xEC, 0x28 }); // call rax shellcode.AddRange(new byte[] { 0xFF, 0xD0 }); // add rsp, 0x28 shellcode.AddRange(new byte[] { 0x48, 0x83, 0xC4, 0x28 }); if (callDescriptor.ReturnAddress != IntPtr.Zero) { // mov [returnAddress], rax shellcode.AddRange(new byte[] { 0x48, 0xA3 }); shellcode.AddRange(BitConverter.GetBytes((long)callDescriptor.ReturnAddress)); } // xor eax, eax shellcode.AddRange(new byte[] { 0x31, 0xC0 }); // ret shellcode.Add(0xC3); } return(shellcode.ToArray()); }
private object[] GetParameters(CallDescriptor call, HttpRequest request) { List <object> paras = new List <object>(); foreach (ParameterDescriptor para in call.Parameters) { string name = para.Info.Name; Type parameterType = para.Info.ParameterType; if ((para.Descriptor != null && para.Descriptor.Type == ParameterType.Body) || name == call.Descriptor?.PostParameter) { BodyType bType = BodyType.Undefined; if (para.Descriptor != null) { bType = para.Descriptor.BodyType; } if (call.Descriptor != null && bType == BodyType.Undefined) { bType = call.Descriptor.RequestType; } if (bType == BodyType.Undefined) { bType = Server.DefaultRequestType; string contentType = request.GetHeader("Content-Type"); if (contentType != null) { switch (contentType.ToLower()) { case "text/json": case "application/json": bType = BodyType.JSON; break; case "text/xml": case "application/xml": bType = BodyType.XML; break; case "application/x-www-form-urlencoded": bType = BodyType.UrlEncoded; break; case "multipart/form-data": bType = BodyType.MultipartStream; break; } } } paras.Add(request.GetDataObject(parameterType, bType, call)); } else { if (para.Descriptor != null) { if (!string.IsNullOrEmpty(para.Descriptor.Name)) { name = para.Descriptor.Name; } if (!para.Descriptor.Optional && request.Parameters.ContainsKey(name)) { throw new ArgumentException($"Parameter {name} missing"); } } string data = null; request.Parameters.TryGetValue(name, out data); if (data != null) { paras.Add(ParameterConverter.Static.Parse(parameterType, request.Parameters[name])); } else { paras.Add((parameterType.IsValueType) ? Activator.CreateInstance(parameterType) : null); } } } return(paras.ToArray()); }
private static void HandleResult(HttpServer server, HttpRequest request, CallDescriptor call, BodyType responseType, object result, bool useWrap) { Type resultType = result.GetType(); if (useWrap && responseType != BodyType.Raw && responseType != BodyType.Razor) { result = new APIWrap(result); } if (!server?.IsAllowedResponse(responseType) ?? false) { throw new Exceptions.ConfigurationException("Requested response type is not allowed"); } switch (responseType) { case BodyType.Razor: HandleRazor(request, call, result); break; case BodyType.Raw: if (resultType == typeof(byte[])) { request.Response.ContentType = "application/octet-stream"; request.Write((byte[])result); } else { request.Response.ContentType = "text/plain"; request.Write(result.ToString()); } break; case BodyType.JSON: request.Response.ContentType = "application/json"; JsonSerializerSettings settings = call.JsonSerialization?.Response; if (settings != null) { request.Write(JsonConvert.SerializeObject(result, settings)); } else { request.Write(JsonConvert.SerializeObject(result)); } break; case BodyType.XML: if (useWrap) { XmlParser.AddSubType(typeof(APIWrap), resultType); } request.Response.ContentType = "application/xml"; request.Write(XmlParser.Serialize(result)); break; case BodyType.UrlEncoded: throw new NotSupportedException(); case BodyType.MultipartStream: throw new NotSupportedException(); } }
internal static Span <byte> AssembleCall64(CallDescriptor <long> callDescriptor) { var shellcode = new List <byte>(); var shadowSpaceSize = Constants.ShadowSpaceSize + sizeof(long) * Math.Max(0, callDescriptor.Arguments.Count - 4); // sub rsp, shadowSpaceSize shellcode.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 shellcode.AddRange(new byte[] { 0x31, 0xC9 }); break; } case >= int.MinValue and <= uint.MaxValue: { // mov ecx, argument shellcode.Add(0xB9); shellcode.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov rcx, argument shellcode.AddRange(new byte[] { 0x48, 0xB9 }); shellcode.AddRange(BitConverter.GetBytes(argument)); break; } } } if (callDescriptor.Arguments.Count > 1) { var argument = callDescriptor.Arguments[1]; switch (argument) { case 0: { // xor edx, edx shellcode.AddRange(new byte[] { 0x31, 0xD2 }); break; } case >= int.MinValue and <= uint.MaxValue: { // mov edx, argument shellcode.Add(0xBA); shellcode.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov rdx, argument shellcode.AddRange(new byte[] { 0x48, 0xBA }); shellcode.AddRange(BitConverter.GetBytes(argument)); break; } } } if (callDescriptor.Arguments.Count > 2) { var argument = callDescriptor.Arguments[2]; switch (argument) { case 0: { // xor r8, r8 shellcode.AddRange(new byte[] { 0x4D, 0x31, 0xC0 }); break; } case >= int.MinValue and <= uint.MaxValue: { // mov r8d, argument shellcode.AddRange(new byte[] { 0x41, 0xB8 }); shellcode.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov r8, argument shellcode.AddRange(new byte[] { 0x49, 0xB8 }); shellcode.AddRange(BitConverter.GetBytes(argument)); break; } } } if (callDescriptor.Arguments.Count > 3) { var argument = callDescriptor.Arguments[3]; switch (argument) { case 0: { // xor r9, r9 shellcode.AddRange(new byte[] { 0x4D, 0x31, 0xC9 }); break; } case >= int.MinValue and <= uint.MaxValue: { // mov r9d, argument shellcode.AddRange(new byte[] { 0x41, 0xB9 }); shellcode.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov r9, argument shellcode.AddRange(new byte[] { 0x49, 0xB9 }); shellcode.AddRange(BitConverter.GetBytes(argument)); break; } } } if (callDescriptor.Arguments.Count > 4) { foreach (var argument in callDescriptor.Arguments.Skip(4).Reverse()) { switch (argument) { case >= sbyte.MinValue and <= sbyte.MaxValue: { // push argument shellcode.AddRange(new byte[] { 0x6A, unchecked ((byte)argument) }); break; } case >= int.MinValue and <= int.MaxValue: { // push argument shellcode.Add(0x68); shellcode.AddRange(BitConverter.GetBytes((int)argument)); break; } default: { // mov rax, argument shellcode.AddRange(new byte[] { 0x48, 0xB8 }); shellcode.AddRange(BitConverter.GetBytes(argument)); // push rax shellcode.Add(0x50); break; } } } } // mov rax, Address shellcode.AddRange(new byte[] { 0x48, 0xB8 }); shellcode.AddRange(BitConverter.GetBytes(callDescriptor.Address.ToInt64())); // call rax shellcode.AddRange(new byte[] { 0xFF, 0xD0 }); if (callDescriptor.ReturnAddress != IntPtr.Zero) { // mov [ReturnAddress], rax shellcode.AddRange(new byte[] { 0x48, 0xA3 }); shellcode.AddRange(BitConverter.GetBytes(callDescriptor.ReturnAddress.ToInt64())); } // xor eax, eax shellcode.AddRange(new byte[] { 0x31, 0xC0 }); // add rsp, shadowSpaceSize shellcode.AddRange(new byte[] { 0x48, 0x83, 0xC4, (byte)shadowSpaceSize }); // ret shellcode.Add(0xC3); return(CollectionsMarshal.AsSpan(shellcode)); }
public void CallFunction(CallDescriptor callDescriptor) { // Write the shellcode used to perform the function call into a buffer var shellcode = AssembleShellcode(callDescriptor); var shellcodeBuffer = _memory.AllocateBlock(IntPtr.Zero, shellcode.Length, ProtectionType.ReadWrite); _memory.WriteBlock(shellcodeBuffer, shellcode); _memory.ProtectBlock(shellcodeBuffer, shellcode.Length, ProtectionType.ExecuteRead); // Create a suspended thread with a spoofed start address var ntStatus = Ntdll.NtCreateThreadEx(out var threadHandle, AccessMask.SpecificRightsAll | AccessMask.StandardRightsAll, IntPtr.Zero, _process.SafeHandle, _process.MainModule.BaseAddress, IntPtr.Zero, ThreadCreationFlags.CreateSuspended | ThreadCreationFlags.HideFromDebugger, 0, 0, 0, IntPtr.Zero); if (ntStatus != NtStatus.Success) { throw new Win32Exception($"Failed to call NtCreateThreadEx with error code {ntStatus}"); } if (callDescriptor.IsWow64Call) { // Get the context of the thread var threadContext = new Wow64Context { ContextFlags = Wow64ContextFlags.Integer }; if (!Kernel32.Wow64GetThreadContext(threadHandle, ref threadContext)) { throw new Win32Exception($"Failed to call Wow64GetThreadContext with error code {Marshal.GetLastWin32Error()}"); } // Change the spoofed start address to the address of the shellcode threadContext.Eax = (int)shellcodeBuffer; // Update the context of the thread if (!Kernel32.Wow64SetThreadContext(threadHandle, ref threadContext)) { throw new Win32Exception($"Failed to call Wow64GetThreadContext with error code {Marshal.GetLastWin32Error()}"); } } else { // Get the context of the thread var threadContext = new Context { ContextFlags = ContextFlags.Integer }; var threadContextBuffer = Marshal.AllocHGlobal(Unsafe.SizeOf <Context>()); Marshal.StructureToPtr(threadContext, threadContextBuffer, false); if (!Kernel32.GetThreadContext(threadHandle, threadContextBuffer)) { throw new Win32Exception($"Failed to call GetThreadContext with error code {Marshal.GetLastWin32Error()}"); } threadContext = Marshal.PtrToStructure <Context>(threadContextBuffer); // Change the spoofed start address to the address of the shellcode threadContext.Rcx = (long)shellcodeBuffer; Marshal.StructureToPtr(threadContext, threadContextBuffer, false); // Update the context of the thread if (!Kernel32.SetThreadContext(threadHandle, threadContextBuffer)) { throw new Win32Exception($"Failed to call SetThreadContext with error code {Marshal.GetLastWin32Error()}"); } Marshal.FreeHGlobal(threadContextBuffer); } // Resume the thread if (Kernel32.ResumeThread(threadHandle) == -1) { throw new Win32Exception($"Failed to call ResumeThread with error code {Marshal.GetLastWin32Error()}"); } if (Kernel32.WaitForSingleObject(threadHandle, int.MaxValue) == -1) { throw new Win32Exception($"Failed to call WaitForSingleObject with error code {Marshal.GetLastWin32Error()}"); } threadHandle.Dispose(); _memory.FreeBlock(shellcodeBuffer); }
private static byte[] AssembleShellcode(CallDescriptor callDescriptor, IntPtr completionFlagBuffer) { var shellcode = new List <byte>(); if (callDescriptor.IsWow64Call) { // pushf shellcode.Add(0x9C); // pusha shellcode.Add(0x60); // Assemble the function parameters if (callDescriptor.CallingConvention == CallingConvention.FastCall) { ParameterAssembler.AssembleFastCallParameters(callDescriptor, ref shellcode); } else { ParameterAssembler.AssembleStdCallParameters(callDescriptor, ref shellcode); } // mov eax, functionAddress shellcode.Add(0xB8); shellcode.AddRange(BitConverter.GetBytes((int)callDescriptor.FunctionAddress)); // call eax shellcode.AddRange(new byte[] { 0xFF, 0xD0 }); if (callDescriptor.ReturnAddress != IntPtr.Zero) { // mov [returnAddress], eax shellcode.Add(0xA3); shellcode.AddRange(BitConverter.GetBytes((int)callDescriptor.ReturnAddress)); } // mov BYTE PTR [completionFlagBuffer], 0x01 shellcode.AddRange(new byte[] { 0xC6, 0x05 }); shellcode.AddRange(BitConverter.GetBytes((int)completionFlagBuffer)); shellcode.Add(0x01); // popa shellcode.Add(0x61); // popf shellcode.Add(0x9D); // ret shellcode.Add(0xC3); } else { // pushf shellcode.Add(0x9C); // push rax shellcode.Add(0x50); // push rbx shellcode.Add(0x53); // push rcx shellcode.Add(0x51); // push rdx shellcode.Add(0x52); // push r8 shellcode.AddRange(new byte[] { 0x41, 0x50 }); // push r9 shellcode.AddRange(new byte[] { 0x41, 0x51 }); // push r10 shellcode.AddRange(new byte[] { 0x41, 0x52 }); // push r11 shellcode.AddRange(new byte[] { 0x41, 0x53 }); // Assemble the function parameters ParameterAssembler.AssembleFastCallParameters(callDescriptor, ref shellcode); // mov rax, functionAddress shellcode.AddRange(new byte[] { 0x48, 0xB8 }); shellcode.AddRange(BitConverter.GetBytes((long)callDescriptor.FunctionAddress)); // sub rsp, 0x28 shellcode.AddRange(new byte[] { 0x48, 0x83, 0xEC, 0x28 }); // call rax shellcode.AddRange(new byte[] { 0xFF, 0xD0 }); // add rsp, 0x28 shellcode.AddRange(new byte[] { 0x48, 0x83, 0xC4, 0x28 }); if (callDescriptor.ReturnAddress != IntPtr.Zero) { // mov [returnAddress], rax shellcode.AddRange(new byte[] { 0x48, 0xA3 }); shellcode.AddRange(BitConverter.GetBytes((long)callDescriptor.ReturnAddress)); } // mov rax, completionFlagBuffer shellcode.AddRange(new byte[] { 0x48, 0xB8 }); shellcode.AddRange(BitConverter.GetBytes((long)completionFlagBuffer)); // mov BYTE PTR [rax], 0x01 shellcode.AddRange(new byte[] { 0xC6, 0x00, 0x01 }); // pop r11 shellcode.AddRange(new byte[] { 0x41, 0x5B }); // pop r10 shellcode.AddRange(new byte[] { 0x41, 0x5A }); // pop r9 shellcode.AddRange(new byte[] { 0x41, 0x59 }); // pop r8 shellcode.AddRange(new byte[] { 0x41, 0x58 }); // pop rdx shellcode.Add(0x5A); // pop rcx shellcode.Add(0x59); // pop rbx shellcode.Add(0x5B); // pop rax shellcode.Add(0x58); // popf shellcode.Add(0x9D); // ret shellcode.Add(0xC3); } return(shellcode.ToArray()); }
public void CallFunction(CallDescriptor callDescriptor) { var completionFlagBuffer = _memory.AllocateBlock(IntPtr.Zero, sizeof(bool), ProtectionType.ReadWrite); // Write the shellcode used to perform the function call into a buffer var shellcode = AssembleShellcode(callDescriptor, completionFlagBuffer); var shellcodeBuffer = _memory.AllocateBlock(IntPtr.Zero, shellcode.Length, ProtectionType.ReadWrite); _memory.WriteBlock(shellcodeBuffer, shellcode); _memory.ProtectBlock(shellcodeBuffer, shellcode.Length, ProtectionType.ExecuteRead); // Open a handle to the first thread var firstThreadHandle = Kernel32.OpenThread(AccessMask.SpecificRightsAll | AccessMask.StandardRightsAll, false, _process.Threads[0].Id); if (firstThreadHandle.IsInvalid) { throw new Win32Exception($"Failed to call OpenThread with error code {Marshal.GetLastWin32Error()}"); } if (callDescriptor.IsWow64Call) { // Suspend the thread if (Kernel32.Wow64SuspendThread(firstThreadHandle) == -1) { throw new Win32Exception($"Failed to call Wow64SuspendThread with error code {Marshal.GetLastWin32Error()}"); } // Get the context of the thread var threadContext = new Wow64Context { ContextFlags = Wow64ContextFlags.Control }; if (!Kernel32.Wow64GetThreadContext(firstThreadHandle, ref threadContext)) { throw new Win32Exception($"Failed to call Wow64GetThreadContext with error code {Marshal.GetLastWin32Error()}"); } // Write the original instruction pointer of the thread into the top of its stack threadContext.Esp -= sizeof(int); _memory.Write((IntPtr)threadContext.Esp, threadContext.Eip); // Overwrite the instruction pointer of the thread with the address of the shellcode threadContext.Eip = (int)shellcodeBuffer; // Update the context of the thread if (!Kernel32.Wow64SetThreadContext(firstThreadHandle, ref threadContext)) { throw new Win32Exception($"Failed to call Wow64SetThreadContext with error code {Marshal.GetLastWin32Error()}"); } } else { // Suspend the thread if (Kernel32.SuspendThread(firstThreadHandle) == -1) { throw new Win32Exception($"Failed to call SuspendThread with error code {Marshal.GetLastWin32Error()}"); } // Get the context of the thread var threadContext = new Context { ContextFlags = ContextFlags.Control }; var threadContextBuffer = Marshal.AllocHGlobal(Unsafe.SizeOf <Context>()); Marshal.StructureToPtr(threadContext, threadContextBuffer, false); if (!Kernel32.GetThreadContext(firstThreadHandle, threadContextBuffer)) { throw new Win32Exception($"Failed to call GetThreadContext with error code {Marshal.GetLastWin32Error()}"); } threadContext = Marshal.PtrToStructure <Context>(threadContextBuffer); // Write the original instruction pointer of the thread into the top of its stack threadContext.Rsp -= sizeof(long); _memory.Write((IntPtr)threadContext.Rsp, threadContext.Rip); // Overwrite the instruction pointer of the thread with the address of the shellcode threadContext.Rip = (long)shellcodeBuffer; Marshal.StructureToPtr(threadContext, threadContextBuffer, false); // Update the context of the thread if (!Kernel32.SetThreadContext(firstThreadHandle, threadContextBuffer)) { throw new Win32Exception($"Failed to call SetThreadContext with error code {Marshal.GetLastWin32Error()}"); } Marshal.FreeHGlobal(threadContextBuffer); } // Send a message to the thread to ensure it executes the shellcode if (!User32.PostThreadMessage(_process.Threads[0].Id, MessageType.Null, IntPtr.Zero, IntPtr.Zero)) { throw new Win32Exception($"Failed to call PostThreadMessage with error code {Marshal.GetLastWin32Error()}"); } // Resume the thread if (Kernel32.ResumeThread(firstThreadHandle) == -1) { throw new Win32Exception($"Failed to call ResumeThread with error code {Marshal.GetLastWin32Error()}"); } while (!_memory.Read <bool>(completionFlagBuffer)) { Thread.Sleep(1); } firstThreadHandle.Dispose(); _memory.FreeBlock(shellcodeBuffer); _memory.FreeBlock(completionFlagBuffer); }
internal static void AssembleFastCallParameters(CallDescriptor callDescriptor, ref List <byte> shellcode) { var stackParameters = new List <byte>(); var parameterIndex = 0; if (callDescriptor.IsWow64Call) { foreach (var parameter in callDescriptor.Parameters) { switch (parameterIndex) { case 0: { if (parameter == 0) { // xor ecx, ecx shellcode.AddRange(new byte[] { 0x31, 0xC9 }); } else { // mov ecx, parameter shellcode.Add(0xB9); shellcode.AddRange(BitConverter.GetBytes((int)parameter)); } parameterIndex += 1; break; } case 1: { if (parameter == 0) { // xor edx, edx shellcode.AddRange(new byte[] { 0x31, 0xD2 }); } else { // mov edx, parameter shellcode.Add(0xBA); shellcode.AddRange(BitConverter.GetBytes((int)parameter)); } parameterIndex += 1; break; } default: { if (parameter <= 0x7F) { // push parameter stackParameters.InsertRange(0, new byte[] { 0x6A, (byte)parameter }); } else { // push parameter var operation = new List <byte> { 0x68 }; operation.AddRange(BitConverter.GetBytes((int)parameter)); stackParameters.InsertRange(0, operation); } break; } } } } else { foreach (var parameter in callDescriptor.Parameters) { switch (parameterIndex) { case 0: { if (parameter == 0) { // xor ecx, ecx shellcode.AddRange(new byte[] { 0x31, 0xC9 }); } else { // mov rcx, parameter shellcode.AddRange(new byte[] { 0x48, 0xB9 }); shellcode.AddRange(BitConverter.GetBytes(parameter)); } parameterIndex += 1; break; } case 1: { if (parameter == 0) { // xor edx, edx shellcode.AddRange(new byte[] { 0x31, 0xD2 }); } else { // mov rdx, parameter shellcode.AddRange(new byte[] { 0x48, 0xBA }); shellcode.AddRange(BitConverter.GetBytes(parameter)); } parameterIndex += 1; break; } case 2: { if (parameter == 0) { // xor r8, r8 shellcode.AddRange(new byte[] { 0x4D, 0x31, 0xC0 }); } else { // mov r8, parameter shellcode.AddRange(new byte[] { 0x49, 0xB8 }); shellcode.AddRange(BitConverter.GetBytes(parameter)); } parameterIndex += 1; break; } case 3: { if (parameter == 0) { // xor r9, r9 shellcode.AddRange(new byte[] { 0x4D, 0x31, 0xC9 }); } else { // mov r9, parameter shellcode.AddRange(new byte[] { 0x49, 0xB9 }); shellcode.AddRange(BitConverter.GetBytes(parameter)); } parameterIndex += 1; break; } default: { if (parameter <= 0x7F) { // push parameter stackParameters.InsertRange(0, new byte[] { 0x6A, (byte)parameter }); } else { var operation = new List <byte>(); if (parameter < int.MaxValue) { // push parameter operation.Add(0x68); operation.AddRange(BitConverter.GetBytes((int)parameter)); } else { // mov rax, parameter operation.AddRange(new byte[] { 0x48, 0xB8 }); operation.AddRange(BitConverter.GetBytes(parameter)); // push rax operation.Add(0x50); } stackParameters.InsertRange(0, operation); } break; } } } } shellcode.AddRange(stackParameters); }
//Data public object GetDataObject(Type type, BodyType body = BodyType.Undefined, CallDescriptor call = null) { if (body == BodyType.Undefined) { char firstCharacter = DataString.Trim().FirstOrDefault(); switch (firstCharacter) { case '{': case '[': body = BodyType.JSON; break; case '<': body = BodyType.XML; break; default: if (DataString.Contains("=")) { body = BodyType.UrlEncoded; } break; } } switch (body) { case BodyType.JSON: JsonSerializerSettings settings = call?.JsonSerialization?.Request; if (settings != null) { return(JsonConvert.DeserializeObject(DataString, type, settings)); } else { return(JsonConvert.DeserializeObject(DataString, type)); } case BodyType.XML: return(XmlConvert.DeserializeObject(DataString, type)); case BodyType.UrlEncoded: object targetObj = Activator.CreateInstance(type); foreach (PropertyInfo info in type.GetPropertiesCached()) { if (DataUrlEncoded.ContainsKey(info.Name)) { info.SetValue(targetObj, ParameterConverter.Static.Parse(info.PropertyType, DataUrlEncoded[info.Name])); } } return(targetObj); case BodyType.Raw: if (type == typeof(string)) { return(DataString); } else if (type == typeof(byte[])) { return(Data); } else { return(null); } case BodyType.MultipartStream: return(new MultiPartStream(DataStream)); default: return(null); } }