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

# Часть 27

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

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

[http://ricardo.crver.net/WEB/INTRODUCCION AL REVERSING CON IDA PRO DESDE CERO/EJERCICIOS/IDA\_STRUCT\_RESOLVER DESPUES DE LA PARTE 26.7z](http://ricardo.crver.net/WEB/INTRODUCCION%20AL%20REVERSING%20CON%20IDA%20PRO%20DESDE%20CERO/EJERCICIOS/IDA_STRUCT_RESOLVER%20DESPUES%20DE%20LA%20PARTE%2026.7z)

Исполняемый файл называется **CONSOLEAPPLICATION4.EXE** и в том же каталоге находятся символы **CONSOLEAPPLICATION4.PDB**.

Когда **IDA** загрузит файл, то сообщит Вам, что она пытается найти символы. Вы можете указать на этот файл и загрузить символы, с которыми программу станет немного яснее понимать. Но для нашего примера, я буду удалять или переименовывать файл **PDB**, чтобы загрузить программу без символов, что соответствует тому, с чем мы обычно имеем дело. Хотя это и выглядит сложнее, зато приближено к реальности.

![](/files/-Ljr6D1B4Os6ADSfd5xt)

Очевидно, что без символов, функция не будет определяться как **MAIN**. Мы можем добраться до неё, так как это консольное приложение, которое ищет аргументы **ARGV** или **ARGC**, а если их не&#x442;*\*\**, то её, обычно, можно найти через поиск строк.

Если мы запустим программу, то увидим, что первое, что она сделает - это попросит нас ввести число. Здесь Вы видите строки, которые могут быть хорошим и плохим сообщением.

![](/files/-Ljtu97bKyKEq_jHlg_A)

Щелкните на строке **PLEASE ENTER YOUR NUMBER OF CHOICE:**

![](/files/-Ljr6D1KCjpSXC-RcAmH)

Мы видим, что строка имеет ссылки. Помещая курсор над стрелкой или нажимая клавишу **X** на адресе, мы видим, что строка имеет ссылку.

![](/files/-Ljr6D1NkdgLfMcMpdZh)

![](/files/-Ljr6D1Qd3z-hK-LCn8B)

Давайте перейдём по этому адресу.

![](/files/-Ljtu99JNRq4SS9TPEVU)

Видим, что мы находимся внутри функции. Далее видно вывод строки и вызов функции для печати этой строки. Поскольку у нас нет символом, **IDA** не говорит нам, что функция по адресу **0x401220** - это функция **PRINTF**. Если мы посмотрим во внутрь этой функции.

![](/files/-Ljtu99oNRB1xx-PwLOQ)

Вы можете посмотреть во внутрь этой функции и увидеть, что внутри есть ещё несколько функций.

![](/files/-Ljr6D1ZCI4vbuFFffeN)

![](/files/-Ljtu9AJwCIkdlUVdByP)

В **PROXIMITY VIEW**, в который можно войти нажав клавишу **-** и выйти, нажав **+**, мы видим, что функция **0x401220** вызывает те же самые три функции, но две функции **0x401000** и **ACRT\_IOB\_FUNC** - это функции, которые что-то делают и возвращаются назад. Они не идут к другим дочерним функциям.

![](/files/-Ljr6D1foHoS2PpL1a2C)

Здесь, где есть стрелки, которые я добавил, видно, что блоки не идут к другим функциям. Только одна функция, которая вызывает другие функции - это функция по адресу **0x4010F0**, которая вызывает две функции и первая из вызываемых, это функция **VFPRINTF**. Функция после своего выполнения возвращается обратно и ниже уже нет никаких других функций.

Это также можно увидеть в дизассемблерном списке. Если я посмотрю во внутрь каждой функции, я увижу то же самое.

Видно, что функция **0x401000** никуда не ведёт. Функция просто исполняет ерунду и возвращает выполнение.

![](/files/-Ljtu9B1KtwxLLikdLZd)

![](/files/-Ljtu9BMVNNktEM6sa5f)

И **\_ACRT\_IOB\_FUNC** - это **API** функция. Поэтому функция не будет продолжаться??? Функция будет только инициализировать поток **STDOUT**, чтобы затем быть готовой печатать текст.

![](/files/-Ljtu9Bg8ywCOhs35QWk)

Другими словам, передача аргумента **1**, как в нашем случае, будет инициализировать поток **STDOUT**.

![](/files/-Ljtu9Bz-Rf2eNM2HX3A)

И третья функция, которая будет вызываться, это функция по адресу **4010F0**.

![](/files/-Ljr6D1ytRIcTmVd1P1m)

Функция заканчивается вызовом **VFPRINTF**, т.е. мы пришли к тому же, что видно и в **PROXIMITY VIEW**, но это занимает больше времени.

![](/files/-Ljr6D20rEiNDyEusY8i)

Так что давайте переименуем функцию по адресу **0x401220** в **PRINTF**.

![](/files/-Ljtu9CwDb1VLLLkxuIK)

Те блоки, которые заканчиваются вызовом **API**, как в этом случае **PRINTF**, я буду закрашивать в небесный цвет. Каждый будет делать это по своему вкусу.

![](/files/-Ljr6D24Yw78DOd-UftB)

Следующая функция по адресу **0x40109D** – это конечно же функция **SCANF**. Если мы перейдём в неё и посмотрим в **PROXIMITY VIEW**, то увидим:

![](/files/-Ljr6D26SMhe_wtwvvs-)

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

![](/files/-Ljtu9DkcB8sB2652Z5u)

Мы видим, что это функция **SCANF**.

![](/files/-Ljtu9E2Xve8VAwLWX6y)

И в этом случае, функция **\_ACRT\_IOB\_FUNC** с аргументом **0** инициализирует поток **STDIN**.

![](/files/-Ljtu9ELU6ugCvm9IO9W)

Поэтому, мы переименовываем эту функцию в **SCANF**.

![](/files/-Ljtu9EhHE7FN4MTjVkz)

![](/files/-Ljtu9F-cEDXU7BpHbka)

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

Давайте посмотрим на ссылки для следующую функции.

![](/files/-Ljtu9FIH9EcnuNcPSA3)

Мы видим, что есть две ссылки. Если я зайду в первую.

![](/files/-Ljr6D2QtO2yMFbbD8rR)

Я вижу, что аргумент в обоих случаях является адресом, который даёт представление о структурах.

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

Мы видим, что максимальное смещение, которое я нахожу до настоящего времени равно **0x14**. Поэтому я буду создавать структуру этой длины. Если структура станет больше, я буду увеличивать её размер.

![](/files/-Ljtu9FvPCzx-OHQSpHi)

Для этого, я иду во вкладку **STRUCTURES**. Это один из способов её создания. Другой способ - это перейти в **LOCAL TYPES** и создать её как код в стиле **C**. Давайте так и сделаем.

Это немного раздражает и это не очень интуитивно. Но хорошо когда мы находимся в месте где определена структура. Здесь мы можем сделать **CREATE STRUCT FROM SELECTION**. Обычно мы будем создавать структуру в некоторой функции где она не определена, ничего не зная о ней.

Я знаю, что если проанализировать представление стека функции **MAIN**, я мог бы здесь использовать опцию **CREATE STRUCT FROM SELECTION** и это облегчило бы мне жизнь, но давайте возьмём наихудший случай. Представим, что мы находимся в функции очень большой программы и что мы далеко от того места, где она была определена, так что мы должны исправить их как только можем.

![](/files/-Ljr6D2Vk9jugdlyn3gM)

Здесь мы видим, что для создания структуры Вы должны нажать клавишу **INS**. Сделаем же это.

![](/files/-Ljtu9GP5L_jfx_dBPbq)

Я могу назначить имя, какое захочу. Назовём нашу структуру **MYSTRUCT**.

![](/files/-Ljr6D2ZDWqXokJkaJIa)

Здесь она была создана мной с размером **0**. Сейчас я буду делать трюк. Когда я всё ещё не знаю полей или чего-то ещё и я хочу дать полю размер, сначала я нажимаю **D** на слове **ENDS**, чтобы добавить одно поле.

![](/files/-Ljr6D2acCjoX_crASRY)

Здесь я добавляю поле длиной **1** байт **DB**. Если бы я снова нажал **D**, я бы переключился на слово **DW** и затем на **DD**.

Но здесь, поскольку мы не знаем, что нам нужно, мы оставляем всё так и делаем правый щелчок на структуре.

![](/files/-Ljr6D2cpcefDhizNO7s)

Так как я видел поле со смещением **0x14**.

![](/files/-Ljr6D2h8lvTxEOc9RbY)

Таким образом, чтобы заполнить это поле с помощью **DWORD**, структуре нужно ещё **4** байта, поэтому я буду создавать структуру из **0x18** байт. Я буду добавлять ещё **0x17** байт к байту, который был у структуры.

![](/files/-Ljr6D2jdOVDKMH9mlFt)

![](/files/-Ljr6D2mGIeYdUMu6xZk)

Я вижу, что размер стал равен **0x18**. Теперь мы оставим структуру такой.

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

В исходном коде, это выглядит так. Чтобы уточнить две переменные типа **MYSTRUCT** первая называется **PEPE**, а другая **JUAN**. Обе передают свой адрес как аргумент в функцию.

![](/files/-Ljtu9IlMqAau_a8ev86)

![](/files/-Ljr6D2vzNx3p3wn4UUj)

Так как одна и та же функция сначала будет иметь адрес первой структуры или **PEPE** в аргументе **ARG0** и во второй раз, когда она будет вызвана она будет иметь адрес структуры **JUAN**, то я буду давать структуре общее имя для обоих случаев, например \_**STRUCT**.

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

![](/files/-Ljr6D322l7wXw74iRTM)

Я вижу, что определение переменной является простым типом **INT**, а не как в исходном коде как адрес структуры. Я могу это исправить так.

![](/files/-Ljr6D35y8i5hXkJRkhO)

Это позволяет выбрать адрес структуры, в котором она находится, и здесь мы будем выбирать тип **MYSTRUCT**.

![](/files/-Ljr6D3BUwmfZMig4mYZ)

Очевидно, что **BUF** - это **PEPE** и здесь он получает свой адрес и передаёт его как аргумент. Давайте посмотрим **BUF** в представлении стека.

Поэтому структура необязательна, чтобы создать её, потому что она уже существует. Я просто должен сказать Вам, что **BUF –** это переменная типа **MYSTRUCT**. Для этого нажмите сочетание **ALT** + **Q** на переменной **BUF**.

![](/files/-Ljr6D3D5pND4N6jW4ZF)

и **IDA** назначит переменной **BUF** тип **MYSTRUCT**. Если мы поместим размер ниже, некоторые поля будут опущены, но затем можно будет увеличить структуру **MYSTRUCT** и она будет исправлена только здесь.(Если она не сломается конечно)

![](/files/-Ljr6D3HwAQlPJDg0E69)

Мы переименовываем **BUF** в **PEPE**.

Мы видим здесь, что адрес **PEPE** передан и во втором вызове передаётся адрес **VAR**\_**44**, которая также будет переменной **JUAN** типа **MYSTRUCT**, поэтому мы пойдём в представление стека и на переменной **VAR**\_**44** мы также нажмём **ALT** + **Q**.

![](/files/-Ljr6D3L-E1a4xiXzsvx)

У нас уже есть две структуры типа **MYSTRUCT**.

Я возвращаюсь в функцию.

![](/files/-Ljtu9LAP8fyBddWOSBe)

![](/files/-Ljtu9LRFJlk0itFwLV8)

Мы видим, что поле **0x10** является **DWORD** где оно передаётся функции **SCANF**, поэтому мы переходим в **MYSTRUCT** и в поле **0x10** мы нажимаем **D** до тех пор пока оно не будет иметь тип **DD**.

![](/files/-Ljr6D3UBt76qTdOdHpq)

![](/files/-Ljr6D3W6fSj5pdYPfTR)

Я переименую это поле в **NUMERO**.

![](/files/-Ljtu9MLSpF3fSNKB-IA)

Другая запись - это поле по смещению **0x14**, которое используется в цикле для удаления символа **0xA**. Я назову его **C**.

![](/files/-Ljr6D3bza5jlfQJfID3)

Давайте пойдём в поле по смещению **0x14** структуры **MYSTRUCT** и будем нажимать **D** до тех пор пока не появится **DWORD** и давайте назовём это поле именем **C**.

![](/files/-Ljtu9N--hUnzW9gcxRC)

![](/files/-Ljr6D3fTv54Mn8ZCjej)

Здесь мы нажимаем **T**, чтобы освежить информацию.

Наконец, я переименовываю функцию в **ENTER**.

![](/files/-Ljtu9NZks0Bgej1zIQk)

Мы видим, что три первые функции вызывают **ENTER** передавая адрес **PEPE** и три следующие передают адрес **JUAN**.

Давайте посмотрим на следующую функцию.

![](/files/-Ljtu9NpMAPm8ic4zHVM)

Эта функция также использует обе структуры, поэтому Вы можете равно как в предыдущем случае нажать **F5**.

![](/files/-Ljr6D3mH_Y37YPZNHIS)

Здесь на переменной **\_STRUCT** я делаю правый щелчок и выбираю пункт **CONVERT TO STRUCT \***.

![](/files/-Ljtu9OMQURT8aJkm024)

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

![](/files/-Ljr6D3qmSPXarnyaeMD)

Здесь мы видим, что программа сравнивает поле **NUMERO**, которое мы передали, с числом **0x10** и поскольку сравнение знаковое, любое отрицательное число может пройти. Например число **0xFFFFFFFF**, которое равно **-1** и которое меньше **0x10**.

![](/files/-Ljtu9Ot08l_lnuBAWeu)

Затем, программа использует **NUMERO** как размер для функции **GETS**\_**S**, которое мы передали ей и другой аргумент. Он должен быть буфером, который находится в начале структуры, потому что он использует её начальный адрес.

Я иду в **MYSTRUCT** и по смещению **0x0** нажимаю **D** один раз, чтобы создать единственное байтовое поле.

![](/files/-Ljtu9PABlEJRb_T8YLJ)

Теперь, здесь, я делаю правый щелчок и выбираю **ARRAY**.

![](/files/-Ljtu9PTaReUV1yzWCyD)

Размер буфера будет равен **16** байт. Я соглашаюсь с этим значением.

Переименовываю это поле в **BUFFER**.

![](/files/-Ljtu9PhSL7toVggfphK)

Размер поля теперь равен **16** десятичных байт.

Давайте продолжим реверсить.

![](/files/-Ljtu9Q-rZH2HZ1CTRkC)

Проблема состоит в том, что с помощью функции **GETS**\_**S** буфер может быть переполнен, так как функция **CHECK** позволяет передавать отрицательные значении при использовании в качестве размера. Они будут восприниматься как беззнаковые значения и будут очень большими.

Если, например, мы передадим **0xFFFFFFFF** в сравнение, значение будет равно **-1**, потому что оно идет как знаковое и будет меньше чем **0x10**, но используя его как размер, оно будет большим положительным значением **0xFFFFFFFF**, которое позволяет нам передавать количество символом, которое мы хотим через функцию **GET**\_**S** в буфер и переполнить его.

Таким образом, мы могли бы переименовать функцию в **CHECK** или **GET**. Независимо от того, что мы хотим, нужно чтобы имя отражало то, что делает функция. Мы будем проверять это соответствие.

![](/files/-Ljr6D46ZLdY-h5LNbw3)

У меня есть третья функция. Аргумент тот же, поэтому я повторяю процедуру, нажимаю **F5** и изменяю тип аргумента.

![](/files/-Ljtu9QoFl2FQeg0SFRk)

Я продолжаю реверсить.

![](/files/-Ljr6D4ENI9jDNTJsI1b)

Мы видим, что есть ещё одно поле, поскольку оно пытается сравнить значение \[**EAX**+**0x18**], которое мы не определили, так как наше последнее поле структуры **MYSTRUCT** равно **0x14**. Давайте добавим его.

![](/files/-Ljtu9RLz_F-eYOaarX4)

Мы помещаем указатель на слове **ENDS** и нажимаем **D** до тех пор пока не создадим новое поле **DD**.

![](/files/-Ljtu9Rht_3uZRuD9_TX)

Я переименовываю это поле в **COOKIE**.

![](/files/-Ljr6D4OyoEVg4HxhbgB)

Я возвращаюсь в нашу функцию и нажимаю **T** для обновления информации.

![](/files/-Ljr6D4QoiH8z5rDnkOm)

Мы видим, что существует ещё другое поле. Это единственный байт.

![](/files/-Ljr6D4TD5Ls0nyTADK7)

Поэтому мы возвращаемся назад в структуру **MYSTRUCT** и на слове **ENDS** нажимаем **D** один раз и у нас появляется поле из одного байта.

![](/files/-Ljr6D4VY1HzO-wigWnY)

Я переименовываю это поле во **FLAG**, чтобы он совпадал с исходным кодом.

Теперь возвращаемся в функцию.

![](/files/-Ljtu9TROeGRDsvoAqIB)

Если переменная **COOKIE** равна **0x99989796**, тогда программа будет устанавливать флаг структуры в **1**.

![](/files/-Ljr6D4ZQAqjJn6zt4Q9)

Если для некоторых изменений, которые не распространялись хорошо, переменные функции **MAIN** сломаются, и мы не сделали снимок, как это случилось со мной, то делаем так.

![](/files/-Ljr6D4aBE9l1ejrRZaO)

Я иду в начало поломанной функции и нажимаю **U**.

![](/files/-Ljr6D4cIy9j4Sw5Oxd9)

Я соглашаюсь на вопрос программы "сломать" функцию.

![](/files/-Ljtu9UwTGSf-XU2rYLR)

Затем, там же, в начале функции, я нажимаю **C**.

![](/files/-Ljtu9VB2-zikDH9GlcL)

И затем я делаю правый щелчок и выбираю пункт **CREATE FUNCTION**.

![](/files/-Ljr6D4iGLUik2MsHWzW)

Теперь всё хорошо.

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

![](/files/-Ljr6D4k9ZBzMuqaJVxu)

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

Только имена структур неправильные, но я буду менять их в соответствии с исходным коде, т. е. я изменяю имена на **PEPE** и **JUAN**.

![](/files/-Ljtu9WXg4Y3qz3M4dOX)

![](/files/-Ljr6D4ox1iXV5TLqGV9)

Мы видим, что со структурой **JUAN** программа будет делать то же самое что и с **PEPE**. Структуры будут передавать свои адреса функциям **ENTER** и **CHECK**. Но программа имеет ещё третью функцию. Давайте посмотрим, что она делает.

![](/files/-Ljtu9XJljxIGsvnBtf-)

Нажимая **T** в полях, мы видим, что функция похожа на функцию "**DESICION**", только константа с которой сравнивается поле **COOKIE** структуры **JUAN** отличается. В этом случае константа равна **0x33343536**.

Другими словами, поле **COOKIE** структуры **PEPE** должно быть равно значению **0x99989796** и поле структуры **JUAN** должно быть равно **0x33343536**. Только так флаги каждой структуры будут равны 1.

![](/files/-Ljtu9XdCiUI_W0Fpeww)

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

Это упражнение имеет много решений, потому что структура **JUAN** находится выше в стеке.

![](/files/-Ljr6D4uFVf25lQ4QlJU)

Выполняя функции **GET\_S** мы можем перезаписать все флаги так, чтобы они остались равны **1** и таки образом, сделать это одним переполнением. Также это можно сделать индивидуально, перезаписывая в каждой функции **GET\_S** флаг и нет необходимости перезаписывать поле **COOKIE** правильным значением, потому что мы напрямую перезаписываем флаг, не дожидаясь сравнения поля **COOKIE**, чтобы изменить его.

Чтобы перезаписать флаг, у меня есть **16** десятичных байт, плюс к этому значению **3 DWORD** или это будет **12** байт.

Общая сумма байт будет равна: **16** + **12** = **28** байт.

![](/files/-Ljr6D4wUsHmmh1OaxVV)

Поэтому количество фруктов будет равно: **FRUTA** = **28\*** "**A**" **+** "**\x01**"

![](/files/-Ljtu9YU3EBBhacjiDdE)

Конечно, для каждой из структур Вы должны передать **-1** или отрицательное значение, которое пройдет проверку против числа **0x10**, а затем должен следовать фруктовый-шеллкод )).

![](/files/-Ljtu9YjO9eyZeTD4G9c)

Если мы немного поотлаживаем, мы увидим значение **-1**, которое помещается в поле **NUMERO** структуры **PEPE**.

![](/files/-Ljtu9Yy3SQRi8DDwHLO)

![](/files/-Ljr6D5NYzq1wbehf8fR)

Программа проходит проверку и достигает функцию **GETS\_S**. Здесь и читаются переданные фрукты.

![](/files/-Ljtu9ZSJoGGOnB4smmW)

Здесь я вижу адрес структуры **PEPE** на моей машине, после исполнения функции **GET\_S**. Если я пойду в стек.

![](/files/-Ljtu9ZeCJwwnFHIWMS5)

Здесь я вижу наши **ФРУКТЫ**, которые я посылаю программе. Если я хочу сконвертировать эти данные в структуру, то я нажимаю **ALT + Q**.

![](/files/-Ljr6D5TsnITYg1RE489)

И выбираю пункт **MYSTRUCT**.

![](/files/-Ljr6D5YtH2AG5wpY0B2)

Я вижу, что поля и флаг уже перезаписан на значение 1 переполнением. Не исследуя подробно другие функции, я продолжаю трассировать.

![](/files/-Ljr6D5_8l5DoTSW0r4j)

Мы видим, что программа сравнивает **COOKIE** со значением **0x99989796** и поскольку это разные значения, программа не изменят флаг. Но флаг уже равен **1**, поэтому это не так важно.

Тот же процесс будет повторяться и мы переходим в функцию **DECISION2**.

![](/files/-Ljtu9_lQEVlvDqIvDlZ)

Я начинаю трассировать с помощью клавиши **F7**.

![](/files/-Ljtu9_zCL8wGpZvF3AL)

Регистр **EAX** имеет начало структуры **JUAN**. Давайте посмотрим на эту структуру в стеке.

![](/files/-Ljr6D5i7SwAStWsAkxd)

Здесь, я также нажимаю сочетание **ALT + Q**, чтобы из байтов собрать структуру. Снова выбираем нашу структуру **MYSTRUCT**.

![](/files/-Ljtu9ajD-4uB0287URZ)

Так же как и раньше флаг будет равен **1**. Сравнение с **COOKIE** не будет равно, но программа продолжит выполняться, потому что с флагом уже всё хорошо.

![](/files/-Ljtu9b5LK0KEjGbGYYV)

Поле **PEPE.FLAG** равно **1** и это хорошо, поэтому продолжаем трассировку.

![](/files/-Ljtu9bceMTKnoCiy8SK)

Поле **JUAN.FLAG** также правильное, поэтому я достигаю хорошего сообщения.

![](/files/-Ljr6D5q4CINr8FDFYn6)

Готово. Теперь пример решен. Мы победили. Увидимся в **28**-й главе.

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

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

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

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

05.01.2018

Источник:

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