[Assembler] Warum wird der Stack-Pointer hier mit 0x28 subtrahiert?

  • Sonstige

Es gibt 4 Antworten in diesem Thema. Der letzte Beitrag () ist von rwCapt.

    [Assembler] Warum wird der Stack-Pointer hier mit 0x28 subtrahiert?

    Moin Moin,

    ich finde mich gerade etwas in Assembler ein und habe im Internet einen sehr kleinen und einfachen Assembler-Code gefunden, der bereits alles "vor eingestellt" hat, quasi keine Verlinkungen durch einen Linker mehr braucht, sondern nur noch kompiliert, also von der Assemblersprache in den Zahlencode übersetzt werden muss. Dazu nutze ich nasm und es funktioniert prima. Das kleine Programm stellt eine ganz einfache MessageBox dar.

    Was ich mich aber frage ist, warum der Stack Pointer (also das Register rsp) um 0x28 subtrahiert werden muss. Lasse ich die Zeile sub rsp, 5 * 8 weg, kann ich im Task-Manager sehen, dass mein kleines Programm mit einem Fehler startet (diese Windows-Fehlerdiagnose öffnet sich, was nur im Task-Manager sichtbar ist).

    Hier mal der ganze Code:

    Quellcode

    1. EntryPoint:
    2. sub rsp, 5 * 8 ; 48 83 EC 28 ; Um diese Zeile geht es
    3. mov r9d, 0 ; 41 B9 00
    4. mov r8d, Title_ + DATADELTA ; 41 B8 00 30 40
    5. mov edx, Caption + DATADELTA ; BA 0D 30 40 00
    6. mov ecx, 0 ; B9 00
    7. call [__imp__MessageBoxA] ; 00 FF 14 25 88 20 40
    8. exit:
    9. mov ecx, 0 ; B9 00
    10. call [__imp__ExitProcess] ; 00 FF 14 25 78 20 40


    Bei der Gelegenheit würde ich auch noch mal eine zweite Frage stellen: Warum braucht ein Prozess einen Aufruf der Funktion "ExitProcess()"? Ich vermute, dass das Betriebssystem die Info braucht, wann das Programm beendet ist.

    Vielen Dank vorab für die Hilfe!

    rwCapt schrieb:

    Was ich mich aber frage ist, warum der Stack Pointer (also das Register rsp) um 0x28 subtrahiert werden muss


    Die Antwort findest du hier im Kapitel 11:
    nasm.us/xdoc/2.14.01rc1/html/nasmdo11.html


    All known 64-bit platforms except some embedded platforms require that
    the stack is 16-byte aligned at the entry to a function. In order to
    enforce that, the stack pointer (RSP) needs to be aligned on
    an odd multiple of 8 bytes before the CALL
    instruction.
    So sollte der Beitrag eigentlich nicht aussehen. Naja, machen wir halt mal Lückenraten ;)

    Das hätte ich wohl nie gefunden. Ich bin eigentlich so an die Sache ran gegangen, mich mit Assembler zu beschäftigen, dabei aber kaum ein Augenmerk auf das Übersetzungstool zu legen (NASM), was ja auch so einiges gut beschreibt.

    Das ist jetzt etwas zusammenhangslos, aber ich frage einfach mal. Die EXE, die ich da quasi "nackt" und ohne Kompiler/Linker schreibe, orientiert sich . Das in einem beliebigen Hexadezimal-"fähigen"-Editor eingegeben, ergibt das im Eingangspost beschriebene Programm.

    Wenn ich dieses Programm nun aber in Visual Studio mit C zu erstellen versuche und dann mit einem Hexadezimal-Editor öffne, steht da deutlich mehr drinne als es eigentlich sollte. Ich habe die Konfiguration auf "Release" gestellt und die Zielplattform x86 ausgewählt und mich bei den Projekteinstellungen hieran orientiert. Das reduzierte das ganze um einiges, es bleibt aber trotzdem noch ein Rest in der imports-Section der EXE, der im Code auf dem nicht vorhanden ist.
    Naja, halb so tragisch, wenigstens ist die EXE so klein, dass man damit arbeiten/lernen kann. Allerdings ähnelt sich der Code der EXE aus dem Visual Studio kaum mit dem aus dem . Ich schlussfolgere daraus, dass eine EXE zumindest in ihren einzelnen sections (außer eben der Code-section, denn da geht nur reinste Assembler-Sprache) bspw. von der "Benennung" ihrer Imports sehr unterschiedlich sein kann, also je nach Linker. Gibt es da ein System hinter, das man irgendwie verstehen kann? So, wie es im im Anhang gemacht wurde, ist es für mich am einfachsten.
    Bilder
    • imports-section.png

      77,72 kB, 877×698, 84 mal angesehen
    Bin ich grad überfragt, wenn ich deine EXE hätte könnte ich das unter die Lupe nehmen. Aber du kannst das auch selbst, mit der Explorer Suite(CFF Explorer), das wird vermutlich am einfachsten sein. Nützliches Tool um PE's zu untersuchen, wie auch verändern.
    ntcore.com/?page_id=388



    rwCapt schrieb:

    Wenn ich dieses Programm nun aber in Visual Studio mit C zu erstellen versuche


    Du kannst auch mit VS kompileren, ohne das die VC-Runtime benötigt bzw. dazu was mit rein kommt. Hab das aber nie probiert(ausser für Arduino). Alternativ, kannste auch die GCC verwenden, da ist dann weniger Ballast im kompilat.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Takafusa“ ()

    Vielen Dank nochmal @Takafusa , der CFF Explorer hat mir bisher sehr weitergeholfen. Mit Ghidra ist es ein unglaublich nützliches Werkzeug, um sich zurecht zu finden.

    Ich habe nun so in etwa verstanden, was der Stack Pointer so macht. Ist es also so, dass wenn ich etwas auf den Stack verfrachten (bspw. mov qword [rsp + 0x08], 0x110) möchte, ich den Stack Pointer quasi um 8 Bytes nach hinten "stellen" muss (sub rsp, 8 * 1)?

    Ich habe es geschafft, die WNDCLASSEXW-Struktur so auf den Stack zu bekommen, dass ich die Funktion RegisterClassExW(...) damit füttern kann. Ich habe gemerkt, dass die Argumente der Funktionen aus der user32.dll in der Reihenfolge übergeben werden sollten: 1. Parameter: rcx, 2. Parameter: rdx, 3. Parameter: r8, 4. Parameter: r9, [jetzt wird es spannend] 5. Parameter [rsp + 0x20].

    Und genau ab dem fünften Parameter bin ich mir unsicher. Das hat jetzt mit RegisterClassExW (hat ja auch nur ein Argument, das über das Register rcx übergeben wird) geklappt, aber mit CreateWindowExW klappt es nicht. Da gibt mir GetLastError den Fehlercode, den ich mir mit wsprintfA in einer MessageBox habe ausgeben lassen, 998 für No_Memory_Access aus. Ich habe auch hier wieder sämtliche Parameter nach dem obigen Schema übergeben. Auch ein Blick in die user32.dll und in die Funktion CreateWindowExW bestätigt mir, dass das Übergabeformat, wie ich es aufgestellt habe, richtig ist. Trotzdem erhalte ich kein gültiges Handle (hWnd = 0).

    Spoiler anzeigen

    Quellcode

    1. sub rsp, 8 * 22 ; <-- Ich bin hier am zweifeln, worauf ich den Stack Pointer stellen soll
    2. ; Constans
    3. SW_SHOWDEFAULT equ 0xA
    4. WNDCLASSEXW.size equ 0x50
    5. CS_VREDRAW equ 0x1
    6. NULL equ 0
    7. COLOR_3DSHADOW equ 0x10
    8. IDI_APPLICATION equ 0x7F00
    9. IDC_ARROW equ 0x7F00
    10. MB_OK equ 0x00
    11. CW_USEDEFAULT equ 0x80000000
    12. WS_OVERLAPPEDWINDOW equ 0xCF000000
    13. WS_VISIBLE equ 0x10000000
    14. ; HINSTANCE hInstance
    15. hInstance equ 0x70
    16. ; LPSTR CommandLine
    17. CommandLine equ 0x78
    18. ; WNDCLASSEXW wc
    19. wc equ 0x20
    20. wc.cbSize equ 0x20
    21. wc.style equ 0x24
    22. wc.lpfnWndProc equ 0x28
    23. wc.cbClsExtra equ 0x30
    24. wc.cbWndExtra equ 0x34
    25. wc.hInstance equ 0x38
    26. wc.hIcon equ 0x40
    27. wc.hCursor equ 0x48
    28. wc.hbrBackground equ 0x50
    29. wc.lpszMenuName equ 0x58
    30. wc.lpszClassName equ 0x60
    31. wc.hIconSm equ 0x68
    32. ; HWND hwnd
    33. hwnd equ 0x70
    34. ; MSG msg
    35. msg equ 0x78
    36. msg.hwnd equ 0x78
    37. msg.message equ 0x80
    38. msg.wParam equ 0x84
    39. msg.lParam equ 0x8B
    40. msg.time equ 0x93
    41. mov rcx, NULL ; hInstance = GetModuleHandle(NULL)
    42. call [__imp__GetModuleHandleW] ; <--
    43. mov [rsp + hInstance], rax ; <--
    44. call [__imp__GetCommandLineW] ; CommandLine = GetCommandLineW()
    45. mov [rsp + CommandLine], rax ; <--
    46. push SW_SHOWDEFAULT ; WinMain(hInstance, NULL, CommandLine, SW_SHOWDEFAULT)
    47. lea rax, [rsp + CommandLine] ; <--
    48. push rax ; <--
    49. push NULL ; <--
    50. mov rax, [rsp + hInstance] ; <--
    51. push rax ; <--
    52. call WinMain ; WinMain = EntryFunction
    53. WinMain:
    54. pop rcx
    55. pop rdx
    56. pop r8
    57. pop r9
    58. mov dword [rsp + wc.cbSize], WNDCLASSEXW.size ; sizeof(WNDCLASSEXW) = 80
    59. mov dword [rsp + wc.style], CS_VREDRAW ; style = CS_VREDRAW | 1 = CS_VREDRAW
    60. mov rax, NULL ; lpfnWndProc = NULL
    61. mov [rsp + wc.lpfnWndProc], rax ; <--
    62. mov dword [rsp + wc.cbClsExtra], NULL ; cbClsExtra = NULL
    63. mov dword [rsp + wc.cbWndExtra], NULL ; cbWndExtra = NULL
    64. mov [rsp + wc.hInstance], rcx ; hInstance = hInstance [param]
    65. mov qword [rsp + wc.hbrBackground], COLOR_3DSHADOW ; hbrBackground = COLOR_3DSHADOW | 16 = COLOR_3DSHADOW
    66. mov rax, ClassName + DATADELTA ; lpszClassName = "MeineClass"
    67. mov [rsp + wc.lpszClassName], rax ; <--
    68. mov rdx, IDI_APPLICATION ; hIcon = LoadIconW(NULL, IDI_APPLICATION)
    69. xor rcx, rcx ; <--
    70. call [__imp__LoadIconW] ; <--
    71. mov [rsp + wc.hIcon], rax ; <--
    72. mov [rsp + wc.hIconSm], rax ; <--
    73. mov rdx, IDC_ARROW ; hCursor = LoadCursorW(NULL, IDC_ARROW)
    74. xor rcx, rcx ; <--
    75. call [__imp__LoadCursorW] ; <--
    76. mov [rsp + wc.hCursor], rax ; <--
    77. lea rcx, [rsp + wc] ; if (!RegisterClassExW(&wc)) { MessageBoxA(NULL, "Ein Fehler ist aufgetreten.", "Fehler", MB_OK); }
    78. call [__imp__RegisterClassExW] ; <--
    79. cmp rax, 0 ; <--
    80. jne RegistrationSuccessful ; <--
    81. je RegistrationNotSuccessful ; <--
    82. RegistrationNotSuccessful: ; <--
    83. mov r9d, MB_OK ; <--
    84. mov r8d, MessageBox_Error_Registration.Title + DATADELTA ; <--
    85. mov rdx, MessageBox_Error_Registration.Caption + DATADELTA ; <--
    86. mov rcx, 0 ; <--
    87. call [__imp__MessageBoxA] ; <--
    88. jmp Exit
    89. RegistrationSuccessful:
    90. mov rcx, NULL
    91. mov rdx, ClassName + DATADELTA
    92. mov r8, AppName + DATADELTA
    93. mov r9, (WS_OVERLAPPEDWINDOW + WS_VISIBLE)
    94. mov dword [rsp + 0x20], CW_USEDEFAULT
    95. mov dword [rsp + 0x28], CW_USEDEFAULT
    96. mov dword [rsp + 0x30], 0x280
    97. mov dword [rsp + 0x38], 0xe6
    98. mov dword [rsp + 0x40], NULL
    99. mov dword [rsp + 0x48], NULL
    100. mov rax, [rsp + hInstance]
    101. mov [rsp + 0x50], rax
    102. mov dword [rsp + 0x58], NULL
    103. call [__imp__CreateWindowExW] ; <-- Fehler = 998: Invalid access to memory location.
    104. cmp rax, 0
    105. jnz Exit
    106. call [__imp__GetLastError]
    107. lea rcx, [rsp + 0x80]
    108. mov rdx, Format + DATADELTA
    109. mov r8, rax
    110. call [__imp__wsprintfA]
    111. mov r9d, MB_OK ; <--
    112. mov r8d, MessageBox_Error_Registration.Title + DATADELTA ; <--
    113. lea rdx, [rsp + 0x80] ; <--
    114. mov rcx, 0 ; <--
    115. call [__imp__MessageBoxA] ; <--
    116. Exit:
    117. mov rcx, 0 ; ExitProcess(0)
    118. call [__imp__ExitProcess]


    Edit:


    Verflucht, mein Fehler war es, einen char* statt eines wchar_t* als Class-Name (lpszClassName) anzugeben. Dadurch scheint die Funktion den Nullterminator nicht gefunden zu haben, also ein kaputter String, wenn man so will. Naja, jetzt funktioniert es und ich habe ein Fenster in Assembler erstellt ^^

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „rwCapt“ ()