> 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-25.md).

# Часть 25

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

## СТРУКТУРЫ

В этой части, мы начнём изучать, как **IDA PRO** помогает нам при реверсинге, когда программа использует структуры.

В конце этой части, решения для файлов **IDA3** и **IDA4** будут краткими, потому что похожая проблема уже обсуждалась.

Что же такое структура?

Нам не нужно очень техническое определение, но мы видели, что **МАССИВЫ** это контейнеры, которые резервировали пространство в памяти для своих полей. Поля были того же типа, что и сами массивы, поэтому **МАССИВЫ** могут быть из байтов, слов, двойных слов. Смысл в том, что в массиве не может быть полей другого типа.

![](/files/-Ljtu_bjv4SULgwbzptm)

Здесь мы видим пример **МАССИВА**, размер которого равен **34** байта и каждый его элемент имеет размер **2** байта, т.е. каждый элемент - это **СЛОВО**. Поэтому общая длина массива будет равна **34** \* **2**, т.е. **68** десятичных байт. (Почему на картинки тогда **DD** и **DB**, а не **DW** ??? Без практики тут не понять. Вижу только один **DW**. Нарваха опять ошибается или всё правильно? Прошу помощи у читателей. - Прим. Яши)

В этом примере **МАССИВА**, каждый его элемент это слово. Другими словами элемент занимает **2** байта (На картинке только один **DW** – Прим. Яши). Если мне нужен массив, каждый элемент которого по размеру равен **1** байту, я буду должен создать другой массив, так как я не могу смешивать в нём данные другого размера или типа.

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

![](/files/-Ljtu_c6i0yanACHNmsX)

<http://decsai.ugr.es/~jfv/ed1/c/cdrom/cap7/cap71.htm>

Структура - это тип составных данных, который позволяет Вам хранить набор данных разного типа. Данные, которые содержит структура, могут быть простого типа (символы, целые числа или вещественные и т.д.) или, в свою очередь, составного типа (векторы, структуры, списки, и т.д.). Каждый элемент из данных или элементов, хранящихся внутри структуры, называется членом этой структуры и он будут принадлежать к определенному типу данных.

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

```cpp
struct MyStruct
{
    char * p_size;
    char size;
    int cookie;
    int leidos;
    int cookie2;
    int maxsize;
    int(*foo2)(char *);
    void * buf2;
    char buf[50];
};
```

В **C++** мы могли бы определить структуру таким способом. В этом примере, мы видим структуру названую мной **MYSTRUCT**, которая имеет несколько полей внутри. Необязательно быть великими гениями программирования, чтобы понять, что это не то же самое, что переменная **INT**, переменная **CHAR** или буфер длиной в **50** байт.

Если у меня есть **VISUAL STUDIO**, я могу увидеть размер всей структуры, который равен **0x54** байта.

![](/files/-Ljtu_cPnZJh9z_M2t3Z)

Я делаю это, чтобы позже сравнить результат с тем, что у нас получится в **IDA**. Всё это не имеет большого значения, если Вы не очень хорошо понимаете, как с этим работает **VISUAL STUDIO**. Просто примите это как информацию к размышлению.

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

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

```cpp
char * p_size; // длина указателя = 4 байта
```

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

```cpp
int(*foo2)(char *); // длина указателя на функцию = 4 байта
void * buf2; // длина указателя на буфер = 4 байта
```

Однако, последний буфер не является указателем (он не имеет символа звездочки **(\*)**, так как это просто буфер, который прямо внутри структуры занимает **50** байтов её же длины).

```cpp
char buf[50]; // размер буфера = 50 байт
```

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

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

![](/files/-LjqtGasC3vLZK0P3KDr)

Мы видим простой код, который получает аргумент через консоль. Если мы не запустим пример с аргументом, программа покажет нам сообщение "**BYE BYE**".

Мы могли бы запустить файл так.

![](/files/-LjqtGawTQLMHki18Ttp)

Если мы просто сделаем двойной щелчок, т.е. не будем вводить какой-нибудь аргумент, программа проверит, что переменная **ARGC**, которая является количеством аргументов, отличается от **2** и завершит своё выполнение (количество аргументов включает в себя имя исполняемого файла, так что, в этом случае, было бы два аргумента. Первый **PEPE.EXE** и второй **aaaaaaaaaaa**)

```cpp
if(argc != 2) {
    printf("Bye Bye");
    exit(1);
}
```

Мы видим, что программа пропускает эту проверку, так как **ARGC** равен **2**.

Кроме определения этой структуры как типа данных, мы можем сделать, чтобы существовали переменные, которые принадлежат типу **INT**, **CHAR**, **FLOAT** или любому другому типу. Также мы можем сделать, чтобы переменные были типа **MYSTRUCT**.

Точно так же мы объявляем целочисленную переменную, например, ставим тип данных спереди.

```cpp
int pepe;
```

Это похоже на переменную структурного типа.

```cpp
MyStruct valores;
```

Значения переменной будут типа **MYSTRUCT**. Они будут иметь то же определение, ту же длину и те же поля.

Мы могли бы создать несколько разных переменных типа **MYSTRUCT**

```cpp
MyStruct pinguyo;
```

И чтобы ссылаться на некоторых свои поля, структура использует:

```
valores.size
pinguyo.size
valores.cookie2
```

Таким способом, в программе значения присваиваются в некоторые поля переменой.

```cpp
valores.cookie2 = 0;
valores.size = 50;
```

И затем программа копирует символы **A** вызовом функции **STRNCPY**, которые находятся в переменной **ARGV\[1]** в буфер **VALORES.BUF** длиной 50 десятичных байт, принимая как максимальный размер буфера, значение **VALORES.SIZE**, которое равно **50**.

```cpp
strncpy (valores.buf , argv[1], valores.size);
```

Таким образом, здесь не будет переполнения, потому что программа копирует **50** байтов в буфер длиной **50** байтов, и он не переполняется.

Затем программа печатает то, что мы ввели. Все это хранится в буфере **VALORES.BUF**.

```cpp
printf(valores.buf);
```

И наконец идёт вызов функции **GETCHAR()**, поэтому программа не будет закрываться, пока мы не нажмем любую клавишу и мы можем увидеть символы **A**.

Сейчас, давайте откроем исполняемый файл в **IDA** и увидим, как мы можем интерпретировать вышесказанное.

![](/files/-Ljtu_dc8ha5-6MWyO_l)

Мы видим, что когда есть символы, то это просто счастье. **IDA** обнаружила переменную **VALORES** как переменную типа **MYSTRUCT**, без проблем. Даже внутри кода, мы видим, что **IDA** обращается к полям структуры по именам.

![](/files/-LjqtGb6lGKtbveekWHi)

И здесь тоже.

![](/files/-Ljtu_eNnETe1aU0JTee)

Мы видим, что **IDA** обнаружила буфер из **50** байт. Она уже переименовала его в **VALORES.BUF**, в вызовах функций **STRNCPY** и в **PRINTF** наконец.

Даже если мы перейдём на вкладку **STRUCTURES**.

![](/files/-Ljtu_ej47C_ks2wUSgH)

![](/files/-Ljtu_f3S8M5YgnkU3Q_)

Мы видим, что определена структура. Если мы нажмём здесь **"CTRL" плюс "+"**.

![](/files/-LjqtGbGtZCvZLR2ym14)

Мы видим, что размеры и имена соответствует тому, что переменная **SIZE** равна **1** байт или **DB**, переменная **COOKIE2** равна **4** байта или **DD** и **BUF** состоит из **50** десятичных байтов.

```cpp
struct MyStruct
{
    char size; // длина 1 байт
    int cookie2; // длина 4 байта
    char buf[50]; // длина 0x32 байта
};
```

Даже в **IDA** существует вкладка **LOCAL TYPES** для того, чтобы редактировать и вводить структуры в формате **С++**. Я мог увидеть, есть ли они там.

Их существует очень много. Но поскольку у меня есть фильтр, то с помощью **CTRL** + **F**, я ввожу **MY** и мне показывается теперь так

![](/files/-Ljtu_fgVaL2s5S_1W0y)

И я могу увидеть это просто сделав правый щелчок и выбрав пункт **EDIT**.

![](/files/-Ljtu_gAFkd6VOCIywtZ)

Видно, что всё правильно.

Но поскольку в жизни не всё происходит как мы хотим и почти никогда у нас не будет символов, нам придется немного попотеть, так как **IDA** не может обнаружить без них поля или структуру, хотя она даёт нам интерактивные инструменты для этого.

Если я cкомпилирую и переименую **PEPE.PDB**

![](/files/-LjqtGbOohocv7S8qOQz)

**IDA** не найдет символов, и отлаживать файл будет сложнее.

Я снова открываю файл **PEPE.EXE** и повторно дизассемблирую его.

![](/files/-Ljtu_gmq751qTV9jQTV)

Мы видим, что структура уже не обнаружена, но её поля видны как отдельные переменные.

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

Мы это ещё увидим, но сейчас просто верьте мне. В реверсинге Вы должны знать как работать со структурами.

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

![](/files/-Ljtu_h30jXUNTP4yZ1F)

Из предыдущего реверсинга, мы уже знаем переменную **CANARY**, поэтому давайте переименуем её.

![](/files/-Ljtu_hL3cyCp76sKueJ)

Программа сравнивает переменную **ARGC** с числом **2**. Если **ARGC** равна этому числу, то программа продолжает выполняться, а если не равна, то будет печатать нам слова "**BYE BYE**" и переходить к **EXIT**. Мы пометим этот блок в красный цвет, а в зеленый цвет хороший блок.

![](/files/-LjqtGbX5N08xXvjxoh2)

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

![](/files/-LjqtGbZ6Lj16THytani)

Переменная **VAR\_40** больше не используется. В любом случае, я переименую переменную в **SIZE**.

![](/files/-LjqtGbayBKGNnILU7jW)

Помещая курсор над этой переменной, мы видим, что **IDA** определяет её как байтовую переменную (**DB**).

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

![](/files/-Ljtu_ilg3lv3f-M9VXA)

Мы знаем, что в оригинальном коде переменная **COOKIE2** не используется и программа помещает в неё в нуль.

![](/files/-Ljtu_j9_m9T_w829g3j)

```cpp
valores.cookie2 = 0;
```

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

![](/files/-Ljtu_jUHgJe-5SC4V9B)

Давайте посмотрим на статическое представление стека.

![](/files/-Ljtu_jiiUF6lH5I9alW)

Нам не хватает буфера **DEST**. Ниже мы видим пустое пространство, которое говорит нам, что это может быть буфер. Когда я ищу ссылки с помощью **X**, то нахожу

![](/files/-Ljtu_k-jEqJCGjLLdVX)

Почти всегда буферы будут иметь некоторую ссылку, т.е. будут использовать инструкцию **LEA**, так как для заполнения мы должны передать адрес некоторой функции. Функция **STRNCPY** получает адрес буфера через **LEA**.

![](/files/-LjqtGbmkNOeN4-ENs7l)

В этом случае, программа резервирует **52** байта вместо **50**, что обычно имеет место.

![](/files/-Ljtu_kc6ZkQu2cB-EFe)

Мы уже видим законченное представление стека. Из этого мы можем понять, что переполнения нет, потому что мы знаем что буфер **DEST** имеет длину **52** байта, а программа копирует в него **0x32** байта через функцию **STRNCPY**.

![](/files/-Ljtu_kvce7QQOm4h2Gh)

![](/files/-LjqtGbsw0s9F3ebGUCE)

Программа копирует **50** десятичных байтов в буфер длиной **52**, что делает его не уязвимым, и переполнение не происходит.

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

Давайте пойдём в статическое представление стека.

Если есть структура, она не будет содержать переменную **CANARY**, которая добавляется компилятором.

![](/files/-LjqtGbu-hIQkernXFOx)

Возможно структура охватит эти адреса.

Таким образом, я помечаю эту область и иду в меню, в пункт **EDIT**→ **CREATE STRUCT FROM SELECTION**

![](/files/-Ljtu_lms8AtpseCTxWh)

![](/files/-LjqtGbyXLQIwLhtGJhh)

И это становится таким. Если мы посмотрим во вкладку структуры.

![](/files/-Ljtu_mQ8m7kcm07tHNM)

Переменная **STRUCT\_0** типа **STRUCT** могла бы выглядеть так, но мы переименуем её в соответствии с нашим кодом.

![](/files/-Ljtu_mfL9ZY368m-FX3)

И в статическом представление стека, переменную типа **MYSTRUCT** мы переименовываем в **VALORES**.

![](/files/-LjqtGc35qIDiyXJy9V9)

Теперь всё это стало похожим на то, когда у нас есть символы.

![](/files/-Ljtu_nHfRjb2uzQqUiu)

Мы видим, что, по крайней мере, в этой функции, где определена переменная **VALORES**, **IDA** автоматически переименовывает поля в **VALORES.XXXX**

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

В следующей главе, мы продолжим с более сложными примерами структур.

Давайте посмотрим на решения упражнений **IDA3** и **IDA4**.

## РЕШЕНИЕ IDA3

![](/files/-Ljtu_nYd6zqhnN-NLI-)

Переменная **TEMP** выше добавлена компилятором.

![](/files/-Ljtu_nzOscIe_t5O24V)

Здесь я вижу представление стека, и переменные, которые я должен перезаписать, это - **COOKIE** и **COOKIE2**. Мы уже знаем, что Вы вводите данные через функцию **GETS**, так что это пример уязвимый программы. Функция не ограничивает количество байт, которое Вы вводите.

Давайте посмотрим, сколько нам нужно байт, чтобы перезаписать переменную **COOKIE**.

Поскольку **COOKIE** находиться чуть ниже **БУФЕРА**, который состоит из **68** десятичных байтов, тогда нужно передать

**68 \* ‘A’ + "ABCD"** байт

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

![](/files/-LjqtGcBV0dMQ7knyYIX)

Мы видим, что несмотря на то, что нет определения хорошего сообщения, программа позволяет печатать мне то, что я хочу, так как она печатает то, что хранится в буфере. Если я обойду проверку обоих переменных **COOKIE**, я могу поместить в буфер вместо букв **A** следующее предложение

"**Lo pude hacerrrr!!!!"**

Мы подготовим скрипт вместе. Также мы видим, что после переменной **COOKIE** идёт **4** байта переменной **FLAG** и **4** следующих байта - это переменная **COOKIE2**.

![](/files/-Ljtu_obxgr6VExgyH6h)

![](/files/-Ljtu_owUShQBpkZvuew)

Ели я запущу эту версию скрипта.

![](/files/-Ljtu_pI7i-hcMJddzke)

Мы видим, что я поместил строку впереди выражения и вычитаю **68** байт из длины этой же строки для того, чтобы осталось в итоге **68** байт, поэтому **COOKIE** и **COOKIE2** не будут перезаписаны строкой и мы останавливается на границе с ними.

```
string + (68 - (len(string))) * "A"
```

![](/files/-Ljtu_plZT42j2sm_2HY)

## РЕШЕНИЕ IDA4

В примере **IDA4** мы видим, что существует функция проверки **CHECK**.

![](/files/-LjqtGcL8OtHovbpJlEM)

Давайте реверсить функцию **MAIN**.

![](/files/-Ljtu_qVlynGxHtQ56z2)

Мы видим, что в функции **PRINTF**, программа печатает три адреса: **COOKIE**, **COOKIE2** и буфер.

Давайте переименуем переменные.

![](/files/-LjqtGcP-8wpW01xrkSD)

Затем программа передаёт буфер в функцию **GETS**, поэтому мы знаем, что он будет уязвимым. Просто посмотрите на длину буфера.

![](/files/-Ljtu_rD6lDFxUdV5e_i)

Мы видим, что он равен **50** десятичных байт.

![](/files/-LjqtGcT8o1sphxOgXbi)

И дальше идут **2 КУКИСА**, по **4** байта каждый.

![](/files/-Ljtu_rgaBiEbjguj5xx)

Мы видим, что два **КУКИСА** передаются как аргументы к функции **CHECK**, плюс добавляется переменная **VAR\_4**, про которую мы ещё ничего не знаем. Мы будет, пока, называть её как **ФЛАГ**, позже мы изменим ей имя.

![](/files/-Ljtu_s-gfo4bRcYQ33k)

Мы видим, что самый дальний аргумент это **FLAG**, затем идёт переменная **COOKIE** и наконец **COOKIE2**. Давайте войдем в функцию **CHECK**.

Мы видим три аргумента и локальную переменную.

![](/files/-LjqtGcZiwoXwj-iXvxU)

В статическом представлении стека всё это выглядит хорошо.

![](/files/-Ljtu_so17UzWzN258pf)

То, что находится ниже линии, под **S** и **R** будет аргументами, а выше - будет локальными переменными.

![](/files/-LjqtGcdIaVBQvhcH4Qr)

Я переименовываю их согласно порядку, в котором они идут и делаю правый щелчок и выбираю **SET TYPE** для того, чтобы распространить имена по функции **MAIN** и вижу, что все хорошо.

![](/files/-LjqtGci3-OKjdKy3cbU)

Мы видим, что имена совпадают.

![](/files/-LjqtGcl5Y8dhH4MKbh9)

![](/files/-Ljtu_u17DCCeBss3zfj)

Мы видим, что единственный способ добраться до **RET** и не пойти на **ВЫХОД** - это идти по зеленым блокам и **COOKIE2** должно быть равно **qrUS**, а **COOKIE** равно **0x10200D0A**. Давайте добавим эти значения в наш скрипт.

![](/files/-LjqtGcpP11NR9HZ_ybo)

С этими значениями, мы оставим функцию **CHECK**, но всё ещё чего-то не хватает. Мы видим, что значение, которое возвращается функцией **CHECK** - это значение флага.

![](/files/-Ljtu_udq2xs08A21FU-)

![](/files/-LjqtGcw2NqGm4lN4Lw8)

Это значение, которое программа сохраняет в переменную **VAR\_8** и наконец сравнивает. Так что флаг должен быть равен значению **35224158**.

![](/files/-Ljtu_vAu1qrYeimRuwO)

![](/files/-Ljtu_vTV6Bx2HDrP5mQ)

Если мы попробуем теперь такой скрипт.

![](/files/-LjqtGd10iKawqTHpj-9)

Он работает. В следующей части, мы рассмотрим больше структур.

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

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

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

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

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

10.12.2017

[**Источник: ricardonarvaja.info**](http://ricardonarvaja.info/WEB/IDA%20DESDE%20CERO/CURSO%20DE%20IDA%20TUTES/25-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-25.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.
