I'm fairly new to thread injection and am learning the technique through a book called "Game Hacking: Developing Autonomous Bot for Online Games." Chapter 7 highlights 3 injection techniques (thread injection, thread hijacking, and DLL injection).
Currently, I'm having trouble getting my thread hijacking code to work, because I keep running into an Access Violation Error when running my code. I've tried running as admin and the error still persists. Could anyone sift through my code and give me a reason why this error keeps popping up? I'm using Visual Studios 2019.
Exception thrown at 0x00000024 in GameHacking.exe: 0xC0000005: Access violation executing location 0x00000024.
Thanks and Happy Halloween!
#include <windows.h>
#include <tlhelp32.h>
#include <string>
using namespace std;
// function being referenced by injector
DWORD printStringManyTimes(int times, const char* string)
{
for (int i = 0; i < times; i++)
printf(string);
return 0;
}
// THIS FUNCTION WORKS JUST FINE
void injectCodeUsingThreadInjection(HANDLE process, LPVOID func, int times, const char* string)
{
BYTE codeCave[20] = {
0xFF, 0x74, 0x24, 0x04, // PUSH DWORD PTR[ESP+0x4]
0x68, 0x00, 0x00, 0x00, 0x00, // PUSH 0
0xB8, 0x00, 0x00, 0x00, 0x00, // MOV EAX, 0x0
0xFF, 0xD0, // CALL EAX
0x83, 0xC4, 0x08, // ADD ESP, 0x08
0xC3 // RETN
};
// copy values to the shellcode
memcpy(&codeCave[5], ×, 4);
memcpy(&codeCave[10], &func, 4);
// allocate memory for the code cave
int stringlen = strlen(string) + 1;
int fulllen = stringlen + sizeof(codeCave);
LPVOID remoteString = VirtualAllocEx(process, NULL, fulllen, MEM_COMMIT, PAGE_EXECUTE);
LPVOID remoteCave = (LPVOID)((DWORD)remoteString + stringlen);
if (remoteString) {
// write the code cave
WriteProcessMemory(process, remoteString, string, stringlen, NULL);
WriteProcessMemory(process, remoteCave, codeCave, sizeof(codeCave), NULL);
// run the thread
HANDLE thread = CreateRemoteThread(process, NULL, NULL,
(LPTHREAD_START_ROUTINE)remoteCave,
remoteString, NULL, NULL);
WaitForSingleObject(thread, INFINITE);
VirtualFreeEx(process, remoteString, fulllen, MEM_RELEASE);
CloseHandle(thread);
}
}
// I know this function is redundant because the PID is already
// found in the main function, but I wanted to first copy what
// the book outlines in order to test how it works
DWORD GetProcessThreadID(HANDLE Process)
{
THREADENTRY32 entry;
entry.dwSize = sizeof(THREADENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (Thread32First(snapshot, &entry) == TRUE)
{
DWORD PID = GetProcessId(Process);
while (Thread32Next(snapshot, &entry) == TRUE)
{
if (entry.th32OwnerProcessID == PID)
{
CloseHandle(snapshot);
return entry.th32ThreadID;
}
}
}
CloseHandle(snapshot);
return NULL;
}
// THIS IS THE FUNCTION GIVING ME PROBLEMS
void injectCodeUsingThreadHijacking(HANDLE process, LPVOID func, int times, const char* string) {
BYTE codeCave[31] = {
0x06, //PUSHAD
0x9C, //PUSHFD
0x68, 0x00, 0x00, 0x00, 0x00, // PUSH 0
0x68, 0x00, 0x00, 0x00, 0x00, // PUSH 0
0xB8, 0x00, 0x00, 0x00, 0x00, // MOV EAX, 0x0
0xFF, 0xD0, // CALL EAX
0x83, 0xC4, 0x08, // ADD ESP, 0x08
0x9D, //POPFD
0x61, //POPAD
0x68, 0x00, 0x00, 0x00, 0x00, // PUSH 0
0xC3 // RETN
};
// allocate memory for the code cave
int stringlen = strlen(string) + 1;
int fulllen = stringlen + sizeof(codeCave);
LPVOID remoteString = VirtualAllocEx(process, NULL, fulllen, MEM_COMMIT, PAGE_EXECUTE);
LPVOID remoteCave = (LPVOID)((DWORD)remoteString + stringlen);
// suspend the thread and query its control context
DWORD threadID = GetProcessThreadID(process);
HANDLE thread = OpenThread(THREAD_ALL_ACCESS, false, threadID); //(THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_SET_CONTEXT)
SuspendThread(thread);
CONTEXT threadContext;
threadContext.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(thread, &threadContext);
// copy values to the shellcode (happens late because we need values from allocation)
memcpy(&codeCave[3], &remoteString, 4);
memcpy(&codeCave[8], ×, 4);
memcpy(&codeCave[13], &func, 4);
memcpy(&codeCave[25], &threadContext.Eip, 4);
// write the code cave
WriteProcessMemory(process, remoteString, string, stringlen, NULL);
WriteProcessMemory(process, remoteCave, codeCave, sizeof(codeCave), NULL);
//hijack the thread
threadContext.Eip = (DWORD)remoteCave;
threadContext.ContextFlags = CONTEXT_CONTROL;
SetThreadContext(thread, &threadContext);
ResumeThread(thread);
//clean
CloseHandle(thread);
}
DWORD WINAPI hijackThread(LPVOID lpParam)
{
injectCodeUsingThreadHijacking((HANDLE)lpParam, &printStringManyTimes, 2, "hijacked\n");
return 1;
}
int main() {
string myTitle;
cout << "What is the process you are looking for? ";
cin >> myTitle;
DWORD PID = -1;
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (Process32First(snapshot, &entry) == true) {
while (Process32Next(snapshot, &entry) == true) {
cout << entry.szExeFile << endl;
string binPath = entry.szExeFile;
if (binPath.find(myTitle) != string::npos) {
cout << endl << myTitle << " has the pid " << entry.th32ProcessID << endl;
PID = entry.th32ProcessID;
break;
}
}
}
if (PID == -1) {
printf("\n%s was not found in the process list.\n", myTitle);
return -1;
}
CloseHandle(snapshot);
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
if (process == INVALID_HANDLE_VALUE) {
printf("Failed to open PID %d, error code %lu", PID, GetLastError());
}
// Inject code into self using thread injection
injectCodeUsingThreadInjection(process,&printStringManyTimes, 2, "injected\n");
// inject code into self using thread hijacking
// we need to do it from a secondary thread or else
// the hijacking code would hijack itself.. which
// doesn't work
CreateThread(NULL, 0, hijackThread, process, 0, NULL);
while (true) {
Sleep(100);
}
CloseHandle(process);
return 0;
}
User contributions licensed under CC BY-SA 3.0