Реверсинг для начинающих

Реверсинг для начинающих

s1and1


В предыдущей статье мы работали с таким инструментом как дебаггер(x64dbg), для реверса программы, в этой статье я бы хотел посвятить так такому инструменту как IDA Pro. Статья предназначена новичкам!

IDA Pro представляет из себя простую в использовании, программу, позволяющая перевести бинарный код в дизассемблерный. На него очень много плагинов и она очень востребовательна у реверсеров.

P.S:Мы будем работать с x86 ассемблером(тот, на котором работает ваш пк)


Давайте попробуем написать простую программу на с++, скомпилированными разными компиляторами(msvc, mingw), в начале попробуем разобрать, как же будет выглядеть программа, после того, как мы его соберём:


Давайте для начала напишем простенькую с программу для того, чтобы разобраться как, что внутри нашего exe`шника


код:



Давайте скомпилируем его на этих компиляторах, дабы узнать, что получилось.

MSVC:


MinGW:



Существуют так называемые регистры*, я бы хотел обратить внимание на Регистры общего назначения(eax, ebx, ecx, edx). они используются для выполнений арифметических, математических операций(mov, inc, dec, sub, div, imul, lea и т.д.)


Так же регистры состоят из частей например регистр rax(в 32-битном режиме eax)
RAX - 64 битный (8 байт)
EAX - 32 битный (4 байта)
AX - 16 битный (2 байта)
AH, Al - 8 байтные (1 байт)


И так давайте разберём, что происходит в самом собранном файле.

`_main proc near` - означает, что тут начинается функция main(в си/с++ это начало программы)

`push esi` - означает, что мы добавляем на вершину стека(сохраняем) содержание в регистре esi

`push 400h` - означает, что мы добавляем в стэк значение 0x400, то есть 1024

`call ds:malloc` - тут вызываем функцию malloc(выделяем динамическую память), передавая в качестве аргумента 1024(размер выделяемой динамической памяти)

`mov esi, eax` - здесь мы помещаем результат вызова функции malloc(eax - адрес выделенной памяти)

`push esi` - тут та же самая история с malloc, мы добавляем в стэк значение которое лежит в esi(то есть помещённый указатель eax)

`push offset Format ; "%s"` - тут мы передаём формат строки %s означает просто строка

`call  sub_FD1050` - здесь мы вызываем функцию по адресу 0xFD1050, то есть компилятор создал функцию, через которого и обращается к vfscanf

`push esi` - то же самое что и при вызове с malloc, только теперь она передаётся в качестве аргумента другой функции(printf)

`push offset Format ; "%s"` - то же самое, но только для вызова printf

`call  sub_FD1020` - тут мы вызываем функцию, которая находится по адресу 0xFD1020, она, как в предыдущей функции является "трамплином" между функциями си и с++

`add   esp, 14h` - увеличиваем указатель стека на 0x14(20), для очистки стека

`xor   eax, eax` - тут, мы выполняем операцию XOR(ИЛИ), в этом случае, он обнуляет значение регистра eax на 0, используется xor потому, что он короче на 2 байта

`pop   esi` - помните в самом начале мы добавили на вершину стэка значение регистра esi, так вот, сейчас мы его получаем

`retn` - означает, что функция main была успешно завершена 

`_main endp` - означает, что заканчиваем функцию main



В этом примере вам показал, как, на уровне ассемблера, работает программа.

`_main proc near` - 

`push ebp` - добавляем на вершину стека регистр ebp(это, как в прошлом примере, позволяет сохранить значение ebp)

`mov ebp, esp` - сдвигаем значение регистра esp в регистр ebp(создаём фрейм стека* для данной функции)

`and esp, 0FFFFFFF0h` - выравниваем esp по границе на 16 байт(0x0FFFFFFF0 означает -16)

`sub esp, 20h` - выделяем 32 байт(0x20) в стэке

`call ___main` - вызывается функция ___main, которая нужна для инициализации статических объектов

`mov dword ptr [esp], 400h` - записывает значение 0x400(1024 в стэк)

`call  _malloc` - вызываем функцию для выделения самой динамической памяти

`mov [esp+1Ch], eax` - как говорилось выше, регистры 32-битного режима имеют 4 байта размер, 1Ch(то есть 0x1C(то есть 12)) означает, что значение регистра eax(адрес начала выделенной памяти) ставиться в стэк после трёх таких же регистров в стэке, почему трёх?(12/3=4)

`mov eax, [esp+1Ch]` - тут происходит что-то странное, тут мы помещаем значение адреса 1С(12)(куда был скопирован eax) в eax(странный Min-GW) :)

`mov [esp+4], eax` - здесь мы помещаем значение из eax в стэк после одного 

`mov dword ptr [esp], offset Format ; "%s"` - тут мы в начало стека передаём формат строки

`call _scanf` - вызов функции _scanf

`mov   eax, [esp+1Ch]` - тут означает, что мы берём то самое значение в стэке, которое сохранили в начале выполнения программы

`mov   [esp+4], eax` - тут, то значение, которое мы получили ставим после одного регистра в стэке

`mov   dword ptr [esp], offset Format ; "%s"` - тут мы в начало стек передаём формат строки

`call  _printf` - вызываем printf

`mov eax, 0` - тут мы в регистр eax записываем 0(честно говоря можно было xor eax, eax)

`leave` - тут происходит восстановление значения регистра ebp из стека и перемещение значения esp в ebp.

`retn` - тут происходит возврат из функции и восстановление адреса возврата.

`_main endp` - конец main


Для IDA Pro, x64dbg существуют много плагинов(например snowman), которые смогу облегчить вам дизассемблирование и анализ программы. Я бы хотел вам продемонстрировать работу плагина одного плагина Hex-Rays Decompiler, для чего он нужен? Hex-Rays Decompiler используется чтобы перевести из непонятного ассемблера в псевдо-си синтаксис(иногда это может помочь если вы не привыкли работать с ассемблером, но иногда это не будет работать как надо).


Чтобы попробовать "Превратить" ассемблер в си нужно просто нажать на клавишу F5 и вы получите псевдо си код

давайте посмотрим, как бы выглядела программа, которую я написал. если я воспользуюсь этим плагином:

 Min-GW:

 MSVC:


Всё вышесказанное бы хотел подытожить тем что ассемблер - не простая штука, как показалось на первый взгляд. Я бы порекомендовал для начала прочитать пару книг про ассемблер, чтобы приступить к реверс инжинирингу например книгу от Юрия Магды Ассемблер для процессоров Intel Pentium


            Источники


https://en.m.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Frames - разбор, как устроены функции в MinGW

  https://metanit.com/assembler/tutorial/1.3.php - регистры процессора


Книга реверс для начинающих от Дениса Юричева


Report Page