> For the complete documentation index, see [llms.txt](https://yutewiyof.gitbook.io/intro-rev-ida-pro/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yutewiyof.gitbook.io/intro-rev-ida-pro/chast-09.md).

# Часть 9

[\[Используемые материалы\]](https://github.com/yutewiyof/intro-rev-ida-pro/tree/e1367e11cc661f3d69c02ae5f733b7dd168bc5ab/.gitbook/assets/files/09.zip)

Мы разбираемся понемногу как нужно работать с **ЗАГРУЗЧИКОМ**, мы оставили некоторые вещи на потом, чтобы наблюдать за ними позже в **ОТЛАДЧИКЕ**, например, как меняются флаги в зависимости от используемых инструкций.

Мы будем практиковаться на очень простых примерах, в нашем случае это очень простые крэкми, которые скомпилированы в **VISUAL STUDIO 2015** для нашей практики. Очевидно, для того, чтобы крэкми заработал, у Вас должны быть установлены последние версия библиотек **VISUAL STUDIO 2015 C++**.

<https://www.microsoft.com/es-ar/download/details.aspx?id=48145>

Пакеты **Visual C++ Redistributable** устанавливают компоненты среды выполнения, которые необходимы для запуска приложений написанных на языке **C++** и скомпилированные в **Visual Studio 2015**.

Выберите нужный для своей операционной системы пакет, согласно вашему языку, загрузите и установите библиотеки.

Присоединенный файл - это исполняемый файл, который называется **HOLA\_REVERSER.EXE**([крэкми находится в архиве с оригиналом](http://ricardonarvaja.info/WEB/INTRODUCCION%20AL%20REVERSING%20CON%20IDA%20PRO%20DESDE%20CERO/9-INTRODUCCION%20AL%20REVERSING%20CON%20IDA%20PRO%20DESDE%20CERO%20PARTE%209.7z)) и который запускается под **WINDOWS 7**.

![](/files/-LjpRgS2XsTX5UlG6nqU)

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

![](/files/-LjpRgS4nmaURKdilB0x)

Мы видим, что это очень простой крэкми, если я открою его в **IDA** и только в **ЗАГРУЗЧИКЕ** без отладчика, увидим следующее.

В моём случае (не в Вашем) функция **MAIN** появляется среди других функций, поэтому я могу найти её с помощью комбинации **CTRL+F** здесь на этой вкладке или там, где у меня открыта вкладка с функциями, но это происходит, потому что я скомпилировал программу с помощью **VISUAL STUDIO** и создал **PDB** файл с символами, который обнаруживает **IDA** и загружает оттуда имена функций и имена переменных, мы видим, что в моём листинге равно как и в моём исходном коде, появляется функция **MAIN**, а ниже функция **PRINTF**, мы будем смотреть, что будет происходить в Вашем случае.

![](/files/-Ljtu4WowQMXaC7sS9Lg)

В Вашем случае, **IDA** не скажет, что это за функция, потому что у **IDA'**&#x44B; нет символов.

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

![](/files/-Ljtu4XZCiWqc60QjaZt)

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

Мы видим, что здесь у нас нет символов.

![](/files/-Ljtu4YQDQk3ZMowEwaM)

Мы имеем очень мало информации, но хорошо то, что мы можем видеть строки.

![](/files/-Ljtu4YkksADVNU531IB)

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

Мы можем сделать двойной щелчок на строке **"Pone un numerito\n"**.

![](/files/-Ljtu4Z4c3dLKcOi81de)

Здесь мы видим, что **0x402108** это адрес строки, а рядом с адресом мы видим **ТЭГ**, который помещен рядом и всегда начинается с буквы "**a**" если это строка **ASCII**, а оставшиеся символы принадлежат к той же строки, поэтому её очень легко распознать, в нашем случае тэг называется **aPoneUnNumerito**, а затем идут символы **DB**, потому что строка - это последовательность байт.

Строку можно рассыпать на байты с помощью клавиши **D**, теперь мы видим те же самые символы только по вертикали и по одному.

![](/files/-Ljtu4Z_sAXsExtaK_8r)

Сейчас, когда мы убеждаемся, что это та же строка, мы вновь создаем строку с помощью клавиши **A** и собираем её по горизонтали.

![](/files/-LjpRgSIAfKshU4E8yU4)

Поместив курсор мыши на ссылку в виде маленькой стрелки, мы видим откуда вызывается эта строка, но будет лучше, если мы нажмем клавишу **X**, чтобы увидеть список ссылок и перейдём туда.

![](/files/-Ljtu4_0AY2rNQgZR-aP)

Хорошо, мы находимся в главной функции, здесь она не называется главной, хотя имя буфера, который является общим помечен как **BUF**.

![](/files/-Ljtu4_GWQTzNAlCG1yD)

Предполагается, что мы не знаем исходный код программы, но я Вам его покажу.

![](/files/-Ljtu4baVHmqWjhYNN21)

Я понимаю, что переменные, которые Вы создаете, становятся оптимизированными, например такие как **COOKIE** и **MAX**, которые заменены константами и остаётся только мой буфер, который в моём коде составлял **120** байт в десятичной системе.

Буфер - это пространство памяти зарезервированное, чтобы хранить данные, в нашем случае резервируется **120** байтов.

Как мы можем узнать в **IDA** размер буфера в стеке, если у нас нет исходного кода?

![](/files/-Ljtu4aAiVJevdFwMvd4)

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

![](/files/-LjpRgSS8EfhERrisn59)

Здесь мы видим буфер **BUF**, но он определен как последовательность байт **DB** (**IDA** если нет символов не может обнаружить буфер). Чтобы собрать его в массив символов или пустой массив, щелкните правой кнопкой мыши на слове **BUF** и выберите параметр **ARRAY**.

-= Примечание от **Яши** — Можно также нажать символ **\*** на цифровой клавиатуре=-

![](/files/-LjpRgSUqlrEgrEeroUd)

Здесь мы видим, что до следующей переменной или того, что находится ниже в стеке, **IDA** обнаружила **120** десятичных **DB**, а элемент массива равен длине каждого поля, так как размер поля равен **1**байту, то это массив символов или байтов и его сумма рассчитывается так **120\*1** байт или просто **120** байт.

Если согласимся с этим, то получится так.

![](/files/-Ljtu4bM-VcksAYeEZRh)

Я вижу, что буфер состоит из **120** байт, что соответствует моему исходному коду, хотя компилятор может сделать его больше, пока он минимум равен **120** байт, то всё хорошо, в этом случае они равны. **DUP** - означает дубликат (на самом деле он должен быть умножен) **120** раз на символ "**?"**, потому что значение ещё не определено и это соответствует пустому статическому буферу.

![](/files/-MX3mJITvSg683yHS325)

Я буду прояснять представление статического стека позже, но ниже **BUF** есть переменная **DWORD** (**DW**) называемая **VAR\_4**. **S** и **R** - это сохраненный **EBP** родительской функции, которая вызвала эту функцию и **АДРЕС ВОЗВРАТА**, как мы видели при входе в первую функцию - сначала кладутся аргументы с помощью **PUSH**, а затем делается **CALL**, чтобы войти в функцию, которая сохранит **АДРЕС ВОЗВРАТА** в стек, и локальные переменные будут выше **S**.

> **ПЕРЕМЕННЫЕ**
>
> …
>
> …
>
> **S** (Сохраненный **EBP** - как правило, происходит от **PUSH EBP**, которая является первой инструкцией функции)
>
> **R** (Адрес возврата)
>
> **АРГУМЕНТЫ**

Поскольку аргументы помещаются в стек перед **CALL**, который помещает адрес возврата в стек, они будут ниже него, затем будет идти адрес возврата, а выше него **СОХРАНЕННЙ EBP** порожденный инструкцией **PUSH EBP**, которая как правило первая инструкция в функции, а затем выше идет пространство локальных переменных, мы увидим их более подробно позже.

Если я нажму **X**, чтобы увидеть откуда вызывается эта функция, то увижу:

![](/files/-Ljtu4bsdw_xKsLwDcss)

![](/files/-Ljtu4c5E_DjQ7_ygmTj)

И я иду туда и вижу, что у нас есть инструкции **PUSH**, которые передают аргументы функции, имя которой я видел, когда работал с **СИМВОЛАМИ**, а сейчас её здесь нет, 3 аргумента ниже пропущены, ниже есть изображение с символами.

![](/files/-LjpRgSddEcn8OSrYzpu)

**IDA** обнаружила, что никогда не используются такие аргументы как **ARGC**, **ARGV** и **ENVP**, они присутствуют по умолчанию в функции **MAIN**, но так как там не было никаких ссылок на них или их использование в функции, **IDA** избавилась от них.

![](/files/-LjpRgSfaAk8S75wqfVC)

Кроме того, в моём коде даже не передаются переменные как аргументы к функции **MAIN**, так что все хорошо - функция без аргументов.

Когда мы хотим увидеть из какого места получается доступ к переменной, мы отмечаем её и нажимаем **X**.

![](/files/-LjpRgSh4NuEkYxKdM9j)

Мы видим, что **VAR\_4** используется в двух местах, для тех кто не знает, это переменная **COOKIE**, которую я не программировал, это защита от переполнения стека, программа сохраняет их в начале при запуске функции, проверяет их на целостность при выходе из функции, теперь мы можем переименовать их в **COOKIE\_DE\_SEGURIDAD** или **CANARY**.

![](/files/-LjpRgSjUjDN9yaQ2h6a)

Мы видим, что когда функция хочет напечатать строки она вызывает **CALL** в которой в конечном итоге будет вызвана функция **PRINTF** для печати строк.

![](/files/-LjpRgSlDTNwCA2Oh1tg)

Мы видим, что в версии с символами **IDA** обнаружила её непосредственно как функцию **PRINTF**.

![](/files/-Ljtu4eo0jcYodBdeDTC)

Но только если мы посмотрим внутрь **CALL** то узнаем, что аргументы, это строки, которые печатаются в консоли и мы делаем вывод, что это функция **PRINTF**.

![](/files/-Ljtu4fD6hNbKYR6d3bo)

Мы видим, что внутри эта функция заканчивается вызовом **VFPRINTF**, так что, это то, что нам нужно переименовать.

![](/files/-LjpRgSr6XXvZQsn1zkc)

У нас остаётся только буфер из **120** байт, давайте посмотрим, что будет с ним происходить.

Мы видим, что буфер идет как аргумент к функции **GETS\_S** - эта функция, которая получает то, что мы вводим в консоли.

![](/files/-Ljtu4fhTgSfQm0ToXDu)

Мы видим, что есть два аргумента - указатель на буфер и его максимальный размер, в который позволено нам печатать, давайте посмотрим, что будет в нашем случае.

![](/files/-Ljtu4gHzknAHDTLlDEV)

Ранее мы сказали, что **LEA** вычисляет адрес переменной, в этом случае **LEA** является указателем на буфер **BUF**, который передаётся с помощью инструкции **PUSH EAX**, а затем идёт **PUSH 0x14**,который указывает максимальное количество символов, которое можно ввести в консоли.

![](/files/-Ljtu4gSLhQQofO-XVE3)

В моём исходном, мы видим тот же самый вызов **GET\_S** с двумя аргументами - **БУФЕР** и его максимальное значение, который я обозвал как переменную **MAX** из **20** элементов, но компилятор, чтобы сохранить пространство поместил именно **20** в десятичной или **0x14** в **HEX** в этот аргумент, так как не нужно использовать больше.

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

Позже, тот же указатель на буфер помещается как аргумент к функции **ATOI**.

![](/files/-Ljtu4gkxQSjDTc-uhPo)

Она делает то же самое.

![](/files/-Ljtu4h4L1c35G3VUS8P)

Эта функция конвертирует строку в целое число и если она не может это сделать, потому что происходит переполнение (выход за максимально возможное значение), нам будет показываться ошибка и вернётся ноль, тоже самое происходит и с отрицательным числом, но идея состоит в том, что всё что я буду вводить будет конвертироваться в число, если я введу значение **41424344** оно будет конвертироваться в десятичное значение **41424344**, а так как в ассемблере мы работаем с **HEX** значениями, оно будет возвращено в **HEX** через регистр **EAX**.

Каждая функция возвращает генерируемое значение **INT**, она интерпретирует входные символы как число. Функция возвращает значение **0** для **ATOI** и **\_WTOI**, если ввод не может быть преобразован в значение этого типа.

![](/files/-MX3mJJmqdWhw4qvbyvQ)

Мы видим, что значение возращенное из **EAX** переносится в **ESI** и после печати первоначальной строки, которая была введена, сравнивает **ESI** с числом **0x124578**.

Так что, то, что набрано интерпретируется как строка в десятичном формате, которая возвращается в **HEX** виде и это число сравнивается с этой константой, передавая десятичное значение этой константы.

![](/files/-LjpRgT2azI1UEa9DMPx)

Мы видим, что если сравнение не равно (**JNZ**) оно переносит меня к **BAD REVERSER**, а если равно, то переносит к **GOOD REVERSER**, давайте попробуем в консоли **PYTHON** увидеть десятичное значение числа **0x124578**.

![](/files/-LjpRgT4hynRcdwv7Nnx)

Мы вводим это значение в наш **CRACKME**.

![](/files/-LjpRgT65_n1Pg6D-Brv)

![](/files/-LjpRgT8IONNhEjXmrPe)

Хорошо, это простой пример статического реверсинга, далее также продолжим работу с ЗАГРУЗЧИКОМ

До встрече в **10**-й главе.

Автор оригинального текста — Рикардо Нарваха.

Перевод и адаптация на английский язык — IvinsonCLS.

Перевод и адаптация на русский язык — Яша Яшечкин.

Перевод специально для форума системного и низкоуровневого программирования - WASM.IN

[**Источник: ricardonarvaja.info**](http://ricardonarvaja.info/WEB/IDA%20DESDE%20CERO/CURSO%20DE%20IDA%20TUTES/9-INTRODUCCION%20AL%20REVERSING%20CON%20IDA%20PRO%20DESDE%20CERO.docx)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://yutewiyof.gitbook.io/intro-rev-ida-pro/chast-09.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
