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:
- Öffnen des Prozess-Handles (OpenProcess)
- Allokieren von Speicher im anderen Prozess (VirtualAllocEx)
- Funktion in den allokierten Speicherbereich schreiben (WriteProcessMemory)
- Einen Thread des Prozesses suspenden (SuspendThread)
- Den aktuellen ThreadContext speichern (GetThreadContext)
- Den Context überschreiben (SetThreadContext)
- 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).