Code in Data-Segment ausführen

  • C++

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von Fritschifisch.

    Code in Data-Segment ausführen

    Hi,

    am Anfang wollte ich nur mal alle vorwarnen, die nicht mit Assembler oder der Struktur von Programmen im RAM vertraut sind, denn dieses Wissen wird hier verlangt :).

    Vorgeschichte:
    Ich schreibe ein 3D-Spiel in C++ und DirectX und dazu eine eigene Engine. Ob ich das schaffe sei mal dahin gestellt. Auf jedenfall soll diese Engine eine eigene Skriptsprache besitzen. Soweit schön und gut. Doch ich dachte mir, warum die Skriptsprache nicht in Maschinencode kompilieren (ist dann zwar keine Skriptsprache mehr, aber ich benutze das Wort "Skriptsprache" hier lieber als "Programmiersprache"), in den RAM laden, Instruction Pointer auf die Adresse der geladenen Bytes setzen und dann ausführen lassen. Gesagt, getan. Doch dann bekam ich gleich den ersten Fehler: 0xC0000005. Dieser sagt aus, dass ich eine Zugriffsverletzung begehe, d.h in meinem Fall keinen Code im Data-Segment ausführen darf. Also Google befragt und voilà, ich fand VirtualProtectEx, mit der man die Berechtigung in bestimmten Adressräumen ändern kann. Also gleich ausprobiert der alte Fehler war weg, aber ...

    Das Problem:
    Code

    C-Quellcode

    1. #include <iostream>
    2. #include <Windows.h>
    3. using namespace std;
    4. int main()
    5. {
    6. int output = 0;
    7. char bytes[2]; //einfache Befehle zu Testzwecken
    8. bytes[0] = 0x40; //inc eax
    9. bytes[1] = 0xCB; //retf
    10. HANDLE h = OpenProcess(PROCESS_VM_OPERATION, FALSE, GetCurrentProcessId());
    11. DWORD dummy = 0;
    12. if (VirtualProtectEx(h, (LPVOID)&bytes, sizeof(bytes), PAGE_EXECUTE_READWRITE, &dummy) == FALSE)
    13. {
    14. cout << "FAILED!" << endl;
    15. system("PAUSE");
    16. return 1;
    17. }
    18. __asm
    19. {
    20. mov eax, 41;
    21. call dword ptr [bytes];
    22. mov [output], eax;
    23. };
    24. printf("%.8X\n", output);
    25. system("PAUSE");
    26. return 0;
    27. }


    ... nun schmeißt er mir einen Fehler in Zeile 28. Wiederrum erzählt er mir, ich würde eine Zugriffsverletzung begehen, doch ich wüsste nicht warum. Weiterhin hat er mir eax (jedenfalls laut VS2013) nicht auf 42 inkrementiert, was er ja eigentlich hätte machen sollen. Daraufhin hab' ich versucht output auch spezielle Berechtigungen zu geben, doch das hat (wie ich beinahe gedacht hatte, weil das war eine reine Verzweiflungstat) keinen Unterschied gemacht. Ich habe es auch über andere Lösungswege versucht, wie zum Beispiel bytes in einen Funktionzeiger zu casten (void(*callFunction)(void) = (void(*)(void))&bytes;), doch auch da schmeißt mir wieder eine Zugriffsverletzung. Ich hoffe das Problem ist nicht zu speziell und jemand kann mir helfen.

    Vielen Dank im Voraus :)

    LG
    Fritschifisch
    Drei Worte, die dein Programmieren verändern: Option Strict On

    Hab grad bisschen geguckt, also so wie du es hast wenn es direkt in der Funktion steht, dann springt der dahin aber findet nur "murks". Wenn man das Array auf den Heap schreibt, dann springt er hin, führt es aus aber landet iwo in der WinMain oder so... die Rücksprungaddresse wird halt nicht richtig gesetzt. Soll es denn unbedingt im Datensegment stehen?

    EDIT: LOL, du machst einen far return, kein wunder dass der da hinspringt... , also mit retn (0xC3) und im Heap funktioniert es.

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Quadsoft“ ()

    Ich verstehe nicht was du mit "murks" meinst, denn eigentlich sollten das Maschinencode-Anweisungen sein xD (Betonung auf "sollten"). Das Problem ist, dass es im Data-Segment stehen muss, da ich das Skript ja zur Laufzeit kompilieren muss. Das kann ich ja schlecht in die Ressourcen packen oder einkompilieren, weil die Skripts vom Nutzer hinzufügbar sein sollen (so, wie 'ne Modding-API). Wenn ich allerdings den ganzen Kram ins Code-Segment verschieben kann wäre das natürlich schon gut. Allerdings frage ich mich was du meinst mit die Rücksprungadresse wird nicht richtig gesetzt? Ich dachte "call" pusht die, also den Instruction Pointer und 'nen Zeiger auf das aktuelle Segment auf den Stack? Oder hab ich da mal was komplett falsch verstanden?
    Drei Worte, die dein Programmieren verändern: Option Strict On

    Siehe Edit, du musst nen near return machen, keinen far return.

    Mit murks meinte ich das hier (aus dem VS-Debugger)

    Adresse ?? ??
    Adresse ?? ??

    stand da.. wahrscheinlich heißt ?? halt dass das was dort stand kein gültiger Befehl ist. Ist im Datensegment nicht das NX-Bit gesetzt und damit eh alles was mit "ausführen" zu tun hat unmöglich?

    Kannst du es nicht einfach das was du im Datensegment stehen hast in den Heap laden zur Laufzeit?
    Oh Mann ... vielen vielen vielen Dank :D Mit 'm near return gehts. Danke ^^. Ich dachte man müsste ein far return machen, weil im "Intel Instruction Set Reference" stand, dass man nur so über Segment springen kann o.O. Nochmal danke :)
    Bilder
    • cs.png

      33,14 kB, 1.038×196, 158 mal angesehen
    Drei Worte, die dein Programmieren verändern: Option Strict On

    Also bei mir funktionierts jetzt so wie im Code oben ... Ist das nicht schon auf'm Heap? Weil das Array ja eigentlich in 'ner Funktion erstellt wird. Da wird das doch eigentlich auf'n Heap geschmissen.

    //EDIT: Sollte tatsächlich auf'm Heap sein, weil ein Array an sich ja nichts weiter als ein Zeiger ist, und die kommen ja eigentlich auf'n Heap. <-- Gefährliches Halbwissen
    Drei Worte, die dein Programmieren verändern: Option Strict On

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Fritschifisch“ ()

    ok, wie es scheint bewegen wir hier uns auf ganz dünnem Eis. Ich hatte gerade noch den Code mit dem Funktionszeiger drin. Da hat es nur nicht auf dem Heap funktioniert. Wenn ich nun auf den Code umsteige, den ich gepostet habe, funktioniert's weder auf dem Heap noch auf dem Stack.

    So, eigene Dummheit. Man sollte natürlich nicht die Adresse und die Größe vom Zeiger benutzen sondern die des Objekts, auf das verwiesen wird ;).
    Drei Worte, die dein Programmieren verändern: Option Strict On

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Fritschifisch“ ()

    C-Quellcode

    1. #include <iostream>
    2. #include <Windows.h>
    3. using namespace std;
    4. int main()
    5. {
    6. int output = 0;
    7. char * bytes = (char*)malloc(sizeof(char)*2); //einfache Befehle zu Testzwecken
    8. bytes[0] = 0x40; //inc eax
    9. bytes[1] = 0xC3; //retn
    10. HANDLE h = OpenProcess(PROCESS_VM_OPERATION, FALSE, GetCurrentProcessId());
    11. DWORD dummy = 0;
    12. if (VirtualProtectEx(h, (LPVOID)bytes, sizeof(char)*2, PAGE_EXECUTE_READWRITE, &dummy) == FALSE)
    13. {
    14. cout << "FAILED!" << endl;
    15. system("PAUSE");
    16. return 1;
    17. }
    18. __asm
    19. {
    20. mov eax, 41;
    21. call dword ptr [bytes];
    22. mov [output], eax;
    23. };
    24. printf("%.8X\n", output);
    25. system("PAUSE");
    26. return 0;
    27. }


    der geht bei mir