Assembler und Funktionsaufrufe
-
Hallo!
Ich beschäftige mich im Moment etwas mit Assembler und habe gerade einige kleine Schwierigkeiten, den generellen Ablauf eines Funktionsaufrufes zu verstehen:
Hier der C-Code:
void test_function(int a, int b, int c, int d) { int flag; char buffer[10] flag = 31337; buffer[0] = 'A'; } int main() { test_fuction(1,2,3,4); }
Der Assembler sieht so aus:
push ebp mov ebp, esp sub esp,0x18 and esp,0xffffffff0 mov eax,0x0 sub esp,eax mov DWORD PTR [esp+12], 0x4 mov DWORD PTR [esp+12], 0x3 mov DWORD PTR [esp+12], 0x2 mov DWORD PTR [esp+12], 0x1 call <test_function>
Dann der Code für den Funktionsaufruf:
push ebp mov ebp, esp sub esp,0x28 mov DWORD PTR [ebp-12], 0x7a69 ;das ist halt die 31337 in hexadezimal mov BYTE PTR [ebp-40], 0x41 ;ist das A in ASCII leave ret
So, was ich nicht genau verstehe ist, warum macht man:
and esp,0xffffffff0 mov eax,0x0 sub esp,eax
Warum führt man auf dem esp ein logisches und mit dieser Zahl aus und warum schiebt man dann eine 0 ins eax und zieht diese von esp wieder ab?
Zweitens:
sub esp,0x28 mov DWORD PTR [ebp-12], 0x7a69 ;das ist halt die 31337 in hexadezimal mov BYTE PTR [ebp-40], 0x41 ;ist das A in ASCII
Hier werden ja durch
sub esp,0x28
40 Byte resaviert. Warum 40? Der Int und die 10 char's des Arrays sind doch insgesamt nur 14 Byte, oder?
Und warum schiebe ich die 0x7a69 ab der Stelle [ebp-12] in den RAM und nicht ab Stelle [ebp-4]? Weil ich mache ja "mov ebp, esp", d.h. ich setze ebp auf den zu dem Zeitpunkt aktuellen ESP. Jetzt zeigte der ESP ja zu dem Zeitpunkt auch natürlich auf das Ende des Stacks was hier der Wert ist, nachdem ichpush ebp
ausgeführt habe. Das Heißt der ESP zeigt doch nach
push ebp
praktisch auf die Stelle hinter dem gesicherten EBP? Auf jeden Fall würde ich die 0x7a69 halt dann ab [ebp] schreiben, halt direkt nach dem gesicherten EBP.
Und daher auch, warum schreibe ich das 'A' ab ebp-40?Kann mir das bitte jemand erklären?
Danke!
-
CCat schrieb:
Der Assembler sieht so aus:
push ebp mov ebp, esp sub esp,0x18 and esp,0xffffffff0 mov eax,0x0 sub esp,eax mov DWORD PTR [esp+12], 0x4 mov DWORD PTR [esp+12], 0x3 mov DWORD PTR [esp+12], 0x2 mov DWORD PTR [esp+12], 0x1 call <test_function>
Ich nehme mal an, dass das kein copy&paste ist. Die Adressen der Funktionsargumente können so ja offensichtlich nicht stimmen.
Mit welchem Compiler auf welchem System mit welchen Compiler-Einstellungen wurde dieser Code erzeugt?
CCat schrieb:
So, was ich nicht genau verstehe ist, warum macht man:
and esp,0xffffffff0 mov eax,0x0 sub esp,eax
Warum führt man auf dem esp ein logisches und mit dieser Zahl aus und warum schiebt man dann eine 0 ins eax und zieht diese von esp wieder ab?
*Man* macht nicht, der Compiler hat sich in diesem speziellen Fall dazu entschieden, diesen Code zu erzeugen. Das and dürfte dazu dienen, den Stack auf 16 Byte auszurichten - gcc macht das z.B. einmalig in main, um dann ohne weiteren Aufwand mit automatischen SSE-Variablen umgehen zu können. Der Zweck des mov eax+sub erschließt sich daraus nicht; es könnte sich evtl. um Padding handeln, um Debugging zu erleichtern.
CCat schrieb:
Zweitens:
sub esp,0x28 mov DWORD PTR [ebp-12], 0x7a69 ;das ist halt die 31337 in hexadezimal mov BYTE PTR [ebp-40], 0x41 ;ist das A in ASCII
Hier werden ja durch
sub esp,0x28
40 Byte resaviert. Warum 40? Der Int und die 10 char's des Arrays sind doch insgesamt nur 14 Byte, oder?
Und warum schiebe ich die 0x7a69 ab der Stelle [ebp-12] in den RAM und nicht ab Stelle [ebp-4]?Der Compiler ist nicht gezwungen, Variablen direkt aufeinander folgend anzuordnen. Insbesondere um Arrays herum wird bei ausgelassener Optimierung gerne etwas Platz gelassen, um Bereichsüberschreitungen erkennen zu können.