Code Injection via SetThreadContext (und ohne CreateRemoteThread)



  • Guten Abend 🙂

    Ich hätte mal eine Frage, undzwar würde ich gerne meinen Code, der in einer PE liegt (und nicht in einer DLL) von einem anderen Prozess ausführen lassen. Am liebsten wäre mir die Methode über SuspendThread und SetThreadContext, weil ich mich mit CreateRemoteThread bereits auskenne. Ich würde gerne etwas neues lernen.

    Theoretisch müsste das ganze ja wie folgt ablaufen:

    1. Öffnen des Prozess-Handles (OpenProcess)
    2. Allokieren von Speicher im anderen Prozess (VirtualAllocEx)
    3. Funktion in den allokierten Speicherbereich schreiben (WriteProcessMemory)
    4. Einen Thread des Prozesses suspenden (SuspendThread)
    5. Den aktuellen ThreadContext speichern (GetThreadContext)
    6. Den Context überschreiben (SetThreadContext)
    7. Den Thread wieder freigeben (ResumeThread)

    Ich habe schon bei google gesucht, aber nur teilweise befriedigende Ergebnisse gefunden. Soweit ich weiß, muss ich das EIP Register des neuen Contextes auf meinen allokierten Speicher setzen, also die Adresse, an der meine Funktion im anderen Prozess beginnt oder? Und wie berechne ich die benötigte Menge an Speicher für meine Funktion?

    Falls jemand Literatur für mich hat oder mir meine Fragen so beantworten kann, wäre ich sehr dankbar 🙂

    MfG DerCoder



  • Soweit ich weiß, muss ich das EIP Register des neuen Contextes auf meinen allokierten Speicher setzen

    So hab ich es damals gelöst

    wie berechne ich die benötigte Menge an Speicher für meine Funktion?

    Einfache Methode (kenne keine einfachere...):

    void ZielFunktion()
    {
    //blub
    }
    void dummy()
    {
    }

    Size = dummy - Zielfunktion

    Edit:
    Wenn du möchtest schau ich mal, ob ich mein altes Projekt noch finde... hab damals einen Injector gebastelt, welcher unter anderem deine "Cave"-Methode unterstützt...32bit und 64bit...



  • Wow! Erstmal danke für Deine Antwort 🙂

    void ZielFunktion()
    {
    //blub
    }
    void dummy()
    {
    }
    // ....
    Size = dummy - ZielFunktion;
    

    Das ist wahrscheinlich Compiler-abhängig, oder? Es gibt ja keine Garantie, dass die beiden Funktionen so später "untereinander" stehen, oder?
    Aber wenn das so funktioniert, werde ich das mal ausprobieren.

    Das klingt super. Ich würde mir Dein altes Projekt gerne mal anschauen 🙂



  • DerCoder schrieb:

    Wow! Erstmal danke für Deine Antwort 🙂

    void ZielFunktion()
    {
    //blub
    }
    void dummy()
    {
    }
    // ....
    Size = dummy - ZielFunktion;
    

    Das ist wahrscheinlich Compiler-abhängig, oder? Es gibt ja keine Garantie, dass die beiden Funktionen so später "untereinander" stehen, oder?

    Vollkommen richtig.
    Es gibt nichtmal ne Garantie dass die Funktion "am Stück" im Speicher steht, und nicht in 100 Teile zerstückelt wurde, keine globalen Tabellen referenziert (z.B. für switch) etc.



  • Okay, habs noch irgendwie gefunden, funktioniert, ist aber bei weitem nicht perfekt 😉
    Desweiteren hat hustbaer recht, das lässt sich aber per Compiler richten...

    // C

    BOOL inject_cave(DWORD pid, LPCWSTR lib, DWORD timeout)
    {
    	DWORD	old_protection = 0;
    	UINT	cave_length;
    	ULONG	tid = 0;
    	HANDLE	thread = 0;
    	HANDLE	process = 0;
    	int		thread_priority = 0;
    	UINT	addr_load_lib = 0;
    	void*	mem_string = 0;
    	void*	mem_cave = 0;
    	UINT	old_ip = 0;
    	CONTEXT	ctx;
    
    	// Cave Length
    #ifdef _M_X64
    	cave_length = (UINT)cave64_len - (UINT)cave64;
    #else
    	cave_length = (UINT)cave32_len - (UINT)cave32;
    #endif
    	addr_load_lib = (UINT)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
    
    	// Open Process
    	process = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, (DWORD)pid);
    	if(!process)
    		return FALSE;
    
    	// Allocate Memory
    	mem_string = VirtualAllocEx(process, NULL, string_length(lib) * 2 + 2, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    	if(!mem_string){
    		CloseHandle(process);
    		return FALSE;
    	}
    	mem_cave = VirtualAllocEx(process, NULL, cave_length + 2, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    	if(!mem_cave){
    		CloseHandle(process);
    		return FALSE;
    	}
    	// Write DLL String
    	if(!WriteProcessMemory(process, mem_string, (LPCVOID)lib, string_length(lib) * 2 + 2, NULL)){
    		CloseHandle(process);
    		return FALSE;
    	}
    
    	// Get Main Thread
    	tid = get_mainthread(pid);
    	if(!tid){
    		CloseHandle(process);
    		return FALSE;
    	}
    	// Open Thread
    	thread = OpenThread(THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_SET_INFORMATION, FALSE, tid);
    	if(!thread){
    		CloseHandle(process);
    		return FALSE;
    	}
    	// Suspend Thread
    	if(SuspendThread(thread) == (DWORD)-1){
    		CloseHandle(thread);
    		CloseHandle(process);
    		return FALSE;
    	}
    	// Get Context
    	ctx.ContextFlags = CONTEXT_CONTROL;
    	if(!GetThreadContext(thread, &ctx)){
    		CloseHandle(thread);
    		CloseHandle(process);
    		return FALSE;
    	}
    	// Set IP
    #ifdef _M_X64
    	old_ip = ctx.Rip;
    	ctx.Rip = (UINT)mem_cave;
    #else
    	old_ip = ctx.Eip;
    	ctx.Eip = (UINT)mem_cave;
    #endif
    	ctx.ContextFlags = CONTEXT_CONTROL;
    
    	// Create Cave
    #ifdef _M_X64
    	VirtualProtect(cave64, cave_length, PAGE_EXECUTE_READWRITE, &old_protection);
    	memcpy((void*)((UINT)cave64+30), &old_ip, 8);
    	memcpy((void*)((UINT)cave64+48), &mem_string, 8);
    	memcpy((void*)((UINT)cave64+58), &addr_load_lib, 8);
    	// Write Cave
    	if(!WriteProcessMemory(process, mem_cave, cave64, cave_length, NULL)){
    		CloseHandle(thread);
    		CloseHandle(process);
    		return FALSE;
    	}
    #else
    	VirtualProtect(cave32, cave_length, PAGE_EXECUTE_READWRITE, &old_protection);
    	memcpy((void*)((ULONG)cave32+1), &old_ip, 4);
    	memcpy((void*)((ULONG)cave32+8), &mem_string, 4);
    	memcpy((void*)((ULONG)cave32+13), &addr_load_lib, 4);
    	// Write Cave
    	if(!WriteProcessMemory(process, mem_cave, cave32, cave_length, NULL)){
    		CloseHandle(thread);
    		CloseHandle(process);
    		return FALSE;
    	}
    #endif
    	// Set Context
    	if(!SetThreadContext(thread, &ctx)){
    		CloseHandle(thread);
    		CloseHandle(process);
    		return FALSE;
    	}
    
    	// Increase Thread Priority
    	thread_priority = GetThreadPriority(thread);
    	SetThreadPriority(thread, THREAD_PRIORITY_ABOVE_NORMAL);
    
    	// Resume Thread
    	if(ResumeThread(thread) == (DWORD)-1){
    		CloseHandle(thread);
    		CloseHandle(process);
    		return FALSE;
    	}
    
    	// Timeout
    	Sleep(timeout);
    
    	// Reset Priority
    	SetThreadPriority(thread, thread_priority);
    
    	// Free And Close
    	VirtualFreeEx(process, mem_string, 0, MEM_RELEASE);
    	VirtualFreeEx(process, mem_cave, 0, MEM_RELEASE);
    	CloseHandle(thread);
    	CloseHandle(process);
    
    	return TRUE;
    }
    

    // cave32

    cave32 PROC
    		push	0DEADBEEFh		;#5
    		pushfd					;#1
    		pushad					;#1
    		push	0DEADBEEFh		;#5
    		mov		EAX, 0DEADBEEFh	;#5
    		call	EAX				;#2
    		popad					;#1
    		popfd					;#1
    		ret						;#1
    cave32 ENDP
    cave32_len PROC
    cave32_len ENDP
    

    // cave64

    cave64 PROC
    	sub		RSP, 8h
    	push	RAX
    	push	RBX
    	push	RCX
    	push	RDX
    	push	RDI
    	push	RSI
    	push	RSP
    	push	RBP
    	push	R8
    	push	R9
    	push	R10
    	push	R11
    	push	R12
    	push	R13
    	push	R14
    	push	R15
    	mov	RAX, 0EFBEADDEh	; address of old ip
    	mov	[RSP+128], RAX
    	mov	RCX, 0EFBEADDEh	; address of dll_string
    	mov	RAX, 0EFBEADDEh	; address of loadlib
    	call	RAX
    	pop	R15
    	pop	R14
    	pop	R13
    	pop	R12
    	pop	R11
    	pop	R10
    	pop	R9
    	pop	R8
    	pop	RBP
    	pop	RSP
    	pop	RSI
    	pop	RDI
    	pop	RDX
    	pop	RCX
    	pop	RBX
    	pop	RAX
    	ret
    cave64 ENDP
    cave64_len PROC
    cave64_len ENDP
    


  • Geht das auch ohne eine DLL in den anderen Prozess zu laden?



  • Sicher, ist nicht viel anders, der Code sollte nur als Grundlage dienen.

    Reservierst Speicher im Prozess (VirtualAllocEx), lädst deinen Code dahin (WriteProcessMemory) und setzt den IP dahin (SetThreadContex).


Anmelden zum Antworten