I'm currently studying different ways to manipulate a process for red teaming.
I decided to create a DLL that would be mapped inside the process in order to allocate some memory to store x64 shellcode and then would hijack a thread momentarily to execute this shellcode and when done, would restore the thread's context like nothing happened.
However what actually happens is that the thread is suspended, its instruction pointer is modified, the shellcode isn't executed at all, the thread is resumed after the Sleep call and continues it's execution on the previous instruction pointer.
Since the code didn't execute I decided to set the hijacked thread's priority to HIGHEST and call switchToThread to ensure that the thread scheduler actually switches to the thread in time before the Sleep finishes.
I also checked using process explorer that the thread was actually suspended and where its instruction pointer was and both were coherent. However I did see that the thread indicated a last error of access denied (5). I also get error c0000374 but this occurs long after the thread was resumed.
I also know that my shellcode isn't optimized (see below) because it does not jump back to the previous RIP and doesn't do an infinite loop after it has finished executing however this doesn't matter since the shellcode never executes in the first place. I even tried placing a breakpoint on the shellcode start and it was never triggered.
Any ideas?
Here is the C++ code:
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
THREADENTRY32 te;
te.dwSize = sizeof(THREADENTRY32);
if (!Thread32First(snapshot, &te)) {
printf("[-] Failed getting threads \n");
HeapFree(GetProcessHeap(), NULL, pTlsConnection);
HeapFree(GetProcessHeap(), NULL, mem);
FreeConsole();
FreeLibrary(hModule);
return FALSE;
}
DWORD tid = 0;
// Enumerate and print information about all threads in the process
do {
if (te.th32OwnerProcessID == GetCurrentProcessId()) {
tid = te.th32ThreadID;
if (tid != GetCurrentThreadId()) {
break;
}
}
} while (Thread32Next(snapshot, &te));
// Close the snapshot handle
CloseHandle(snapshot);
HANDLE hThread = OpenThread(THREAD_SET_INFORMATION | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT, 0, tid);
if (hThread == INVALID_HANDLE_VALUE) {
printf("[-] Failed Opening thread \n");
HeapFree(GetProcessHeap(), NULL, pTlsConnection);
HeapFree(GetProcessHeap(), NULL, mem);
FreeConsole();
FreeLibrary(hModule);
return FALSE;
}
if (SuspendThread(hThread) == -1) {
printf("[-] Failed Suspending thread \n");
HeapFree(GetProcessHeap(), NULL, pTlsConnection);
HeapFree(GetProcessHeap(), NULL, mem);
FreeConsole();
FreeLibrary(hModule);
return FALSE;
}
printf("[+] Suspended thread %d\n", tid);
CONTEXT context;
context.ContextFlags = CONTEXT_FULL;
if (!GetThreadContext(hThread, &context)) {
ResumeThread(hThread);
printf("[-] Failed getting thread context\n");
HeapFree(GetProcessHeap(), NULL, pTlsConnection);
HeapFree(GetProcessHeap(), NULL, mem);
FreeConsole();
FreeLibrary(hModule);
return FALSE;
}
printf("[+] Executing hook\n");
uintptr_t oldRIP = context.Rip;
context.Rip = hook2;
if (!SetThreadContext(hThread, &context)) {
ResumeThread(hThread);
printf("[-] Failed setting thread context\n");
HeapFree(GetProcessHeap(), NULL, pTlsConnection);
HeapFree(GetProcessHeap(), NULL, mem);
FreeConsole();
FreeLibrary(hModule);
return FALSE;
}
Sleep(500);
int prevPriority = GetThreadPriority(hThread);
SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST);
ResumeThread(hThread);
SwitchToThread();
Sleep(5000);
printf("[+] Got file object %p\n", *reinterpret_cast<uintptr_t*>(fileptr));
SuspendThread(hThread);
SetThreadPriority(hThread, THREAD_PRIORITY_NORMAL);
context.Rip = oldRIP;
if (!SetThreadContext(hThread, &context)) {
ResumeThread(hThread);
printf("[-] Failed setting thread context\n");
HeapFree(GetProcessHeap(), NULL, pTlsConnection);
HeapFree(GetProcessHeap(), NULL, mem);
FreeConsole();
FreeLibrary(hModule);
return FALSE;
}
ResumeThread(hThread);
