В течение нескольких часов я пытался расширить свое понимание языка ассемблера, пытаясь прочитать и понять инструкции очень простой программы, которую я написал на C, чтобы понять, как обрабатываются аргументы в КАК М.
#include <stdio.h>
int say_hello();
int main(void) {
printf("say_hello() -> %d\n", say_hello(10, 20, 30, 40, 50, 60, 70, 80, 90, 100));
}
int say_hello(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) {
printf("a:b:c:d:e:f:g:h:i:j -> %d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n", a, b, c, d, e, f, g, h, i, j);
return 1000;
}
Программа, как я уже сказал, очень базовая c и содержит две функции, основную и еще одну, называемую say_hello
, которая принимает 10 аргументов, от a
до j
и печатает каждый из них в вызове printf. Я пробовал проделать тот же процесс (так что пытаюсь понять инструкции и то, что происходит), с той же программой и меньшим количеством аргументов, я думаю, что смог понять большую часть этого, но потом мне было интересно, "хорошо но что происходит, если у меня так много аргументов, что больше нет доступного регистра для хранения значения в "
" Итак, я пошел посмотреть, сколько регистров было доступно и применимо в моем случае, и я узнал из этого веб-сайта , что "только" (не уверен, поправьте меня, если я ошибаюсь) в моем случае могут использоваться следующие регистры для хранения в них значений аргументов edi, esi, r8d, r9d, r10d, r11d, edx, ecx
, то есть 8, поэтому я пошел модифицировать свою программу C и добавил еще несколько аргументов, чтобы достичь предела 8, я даже добавил еще один, я действительно не знаю почему, скажем так, на всякий случай. 1014 но я ничего не ожидал от метода say_hello()
, поэтому в первую очередь я попробовал его.
Итак, я собрал свою программу, затем разобрал ее с помощью команды objdump
(точнее, это полная команда, которую я использовал: objdump -d -M intel helloworld
), и я начал искать свой метод main
, который делал примерно так, как я ожидал. от одного метода к другому. Но потом я пошел искать метод say_hello
, и это меня очень запутало.
000000000000069e <say_hello>:
69e: 55 push rbp
69f: 48 89 e5 mov rbp,rsp
6a2: 48 83 ec 20 sub rsp,0x20
6a6: 89 7d fc mov DWORD PTR [rbp-0x4],edi
6a9: 89 75 f8 mov DWORD PTR [rbp-0x8],esi
6ac: 89 55 f4 mov DWORD PTR [rbp-0xc],edx
6af: 89 4d f0 mov DWORD PTR [rbp-0x10],ecx
6b2: 44 89 45 ec mov DWORD PTR [rbp-0x14],r8d
6b6: 44 89 4d e8 mov DWORD PTR [rbp-0x18],r9d
6ba: 44 8b 45 ec mov r8d,DWORD PTR [rbp-0x14]
6be: 8b 7d f0 mov edi,DWORD PTR [rbp-0x10]
6c1: 8b 4d f4 mov ecx,DWORD PTR [rbp-0xc]
6c4: 8b 55 f8 mov edx,DWORD PTR [rbp-0x8]
6c7: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
6ca: 48 83 ec 08 sub rsp,0x8
6ce: 8b 75 28 mov esi,DWORD PTR [rbp+0x28]
6d1: 56 push rsi
6d2: 8b 75 20 mov esi,DWORD PTR [rbp+0x20]
6d5: 56 push rsi
6d6: 8b 75 18 mov esi,DWORD PTR [rbp+0x18]
6d9: 56 push rsi
6da: 8b 75 10 mov esi,DWORD PTR [rbp+0x10]
6dd: 56 push rsi
6de: 8b 75 e8 mov esi,DWORD PTR [rbp-0x18]
6e1: 56 push rsi
6e2: 45 89 c1 mov r9d,r8d
6e5: 41 89 f8 mov r8d,edi
6e8: 89 c6 mov esi,eax
6ea: 48 8d 3d bf 00 00 00 lea rdi,[rip+0xbf] # 7b0 <_IO_stdin_used+0x20>
6f1: b8 00 00 00 00 mov eax,0x0
6f6: e8 25 fe ff ff call 520 <printf@plt>
6fb: 48 83 c4 30 add rsp,0x30
6ff: b8 e8 03 00 00 mov eax,0x3e8
704: c9 leave
705: c3 ret
706: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0]
70d: 00 00 00
Мне заранее очень жаль, я не совсем уверен, что хорошо понимаю, что делают квадратные скобки, но из того, что я прочитал и понял, это способ «указать» на адрес, содержащий нужное мне значение (пожалуйста, поправьте меня, если я ошибаюсь), например, mov DWORD PTR [rbp-0x4],edi
перемещает значение в edi
на значение по адресу rsp-0x4
, верно?
Я также не совсем понимаю, зачем нужен этот процесс, не может ли метод say_hello
просто читать edi
, например, и все? Почему программе нужно переместить его в [rbp-0x4]
, а затем перечитать обратно из [rbp-0x4]
в eax?
Итак, программа просто продолжает читать все необходимые ей значения и помещает их в доступный регистр, а когда достигает точки, когда не остается ни одного регистра, она просто начинает перемещать их все в esi
и затем помещаем их в стек, а затем повторяем процесс, пока все 10 аргументов не будут где-то сохранены.
В этом есть смысл, я был удовлетворен, а затем просто пошел, чтобы дважды проверить, действительно ли у меня все хорошо, поэтому я начал читать снизу вверх, начиная с 0x6ea
до 0x6e2
, поэтому образец Я работаю над
6e2: 45 89 c1 mov r9d,r8d
6e5: 41 89 f8 mov r8d,edi
6e8: 89 c6 mov esi,eax
6ea: 48 8d 3d bf 00 00 00 lea rdi,[rip+0xbf] # 7b0 <_IO_stdin_used+0x20>
Итак, как и во всех моих предыдущих тестах, я ожидал, что аргументы go будут "обратными", например, первый аргумент - это последняя выполненная инструкция, а последняя одна была выполнена первая инструкция, поэтому я начал дважды проверять каждое поле.
Итак, первый, rdi
, был [rip+0x10b]
, который, как я думал, точно указывает на мою строку.
Затем я перешел к 0x6e8
, который перемещает eax
, который в настоящее время равно значению, хранящемуся в [rbp-0x4]
, которое равно edi
, как указано в 0x6a6
, а edi
равно 0xa
(10), как указано в 0x671
, поэтому мой первый аргумент это моя строка, а вторая - 10
, это именно то, что я ожидал.
Но потом, когда я перешел на инструкцию, выполненную прямо перед 0x6e8
, поэтому 0x6e5
я ожидал, что она будет 20, поэтому я проделал тот же процесс. edi
перемещается в r8d
и в настоящее время равно значению, хранящемуся в [rbp-0x10]
, которое равно ecx
, которое равно, как указано в 0x662 .. 40? Какого черта? Я запуталась, с чего бы это было 40? Затем я попытался найти инструкцию прямо над этой, и нашел 50, проделал то же самое со следующей, и снова я нашел 60 !! Зачем? Я неправильно понимаю эти значения? Что-то не хватает в инструкции? Или я просто предположил что-то, глядя на свои предыдущие программы (все они имели намного меньше аргументов и все были «в обратном порядке», как я сказал ранее), чего мне не следовало бы иметь?
* 1074 - тупой пост, я новичок в ASM (несколько часов опыта!) и просто пытаюсь прояснить это, потому что я действительно не могу понять это в одиночку. Мне также жаль, если этот пост слишком длинный, я пытался включить много информации, чтобы то, что я пытаюсь сделать, было ясным, результат, который я получаю, ясен, и в чем моя проблема также ясно. Большое спасибо за чтение и еще большее спасибо всем, кто поможет!