VirtualAllocEx & CreateRemoteThread --> MessageBox



  • 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 jemand 😕 Licht ins Dunkle bringen...
    Ich freu mich so 😃 das 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ärung 😕 nur 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)&param, 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!



  • KN4CK3R schrieb:

    nur noch so als hinweis: deine sizeoffunc muss nicht klappen. es ist nicht garantiert, dass die eine funktion wirklich hinter der anderen im speicher liegtKN4CK3R

    mag sein das es nicht standardisiert ist, aber kennst du eine Compiler, der die Funktionen umsortiere?



  • nein, aber möglich ist es aus PageFault Sicht schon, deswegen ja auch mein "muss nicht klappen"

    greetz KN4CK3R



  • KN4CK3R schrieb:

    aber möglich ist es aus PageFault Sicht schon

    du meinst, dass eine page fault auftreten kann, wenn er die Funktion kopiert (lese zugriff)?



  • nein, dass immer ein PageFault auftreten würde, wenn die Funktion "weiter weg" liegt. Aber das wäre nur der Fall, wenn immer nur eine Page geladen werden könnte, ist also eher ein theoretisches Problem.
    Aber auch ohne DAS Problem baut der VS Compiler nach Funktionen auch im Releasemodus oft unnötige INT3's ein, die er mit seiner Method mitkopiert dann.

    greetz KN4CK3R



  • KN4CK3R schrieb:

    nein, dass immer ein PageFault auftreten würde, wenn die Funktion "weiter weg" liegt. Aber das wäre nur der Fall, wenn immer nur eine Page geladen werden könnte, ist also eher ein theoretisches Problem.

    Wenn man davon ausgeht, dass die Funktion in der gleichen Ordnung vorlegen wie im Quellcode, dann kann ich überhaupt kein Problem erkennen. Die Funktionen müssen doch in pages liegen, die zu minnest lesbar sind - andernfalls wäre es ja nicht möglich, sie aufzurufen/auszuführen.

    KN4CK3R schrieb:

    Aber auch ohne DAS Problem baut der VS Compiler nach Funktionen auch im Releasemodus oft unnötige INT3's ein, die er mit seiner Method mitkopiert dann.

    Diese INTs gehören nicht zur Funktion, sondern werden für das Algiment nachfolgender Funktion benutzt. Zusätzlich verhindern sie auch, dass es zur Ausführung kommt, wenn dort "versehentlich" hin gesprungen wird.
    Ist aber auch völlig Egal ober er sie mit kopiert, da sie nie ausgeführt werden.



  • PageFault != nicht lesbar
    wegen den INTs: ist mir schon klar, ich wollte nur darauf hinaus, dass er zuviel kopiert

    greetz KN4CK3R


Anmelden zum Antworten