├── src ├── ptinfo.h ├── RemoteJacker.h ├── ptinfo.cpp └── RemoteJacker.cpp ├── example.cpp ├── LICENSE └── README.md /src/ptinfo.h: -------------------------------------------------------------------------------- 1 | #ifndef PTINFO 2 | #define PTINFO 3 | #include 4 | #include 5 | #include 6 | 7 | typedef unsigned long ULong; 8 | typedef void* NTPtr; 9 | typedef unsigned char UByte; 10 | 11 | ULong FindProcessIdFromProcessName(const std::wstring processName); 12 | ULong GetModuleBase(const ULong dwProcessId, LPCWSTR szModuleName); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/RemoteJacker.h: -------------------------------------------------------------------------------- 1 | #ifndef REMOTEJACKER 2 | #define REMOTEJACKER 3 | #include "ptinfo.h" 4 | 5 | ULong GetDispatcher(ULong ProcessId); 6 | ULong MarkShellCode(ULong ProcessId, ULong Shellcode); 7 | void WriteHook(HANDLE Process, ULong BaseAddress, ULong ShellCode); 8 | void SetDispatcher(ULong ProcessId, ULong Address, ULong ShellCode); 9 | void RemoteJack(ULong ProcessId, ULong Target); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /example.cpp: -------------------------------------------------------------------------------- 1 | #include "RemoteJacker.h" 2 | 3 | int main(void) 4 | { 5 | ULong PId = FindProcessIdFromProcessName(L"test.exe"); 6 | ULong Dispatcher = GetDispatcher(PId); 7 | ULong ShellCode = 0x04E90000; 8 | ULong TargetVal = 0x00C92D50; 9 | ULong Protection = MarkShellCode(PId, ShellCode); 10 | 11 | SetDispatcher(PId, Dispatcher, ShellCode); 12 | RemoteJack(PId, TargetVal); 13 | 14 | return 0; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jason Johnson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/ptinfo.cpp: -------------------------------------------------------------------------------- 1 | #include "ptinfo.h" 2 | 3 | ULong FindProcessIdFromProcessName(const std::wstring processName) 4 | { 5 | PROCESSENTRY32 processInfo; 6 | processInfo.dwSize = sizeof(processInfo); 7 | 8 | HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); 9 | if (processesSnapshot == INVALID_HANDLE_VALUE) 10 | return 0; 11 | 12 | Process32First(processesSnapshot, &processInfo); 13 | if (!processName.compare(processInfo.szExeFile)) { 14 | CloseHandle(processesSnapshot); 15 | return processInfo.th32ProcessID; 16 | } 17 | 18 | while (Process32Next(processesSnapshot, &processInfo)) { 19 | if (!processName.compare(processInfo.szExeFile)) { 20 | CloseHandle(processesSnapshot); 21 | return processInfo.th32ProcessID; 22 | } 23 | } 24 | 25 | CloseHandle(processesSnapshot); 26 | return 0; 27 | } 28 | 29 | 30 | ULong GetModuleBase(const ULong dwProcessId, LPCWSTR szModuleName) 31 | { 32 | HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); 33 | if (!hSnap) 34 | return 0; 35 | 36 | MODULEENTRY32 me; 37 | me.dwSize = sizeof(MODULEENTRY32); 38 | ULong dwReturn = 0; 39 | if (Module32First(hSnap, &me)) 40 | { 41 | do 42 | { 43 | if (lstrcmpi(me.szModule, szModuleName) == 0) 44 | { 45 | dwReturn = (ULong)me.modBaseAddr; 46 | break; 47 | } 48 | } while (Module32Next(hSnap, &me)); 49 | } 50 | CloseHandle(hSnap); 51 | return dwReturn; 52 | } 53 | -------------------------------------------------------------------------------- /src/RemoteJacker.cpp: -------------------------------------------------------------------------------- 1 | #include "RemoteJacker.h" 2 | 3 | ULong GetDispatcher(ULong ProcessId) 4 | { 5 | NTPtr Dispatcher = (NTPtr)GetProcAddress(GetModuleHandleA("ntdll.dll"), "KiUserExceptionDispatcher"); 6 | HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); 7 | 8 | if (!Process) 9 | return NULL; 10 | else 11 | CloseHandle(Process); 12 | 13 | ULong Difference = ((ULong)Dispatcher - (ULong)GetModuleHandleA("ntdll.dll")); 14 | return (GetModuleBase(ProcessId, L"ntdll.dll") + Difference + 1); 15 | } 16 | 17 | ULong MarkShellCode(ULong ProcessId, ULong Shellcode) 18 | { 19 | HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); 20 | ULong FormerProtection; 21 | 22 | VirtualProtectEx(Process, (LPVOID)Shellcode, 1, PAGE_EXECUTE_READWRITE, &FormerProtection); 23 | 24 | CloseHandle(Process); 25 | return FormerProtection; 26 | } 27 | 28 | void WriteHook(HANDLE Process, ULong BaseAddress, ULong ShellCode) 29 | { 30 | UByte Patch[5]; 31 | 32 | ULong Address = (ShellCode - BaseAddress) - 5; 33 | Patch[0] = 0xE9; 34 | *(ULong *)(Patch + 1) = Address; 35 | 36 | WriteProcessMemory(Process, (LPVOID)BaseAddress, &Patch, sizeof(Patch), 0); 37 | } 38 | 39 | void SetDispatcher(ULong ProcessId, ULong Address, ULong ShellCode) 40 | { 41 | HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); 42 | ULong FormerProtection; 43 | 44 | VirtualProtectEx(Process, (LPVOID)Address, 1, PAGE_EXECUTE_READWRITE, &FormerProtection); 45 | WriteHook(Process, GetDispatcher(ProcessId), ShellCode); 46 | VirtualProtectEx(Process, (LPVOID)Address, 1, FormerProtection, &FormerProtection); 47 | 48 | CloseHandle(Process); 49 | } 50 | 51 | void RemoteJack(ULong ProcessId, ULong Target) 52 | { 53 | HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); 54 | ULong FormerProtection; 55 | 56 | VirtualProtectEx(Process, (LPVOID)Target, 1, PAGE_EXECUTE_READWRITE, &FormerProtection); 57 | WriteProcessMemory(Process, (LPVOID)Target, "\xF4", 1, 0); 58 | VirtualProtectEx(Process, (LPVOID)Target, 1, FormerProtection, &FormerProtection); 59 | 60 | CloseHandle(Process); 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Remote Thread Executor 2 | 3 | ## Overview 4 | 5 | This is about code injection via hijacking threads instead of creating a remote thread. There are methods of code injection where you can create a thread from another process using **CreateRemoteThread** at an executable code location (Or DLL Injection via **CreateRemoteThread** and executing **LoadLibrary**, passing an argument in the **CreateRemoteThread** routine). 6 | 7 | There are other ways of code injection to, one of them is hijacking an already running thread. Most methods to do this use **GetThreadContext** and **SetThreadContext**. That method suspends the thread(s) or the process, gets the context of a running thread in a process, writes the extended instruction pointer in the context structure to that of another executable location, then it sets the thread context and resumes the thread(s) or process. 8 | 9 | Of course, **CreateRemoteThread** is preferred to not interrupt an application while it's doing something possibly important. Thread hijacking is commonly used to bypass anticheats and antimalware. Antimalware checks for routines that could be used for code injection and are suspicious (**CreateRemoteThread**), while anticheats (espec. kernel-mode anticheats) would attempt to block opening a handle to a thread that they have ownership of from another process. They would also prevent the use of **SetThreadContext** for their threads. The more known a method is, the more anticheats implement ways to obstruct those methods. 10 | 11 | **RemoteJacker** detours the SEH dispatcher to an executable code location the developer wants. It causes the thread to redirect to this area by throwing an exception. 12 | 13 | ```C 14 | ULong GetDispatcher(ULong ProcessId) 15 | { 16 | NTPtr Dispatcher = (NTPtr)GetProcAddress(GetModuleHandleA("ntdll.dll"), "KiUserExceptionDispatcher"); 17 | HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); 18 | 19 | if (!Process) 20 | return NULL; 21 | else 22 | CloseHandle(Process); 23 | 24 | ULong Difference = ((ULong)Dispatcher - (ULong)GetModuleHandleA("ntdll.dll")); 25 | return (GetModuleBase(ProcessId, L"ntdll.dll") + Difference + 1); 26 | } 27 | ``` 28 | 29 | The **GetDispatcher** function gets the virtual address of **KiUserExceptionDispatcher** and returns it as an integral type. 30 | 31 | ```C 32 | ULong MarkShellCode(ULong ProcessId, ULong Shellcode) 33 | { 34 | HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); 35 | ULong FormerProtection; 36 | 37 | VirtualProtectEx(Process, (LPVOID)Shellcode, 1, PAGE_EXECUTE_READWRITE, &FormerProtection); 38 | 39 | CloseHandle(Process); 40 | return FormerProtection; 41 | } 42 | ``` 43 | 44 | The **MarkShellCode** function sets the memory region protections at the executable location (Or Shellcode) and returns the former page protection. 45 | 46 | ```C 47 | void SetDispatcher(ULong ProcessId, ULong Address, ULong ShellCode) 48 | { 49 | HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); 50 | ULong FormerProtection; 51 | 52 | VirtualProtectEx(Process, (LPVOID)Address, 1, PAGE_EXECUTE_READWRITE, &FormerProtection); 53 | WriteHook(Process, GetDispatcher(ProcessId), ShellCode); 54 | VirtualProtectEx(Process, (LPVOID)Address, 1, FormerProtection, &FormerProtection); 55 | 56 | CloseHandle(Process); 57 | } 58 | ``` 59 | 60 | The **SetDispatcher** function detours KiUserExceptionDispatcher in the target process. 61 | 62 | ```C 63 | void RemoteJack(ULong ProcessId, ULong Target) 64 | { 65 | HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); 66 | ULong FormerProtection; 67 | 68 | VirtualProtectEx(Process, (LPVOID)Target, 1, PAGE_EXECUTE_READWRITE, &FormerProtection); 69 | WriteProcessMemory(Process, (LPVOID)Target, "\xF4", 1, 0); 70 | VirtualProtectEx(Process, (LPVOID)Target, 1, FormerProtection, &FormerProtection); 71 | 72 | CloseHandle(Process); 73 | } 74 | ``` 75 | 76 | The **RemoteJack** function causes an exception to occur at a thread's instruction pointer and it gets handled by SEH so we can redirect it in the dispatcher. 77 | 78 | Here is an example: 79 | 80 | ```C 81 | int main(void) 82 | { 83 | ULong PId = FindProcessIdFromProcessName(L"test.exe"); 84 | ULong Dispatcher = GetDispatcher(PId); 85 | ULong ShellCode = 0x04E90000; 86 | ULong TargetVal = 0x00C92D50; 87 | ULong Protection = MarkShellCode(PId, ShellCode); 88 | 89 | SetDispatcher(PId, Dispatcher, ShellCode); 90 | RemoteJack(PId, TargetVal); 91 | 92 | return 0; 93 | } 94 | 95 | ``` 96 | 97 | This hijacks an already existing thread without opening any thread handles and you can choose the location that the thread gets hijacked at, unlike **GetThreadContext & SetThreadContext**. This method is also very simple to implement. 98 | --------------------------------------------------------------------------------