VirtualAllocEx & CreateRemoteThread --> MessageBox
-
Hallo,
vieleicht kennt sich ja jemand, sehr gut aus. Ich komme an einer Stelle einfach nciht dahinter, was ich falsch mache:
#include <Windows.h> #include <conio.h> #include <iostream> #pragma check_stack(off) HWND hW = FindWindow(0, "PuTTY Configuration"); typedef int (WINAPI *fMsgBox) (HWND, LPCSTR, LPCSTR, UINT); typedef struct { fMsgBox pFunc; HWND a; LPCSTR b; LPCSTR c; UINT d; } MsgParam; __inline void MsgBoxWrap(MsgParam* p) { p->pFunc(p->a, p->b, p->c, p->d); } static void after_MsgBoxWrap() {} int main() { HANDLE hProcess; // process HANDLE hThread; // remote thread LPVOID pParam; // pointer to allocated param space LPVOID pFunc; // pointer to allocated function space DWORD sizeOfFunc; // size of function to copy DWORD procID; // process ID DWORD exitCode; // CreateRemoteThread Exit Code MsgParam param; // MessageBox parameter inkl func. pointer param.pFunc = (fMsgBox) GetProcAddress(GetModuleHandle("User32"), "MessageBoxW"); param.a = NULL; param.b = 0; param.c = 0; param.d = MB_YESNO; GetWindowThreadProcessId(hW, &procID); hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, procID); //Speicher für Parameter Struktur reservieren sizeOfFunc = (PBYTE)after_MsgBoxWrap - (PBYTE)MsgBoxWrap; pParam = VirtualAllocEx(hProcess, NULL, sizeof(MsgParam), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if(pParam == NULL) printf("VirtualAllocEx failed! Error: %d\n", GetLastError()); // Speicher für Funktion reservieren pFunc = VirtualAllocEx(hProcess, 0, sizeOfFunc, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if(pFunc == NULL) printf("VirtualAllocEx failed! Error: %d\n", GetLastError()); // Parameter schreiben WriteProcessMemory(hProcess, pParam, (LPVOID)¶m, sizeof(MsgParam), NULL); // Funktion schreiben WriteProcessMemory(hProcess, pFunc, (LPVOID)MsgBoxWrap, sizeOfFunc, 0); // remote thread starten hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)pFunc, ¶m, NULL, NULL); WaitForSingleObject(hThread, INFINITE); GetExitCodeThread(hThread, &exitCode); CloseHandle(hProcess); getch(); return 0; }
Ich will die MessageBox aus 'nem anderen Process starten (hier im Beispiel Putty.exe). Aber sobald ich CreateRemoteThread() aufrufe stürzt putty ab... Hat jemand nen Idee, wo mein Fehler liegt?
Grüße
Nico
-
Hallo Nico:
Auszug aus der Doku zu 'CreateRemoteThread', siehe auch: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682437(v=vs.85).aspx
lpStartAddress [in]
A pointer to the application-defined function of type LPTHREAD_START_ROUTINE to be executed by the thread and represents the starting address of the thread in the remote process. The function must exist in the remote process. For more information, see ThreadProc.
Da bedeutet die Thread Funktion muss im Codesegment des remote process liegen.
- Hätte mich auch gewundert, wenn es nicht so wäre -Die von dir verwendete Thread Funktion liegt im lokalen Prozess.
Das geht so nicht. Der Remote Thread spring in seinem virtuellen Adressraum an eine undefinierte Stelle und raucht ab ...Gruß Frank
-
Hallo Nico,
ich habe es nicht sofort verstanden,
du kopierst ja den Code in den Prozess hinein ...Ich weiß auch nicht warum es nicht geht ...
Gruß Frank
-
CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)pFunc, pParam, NULL, NULL);
?
-
Danke für die Rückmeldungen
Frank Erdorf schrieb:
du kopierst ja den Code in den Prozess hinein ...
Genau, so der Plan!
masm schrieb:
CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)pFunc, pParam, NULL, NULL);
?
Wenn doch alles im Leben so einfach wäre, ich war wohl schon etwas zu lange am selben Code-Fragment ^^. Das klappt schon mal wunderbar, einen hacken hat es aber noch:
Die MessageBox kommt und solange sie auf ist funktioniert auch alles bestens. Wenn ich sie schließe, stürzt das Programm (hier Putty) aber ab.
Woran könnte das liegen? Zumindest der Einstiegspunkt in die MessageBox scheint ja sauber zu sein. Das Struct wird wohl auch sauber kopiert, da die MessageBox sauber angezeigt wird. Vermutlich muss also was mit dem Return aus der injecteten nicht klappen oder?Danke für eure Hilfe
Nico
-
Hallo Nico,
param.pFunc = (fMsgBox) GetProcAddress(GetModuleHandle("User32"), "MessageBoxW");
Das ist für mich zumindest potenziell ein Problem.
An welche Stelle im virtuellen Adressraum eine dll geladen wird kann man
sich zwar wünschen, aber es gibt soweit ich weiß keine Garantie.Demnach bin ich mir nicht sicher, ob die Einsprungadresse von "MessageBoxW" in deinem Prozess die gleiche ist wie im Remote Prozess.
Ich würde mir die Einsprungadresse im Remote Prozess holen ...Wie auch immer das ist zumindest jetzt kein Problem.
Was mich an deinem Code noch irritiert:
__inline void MsgBoxWrap(MsgParam* p)
Meinem Verständnis nach ist das falsch und könnte durchaus mit dem
von dir gemeldeten Problem zu tun haben: Absturz nach der MSGBOX.korrekt wäre so:
DWORD WINAPI MsgBoxWrap( LPVOID lpParameter );
Wegen meiner auch so:
DWORD WINAPI MsgBoxWrap( MsgParam* p );
in jedem Fall würde ich es mal ohne "__inline" probieren.
Und warum "MessageBoxW" lieber "MessageBoxA",
ich würde das Thema Unicode erst mal außen vor lassen...Ich hoffe das hilft ein wenig ...
Viel Erfolg,
Gruß Frank
-
Erstmal danke Frank! Ich hab's! (Dickes) Aber: noch nicht ganz durchschaut, wie ich es gemacht habe
Ich vermute, dass aus irgendeinem Grund entweder die Rücksprungadresse kaputt ist ODER ich habe mir den Dissasembly angesehen, die Prüfung auf den Stackpointer fehlschlägt, was ja fast das gleiche ist--> Also habe ich überlegt den Funktionsaufruf per Assembler durchzuführen:
__declspec(naked) void WINAPI MsgBoxWrap(MsgParam* p) { __asm { /* prolog */ push ebp ; Save ebp mov ebp, esp ; Set stack frame pointer sub esp, __LOCAL_SIZE ; Allocate space for locals push esi ; Save registers push edi ; Save registers push ebx ; Save registers push ecx ; Save registers push edx ; Save registers } // p->pFunc(p->a, p->b, p->c, p->d); __asm { /* call function */ mov esi,esp mov eax,dword ptr [p] mov ecx,dword ptr [eax+10h] push ecx mov edx,dword ptr [p] mov eax,dword ptr [edx+0Ch] push eax mov ecx,dword ptr [p] mov edx,dword ptr [ecx+8] push edx mov eax,dword ptr [p] mov ecx,dword ptr [eax+4] push ecx mov edx,dword ptr [p] mov eax,dword ptr [edx] call eax } __asm { /* epilog */ pop edx ; Restore registers pop ecx ; Restore registers pop ebx ; Restore registers pop edi ; Restore registers pop esi ; Restore registers xor eax, eax ; Return Value - Thread exit mov esp, ebp ; Restore stack pointer pop ebp ; Restore ebp ret ; Return from function } }
Man beachte, dass ich im Prinzip nur
p->pFunc(p->a, p->b, p->c, p->d);
durch einen möglichst äquivalenten Assembler Code ersetzt habe. Aber was soll ich sagen, es funktioniert. Putty öffnet meine MessageBox und läuft (soweit ich weiß) stabil weiter! Und wie gesagt, wenn ich einfach nur
p->pFunc(p->a, p->b, p->c, p->d);
aufrufe stürzt Putty ab!
Also vielleicht kann ja trotzdem jemandLicht ins Dunkle bringen...
Ich freu mich sodas es überhaupt mal klappt^^
Grüße Nico
-
Hallo Nico,
bei dem Assembler Code, kann ich leider nicht folgen,
aber das __inline könnte doch genau damit zu tun haben,
das der Code keine Rücksprungadresse auf den Stack legt.
Wenn wirklich geinlined wird, ist ja ein Rücksprung nicht erforderlich.Hast du den C-Code mal ohne "__inline" ausprobiert?
Gruß Frank
-
Die Funktion MsgBoxWrap muss ebenfalls stdcall sein. Der Sinn des __inline erschließt sich mir nicht.
Die DLLs sind während einer Sitzung in allen Prozessen an die gleiche Stelle gemapt - auch bei ASLR.
Wenn es was Besseres werden soll, sollte man MsgBoxWrap in Assembler schreiben.
-
Frank Erdorf schrieb:
Hast du den C-Code mal ohne "__inline" ausprobiert?
Ja hab ich, mit dem gleichen Resultat, der andere Prozess schmiert ab, nachdem ich die MessageBox schließe :-|
static int WINAPI MsgBoxWrap(MsgParam* p) { return p->pFunc(p->a, p->b, p->c, p->d); } static void after_MsgBoxWrap() {}
masm schrieb:
Die Funktion MsgBoxWrap muss ebenfalls stdcall sein.
Ist sie doch!?
masm schrieb:
Wenn es was Besseres werden soll, sollte man MsgBoxWrap in Assembler schreiben.
Also so lassen? Bin auch nicht 100%ig firm, ist der Code OK?
-
Hallo masm,
Die DLLs sind während einer Sitzung in allen Prozessen an die gleiche Stelle gemapt - auch bei ASLR.
Danke wieder etwas gelernt ...
Hallo Kronos,
ist schon komisch, wie du es schaffst die Signatur der Funktion dann doch wieder anders als in der Doku zu schreiben ...Wer weiß was da lost ist, vielleicht optimiert der Compiler ...
Was soll es, wenn du mit dem Assembler leben kannst und
das sieht ja so aus ist es auch gut.Gruß Frank
-
das disassembly (oder was auch immer) ist definitiv nicht stdcall:
ret 4
Ich würde es an deiner stelle einfach bei einer ganz normalen WINAPI Deklaration belassen:
void WINAPI MsgBoxWrap(MsgParam* p) { ... }
Ansonsten einfach ExitThread() benutzen
-
Hi ihr beiden!
masm schrieb:
das disassembly (oder was auch immer) ist definitiv nicht stdcall:
ret 4
Wo kommt denn die 4 her?
masm schrieb:
normalen WINAPI Deklaration belassen:
void WINAPI MsgBoxWrap(MsgParam* p) { ... }
Ansonsten einfach ExitThread() benutzen
Hab folgendes getestet:
static int WINAPI MsgBoxWrap(MsgParam* p) { p->pFunc(p->a, p->b, p->c, p->d); ExitThread(0); }
Keine Chance, der Prozess raucht ab. Selbst wenn ich
p->pFunc(p->a, p->b, p->c, p->d);
durch den passenden Assembler code von oben ersetze raucht er ab... Bisher läuft er ausschließlich weiter, wenn ich pro- und epilog selber schreibe.
Es gibt (wie immer) bestimmt ne ganz logische Erklärungnur fällt mir keine ein
Mir ist anhand des Dissasembly jedoch was aufgefallen:
cmp esi,esp call @ILT+445(__RTC_CheckEsp) (0E511C2h)
Sowas baut der Compiler immer automatisch mit in den Code ein! Ich vermute das könnte das Problem sein!?!?
PS.: Nen dickes Danke an euch beide, dass ihr mir so super weitergeholfen habt!
-
Kronos_m schrieb:
masm schrieb:
ret 4
Wo kommt denn die 4 her?
Die muss dahin - alles Andere ist falsch!
Folgender Code funktionier perfekt als realease build:
// C, ANIS, release build #include <Windows.h> #include <stdio.h> typedef int (WINAPI *fMsgBox) (HWND, LPCSTR, LPCSTR, UINT); typedef struct { fMsgBox pFunc; HWND a; LPCSTR b; LPCSTR c; UINT d; } MsgParam; void WINAPI MsgBoxWrap(MsgParam* p) { p->pFunc(p->a, p->b, p->c, p->d); } static void after_MsgBoxWrap() {} int main() { HANDLE hProcess; // process HANDLE hThread; // remote thread LPVOID pParam; // pointer to allocated param space LPVOID pFunc; // pointer to allocated function space DWORD sizeOfFunc; // size of function to copy DWORD procID; // process ID DWORD exitCode; // CreateRemoteThread Exit Code MsgParam param; // MessageBox parameter inkl func. pointer HWND hWnd; hWnd = FindWindow(0, "test"); param.pFunc = (fMsgBox) GetProcAddress(GetModuleHandle("User32"), "MessageBoxA"); param.a = NULL; param.b = 0; param.c = 0; param.d = MB_YESNO; GetWindowThreadProcessId(hWnd, &procID); hProcess = OpenProcess(PROCESS_ALL_ACCESS , 0, procID); if (!hProcess) { printf("OpenProcess failed! Error: %d\n", GetLastError()); } sizeOfFunc = (PBYTE)after_MsgBoxWrap - (PBYTE)MsgBoxWrap; pParam = VirtualAllocEx(hProcess, NULL, sizeof(MsgParam), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if(pParam == NULL) printf("VirtualAllocEx failed! Error: %d\n", GetLastError()); pFunc = VirtualAllocEx(hProcess, 0, sizeOfFunc, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if(pFunc == NULL) printf("VirtualAllocEx failed! Error: %d\n", GetLastError()); WriteProcessMemory(hProcess, pParam, (LPVOID)¶m, sizeof(MsgParam), NULL); WriteProcessMemory(hProcess, pFunc, (LPVOID)MsgBoxWrap, sizeOfFunc, 0); hThread = CreateRemoteThread(hProcess, NULL, (SIZE_T)0, (LPTHREAD_START_ROUTINE)pFunc,(void*) pParam, (DWORD)0, NULL); if(!hThread) printf("CreateRemoteThread failed! Error: %d\n", GetLastError()); WaitForSingleObject(hThread, INFINITE); GetExitCodeThread(hThread, &exitCode); CloseHandle(hProcess); system("pause"); return 0; }
(Vergiss nicht die allokierten Ressourcen wieder frei zu geben)
-
Hi masm,
ok jetzt hast du mich :)... Wie gemein, d.h. der Code hat die ganze Zeit funktioniert(!), ich musste ihn nur im Release Mode compilieren -.- (hab's gerade getestet, klappt wunderbar).
Was ist der Grund dafür? Es muss ja eine Compiler-Einstellung sein?!
masm schrieb:
(Vergiss nicht die allokierten Ressourcen wieder frei zu geben)
Ok, danke
PS.: Mein asm Code läuft sowohl im Rel, als auch im Debug Mode
-
Kronos_m schrieb:
Was ist der Grund dafür
? Es muss ja eine Compiler-Einstellung sein?!
zwecks vereinfachten Debuggens, wird andere Code erzeugt als im Release Mode: z.B. werden alle Funktionen über ein jump table aufgerufen und es wird der Stack geprüft. Einfach mal im Debugger anschauen
-
nur noch so als hinweis: deine sizeoffunc muss nicht klappen. es ist nicht garantiert, dass die eine funktion wirklich hinter der anderen im speicher liegt
greetz KN4CK3R
-
Dann mal danke an alle, es läuft nun alles Rund, habe es schon mit verschiedenen anderen Methoden aus User32 und Kernel32 getestet
KN4CK3R schrieb:
[...]deine sizeoffunc muss nicht klappen. es ist nicht garantiert, dass die eine funktion wirklich hinter der anderen im speicher liegt
Ist das so? Gibt's denn nen sichereren Weg?
PS.: Bin nich sooo oft hier, muss ich den Thread irgendwo als gelöst oder so markieren?
-
der "sicherste" Weg ist deine Funktion selbst zu disassemblen, um genau an die Adressen zu kommen (was hier aber sicher zu weit führt, gibts aber eine lib für). Ich komme aus der Cheaterszene und wir benutzen dafür sowas wie Labels um Funktionen vor einen Aufruf zu entschlüsseln bzw danach zu verschlüsseln. Problem an der Methode ist aber dass das Label erst nach dem Prolog einer Funktion auftaucht, es fehlt also ein Teil.
Für dich bietet es sich sicher an, deine Wrapperfunktion nicht in C zu schreiben sondern in Assembler. In dem Fall kannst du anschließend die Funktion in ein byte Array stecken und das per WPM im anderen Prozess anlegen.greetz KN4CK3R
-
Ok danke
Werde das so machen / versuchen!