R0 CREW

Несколько вопросов от начинающего

Всем привет!
Заранее извиняюсь, если такие нубские вопросы тут не задают (если это так, то пошлите меня в верном направлении, пожалуйста:))
Давно интересуюсь реверсингом и всяким прочим низкоуровневым, но только сейчас решил всё-таки заняться этим делом (хотя были раньше попытки начать, которые в итоге провалились, каюсь )).
Сразу же огромная куча вопросов возникла, многие из которых даже не знаю как задать.
Поставил перед собой для начала такую задачу: компилирую разными компиляторами (clang, gcc, cl.exe) с разными флагами оптимизации и под разные операционные системы (FreeBSD, Linux, Windows7) один и тот же простой хеллоуворлд на С. А потом смотрю на все получившиеся варианты через IDA Pro и пытаюсь понять ВСЁ. Потом буду потихоньку код усложнять, чтобы увидеть во что реально превращаются всевозможные массивы указателей на структуры, которые содержат массивы строк :)))
В связи с чем вопросов у меня будет очень много, но начну пока с самых бросающихся в глаза.

Для начала код на C:

#include <stdio.h>

int main(void)
{
	printf("Hello, World!\n");

	return 0;
}

Вот, что я вижу в IDA, когда компилирую его Clang’ом под FreeBSD:

main proc near

	var_8 = dword ptr -8
	var_4 = dword ptr -4

	push rbp
	mov  rbp, rsp
	sub  rsp, 10h         ; 1) ДЛЯ ЧЕГО РЕЗЕРВИРУЕТСЯ 16 БАЙТ В СТЕКЕ?
	mov  rdi, offset format ; "Hello, World!\n"
	mov  [rbp+var_4], 0   ; 2) ЗАЧЕМ В СТЕК КЛАДЁТСЯ ЭТОТ НОЛЬ?
	mov  al, 0            ; 3) ЭТО МЫ СООБЩАЕМ КОЛИЧЕСТВО ПЕРЕДАННЫХ FLOAT-ПАРАМЕТРОВ? ПОЧЕМУ ТОГДА AL, А НЕ EAX?
	call _printf
	mov  ecx, 0
	mov  [rbp+var_8], eax ; 4) ЗАЧЕМ? В EAX ВОЗВРАЩАЕТСЯ РЕЗУЛЬТАТ ФУНКЦИИ PRINTF? А ЗАЧЕМ ЕГО В СТЕКЕ СОХРАНЯТЬ?
	mov  eax, ecx
	add  rsp, 10h
	pop  rbp
	retn

main endp

В комментариях в коде, собственно, четыре вопроса к вам.
В целом, мне понятно, что делает этот код. Увидев его, я бы без проблем понял, что он делает. Но мне непонятно то, ЗАЧЕМ он делает некоторые вещи.

А вот, что я вижу в IDA, когда компилирую gcc (тоже под FreeBSD, если это важно):

main proc near

	sub  rsp, 8 ; ДЛЯ ЧЕГО РЕЗЕРВИРУЕТСЯ 8 БАЙТ В СТЕКЕ?
	mov  edi, offset s   ; "Hello, World!"
	call _puts
	mov  eax, 0
	add  rsp, 8
	retn

main endp

И опять, вопрос в комментарии в коде

И вот ещё, компилируем clang под Linux:

main proc near

        push rax ; ЗАЧЕМ-ТО СОХРАНЯЕМ RAX В СТЕКЕ
        mov  edi, offset s   ; "Hello, World!"
        call   _puts
        xor   eax, eax
        pop  rcx ; А ПОТОМ ЗАЧЕМ-ТО ВЫТАСКИВАЕМ ЭТО ЗНАЧЕНИЕ УЖЕ В RCX. 
        retn

endp

В чём был смысл перебрасывать через стек RAX в RCX?

Может, я и заморачиваюсь больше нужного, но мне не даёт покоя то, что есть вещи, остающиеся непонятыми :slight_smile: Хотелось бы в конечном итоге прийти к тому, чтобы мне была понятна вообще каждая строчка, которую я вижу в IDA, от самого начала до cамого конца, а не только дизассемблированные функции, которые мной же и были написаны.

Подобная работа была проделана Юричевым
https://forum.reverse4you.org/thread/1595

И? Меня интересует мой собственный навык. Как мне поможет тот факт, что кто-то уже проделал то, чему я только собираюсь научиться? Я знаю о книге Юричева и, конечно же, прочитаю её, но на моём собственном пути у меня возникли конкретные вопросы, на которые я был бы рад получить конкретные ответы.

Во всех своих примерах для экспериментов, выноси свой код в отдельную функцию и там уже разбирайся, что почём. Функция main довольно специфична и что туда пихают различные компиляторы, зависит от разрабов самих компиляторов. Можешь к ним пойти и поинтересоваться, что это за мусор и для чего он нужен.

#include <stdio.h>

void test() 
{
   printf("Hello, World!\n");
}

int main(void)
{
	test();

	return 0;
}

Это ответы на все твои вопросы.

Это уже вроде как понял. Для разбора во что превращается конкретный мой код я нашёл онлайн компилятор https://gcc.godbolt.org/ Он сразу показывает ассемблерный листинг и без всякого лишнего хлама.

То есть, эти вещи не должны меня беспокоить? Я имею в виду сам факт того, что я не понимаю этих нюансов. Просто я думал, что прежде чем двигаться дальше, нужно сначала разобраться во всём, чтобы не тянулись следом какие-то недопонятые вещи. Возможно, я слишком заморочился :slight_smile:

Спасибо за совет! :slight_smile:

Тебе дали конкретный ответ на твои конкретные вопросы. Если ты не удосужился открыть книжку и прочитать эти ответы, то это твои проблемы.

Если интересно, то сядь сам разберись, посмотри что происходит до вызова main, или после, и возможно ты получишь ответы на свои вопросы, прокачаешь скил, напишешь статью. Но ИМХО эти вещи не стоят внимания, собсно поэтому никто и не может сказать зачем оно нужно. :slight_smile:

Ахах, ну до написания статей мне ещё дорасти надо)) Я пока ещё ни в чём не разбираюсь настолько, чтобы кого-то ещё и учить чему-то))
А вот разобраться будет интересно, но, как я понял, пока что имеет смысл фильтровать поступающую в меня информацию, чтобы видеть, по-возможности, лишь то, что действительно важно в практическом плане.

Хотя немножко я уже стал понимать, что там до вызова main творится… По крайней мере, у меня исчезло ощущение того, что мой код является самостоятельно выполняющейся программой. Теперь видно, что ОС делает огромную работу для того, чтобы мой код выполнился, подготавливая для него нужную среду и контролируя весь процесс, а потом ещё и убирая за ним мусор) И по сути, функция main - это просто функция, которую вызывает среда выполнения в тот момент, когда она была подготовлена… Думаю, что на данном этапе бОльшего мне знать не нужно :slight_smile:

Я, конечно, ещё совсем зелёный и ничего не знаю, но позволю себе не согласиться :slight_smile: Думаю, внимания стоит абсолютно всё, что непонятно и интересно))

Всегда умиляло, когда человек берёт свой хеллоуворлд, компилит, а потом его изучает в дизассемблере. В реальном мире софт - это сотни тысяч (или миллионы) строк. И первое (для некоторых - основное), с чем вам придётся столкнуться в реальном мире - это то, как локализовать нужный участок кода. Если нужно закейгенить приложение, то нужно найти место проверки серийника. А для этого его нужно локализовать. Для этого нужно знать, как работает АПИ операционной системы, оконная подсистема, реестр и прочее. А вы залупились на main функции clang компилера, которым, может, ни одно приложение в вашей крякерской жизни не будет скомпилировано. Ну ерундой не занимайтесь, ок?

Я постараюсь :slight_smile:

Но мне не очень понятно, как я смогу начать ковыряться в тысячах строк чужого кода, если я даже в трёх строках своего разобраться не могу… С чего-то же надо начинать.
Ну и ещё такой нюанс есть. Передо мной не стоит цели стать крэкером или ещё чего-то подобного. Мне просто хочется научиться понимать как оно там всё устроено. Отсюда и берётся дотошность. Ну а потом, если я всё это дело не заброшу, разберусь, не потеряю в процессе этих разбирательств интерес, то, может, и займусь чем-то более конкретным и прикладным.