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

# Часть 34

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

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

**ЧТО ТАКОЕ DEP** ?

![](/files/-LjrE0svsOHZP7ewvyXT)

Предотвращение выполнения данных (**DEP**) представляет собой набор аппаратных и программных технологий, который выполняет дополнительные проверки в памяти, чтобы помочь избежать запуска в системе вредоносного кода. В **SERVICE PACK 2** (**SP2**) **MICROSOFT WINDOWS XP** и в **MICROSOFT WINDOWS XP TABLET PC EDITION 2005**, как аппаратное, так программное обеспечение применяют **DEP**.

Основное преимущество **DEP** заключается в том, чтобы помочь избежать выполнения кода из страниц данных. Обычно, код по умолчанию не исполняется в куче или стеке. Аппаратное обеспечение **DEP** обнаруживает код, который запускается из этих областей и вызывает исключение, когда он пытается исполниться. Программное обеспечение **DEP** может помочь предотвратить то, что вредоносный код использует преимущество механизма обработки исключений **WINDOWS**.

Давайте оставим определение **DEP** компании **MICROSOFT'у**. На самом деле, существует несколько способов задействовать **DEP**. Один из таких способов, сделать это через Свойства Системы.

![](/files/-LjrE0t4UTQpBKb89fnC)

Сейчас я нахожусь в **WINDOWS 10** и **DEP** включен для основных программ и сервисов. Это настройки по умолчанию. Это означает, что существуют программы, у которых **DEP** выключен по умолчанию.

Конечно, Вы можете изменить настройки на другую опцию, чтобы все программы имели включенный **DEP**, что, очевидно, помогает немного больше, чтобы избежать исполнение кода.

Помимо этой конфигурации системы, каждая программа можем активировать функцию **DEP** самостоятельно, используя **API**, который **MICROSOFT** предоставляет для этого.

![](/files/-Ljtu1G0hhHkL3YIfuLd)

![](/files/-LjrE0tDj9BWEWFkSzRV)

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

Поскольку **DEP** управляется процессом, то у него есть несколько способов запуститься и это можно сделать прям во время выполнения. Мы можем увидеть список процессов с помощью утилиты **PROCESS EXPLORER**, у которой есть столбец, показывающий статус **DEP** для каждого процесса.

<https://technet.microsoft.com/en-us/sysinternals/processexplorer.aspx>

![](/files/-LjrE0tG1-5scl4nfQsh)

![](/files/-LjrE0tI_VKMBMK_f0jU)

Вот нужная нам настройка. Мы должны запустить утилиту под пользователем Администратор. Мы добавляем столбец, сделав правый щелчок в панели и выбрав пункт **SELECT COLUMNS**.

![](/files/-Ljtu1Gtr6NsEeGyu_ua)

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

![](/files/-Ljtu1H5HrIahrUuZSYi)

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

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

Один из основных способов обхода **DEP** является **ROP** или возвратно-ориентированное программирование.

![](/files/-LjrE0tPon30wPEh3lpQ)

> Четверо исследователя из Калифорнийского Университета опубликовали статью под названием "**RETURN**-**ORIENTED PROGRAMMING**: **SYSTEMS**, **LANGUAGES AND APPLICATIONS**" где они показали способ обхода этой защиты. Грубо говоря, он заключается в выполнении фрагментов кода, которые уже существуют в самом коде программы. Таким образом нет необходимости инжектировать свой собственный код. Я постараюсь кратко описать технику и сделать пример приложения. Хотя я советую Вам прочитать оригинальную статью, где это все хорошо объясняется.
>
> Необходимо получить маленькие фрагменты кода, оканчивающиеся инструкцией **RET**. В идеале, с одной ассемблерной инструкцией (в дополнении к **RET**), внутри программы, которую мы хотим использовать. Эти маленькие фрагменты кода называются гаджетами. С помощью этих фрагментов мы должны как с конструктором **LEGO**, создать код эксплоита или шеллкод. Как только эти фрагменты получены и правильно упорядочены, мы можем запустить их в установленном порядке. Как мы это сделаем? То, что эти гаджеты заканчиваются инструкцией **RET** не являются случайными. Этот метод заключается во вводе адресов гаджетов в стек в правильном порядком исполнения, так чтобы при выходе из исполняющейся функции адрес возврата возвращался в регистр **%EIP**, где эти гаджеты вызывались бы один за другим в правильном порядке.

Основной смысл заключается в том, что, когда **DEP** не включен, и например, мы перезаписываем адрес возврата при переполнении стека, то обычно мы переходим на инструкцию **JMP ESP** или **CALL ESP**, которая возвращает выполнение на стек и продолжает выполнять мой код, который расположен ниже инструкции **JMP ESP**.

Но основная идея **ROP** заключается в том, что вместо перехода на инструкцию **JMP ESP**, происходит переход на кусочки кода, называемые гаджетами, которые являются исполняемым кодом программы, которые завершаются инструкцией **RET** (гаджеты являются частью некоторого модуля, вот почему мы можем их запускать) и с помощью которых, вы можете по чуть-чуть делать вызовы некоторых **API**, например такие как **VIRTUALPROTECT** или **VIRTUALALLOC**, которые изменяют и дают разрешение на выполнения кода в стеке или куче, т.е там, где находится мой код и наконец происходит переходит на его выполнение.

Другими словами, если эксплоит, который перезаписывал адрес возврата для примера без **DEP** был:

"**A" \* 200 + АДРЕС\_JMP\_ESP + КОД ДЛЯ ВЫПОЛНЕНИЯ**

То, теперь с **DEP**, в том же случае, код должен быть таким:

"**A" \* 200 + ROP + КОД ДЛЯ ВЫПОЛНЕНИЯ**

Где **ROP** должен предоставить моему коду разрешение на выполнение.

Рассмотрим, пару примеров без защиты **DEP**.

![](/files/-LjrE0tTsMaAVdVRJBgH)

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

Также, программа загружает модуль под названием **MYPEPE.DLL**. Позже увидим, нужен ли программе этот модуль или нет.

Давайте откроем программу в **ЗАГРУЗЧИКЕ IDA**.

![](/files/-Ljtu1HtHU6-yMLx7DhG)

Мы видим, что программа имеет только два аргумента в функции **MAIN** - **ARGC** и **ARGV**. Мы знаем, что переменная **ARGC** - это количество аргументов, которые мы вводим через консоль. Поэтому, если их не два (имя исполняемого файла + второй аргумент после него через пробел) программа будет закрываться, так как условие не выполняется.

![](/files/-Ljtu1I1UeynYIzyk9UJ)

Если количество аргументов не равно **2**, программа будет переходить в красный блок, и выводить сообщение об ошибке, и придёт к возврату без каких либо действий. А если количество аргументов корректно, т.е. равно **2**, программа будет переходить в желтый блок, загрузит **DLL** и затем перейдёт в функцию **SALUDA**. Сейчас имена выглядят уродливо. Я иду в меню **OPTIONS→DEMANGLENAMES→NAMES**.

![](/files/-Ljtu1ILEcZxGHPKIwWL)

![](/files/-LjrE0tqnT2WsoYLcIjG)

Если кто-то не помнит, переменная **ARGC** - это число аргументов, а переменная **ARGV** - это массив указателей. Каждый элемент указывает на строку, которая является аргументом. Другими словами, в случае:

![](/files/-Ljtu1Ig_eEEu7ZtvAhZ)

По адресу **0x0040109C** регистр **ECX** имеет значение переменной **ARGV**. Т.е. это массив указателей.

**ARGV** = \[указатель\_на\_имя\_исполняемого\_файла, указатель\_на\_аргумент1, указатель\_на\_аргумент2…]

В **IDA** мы видим, что программа исполняет инструкцию **SHL EAX**, **0**, другими словами сдвигает **0** байтов, оставляя регистр **EAX** равным, как и раньше, т.е. **4** в этом случае.

Затем выражение \[**ECX**+**EAX**] будет возвращать указатель на аргумент. Если регистр **EAX** равен нулю, программа будет возвращать указатель на имя исполняемого файла. Если он будет равен **4**, то поскольку каждый указатель имеет длину **4** байт, то программа будет читать указатель на следующий аргумент и так далее.

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

![](/files/-Ljtu1J2SSaz3mx-W3Yo)

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

![](/files/-LjrE0u-F3YtuI5Q0xtj)

Поскольку я скомпилировал программу с символами, **IDA** определяет текст как указатель, и что он указывает на строку (или массив символов).

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

Давайте посмотрим буфер.

![](/files/-Ljtu1JMTY4UtatCJOPn)

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

![](/files/-Ljtu1Jb3LnZ8ceNzAE4)

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

![](/files/-Ljtu1Jo6a_wj1vyFi_-)

Поэтому давайте сделаем правый щелчок и выберем пункт **ARRAY**.

![](/files/-LjrE0uBK61KhdPm6gvm)

Здесь нет никаких сомнений. Ниже нет никаких переменных. Что есть ниже, так это переменные **СОХРАНЕННЫЙ EBP** и **АДРЕС ВОЗВАРАТА**. Таким образом, нет никаких сомнений, что буфер рассчитан **IDA** хорошо. (Кроме того, программа имеет символы, которые помогают **IDA** определить, что это буфер, даже без нашей помощи)

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

Я выбираю область, которую я собираюсь заполнить начиная с буфера, оставляя **АДРЕС ВОЗВРАТА**, делаю правый щелчок и выбираю пункт - **ARRAY** без соглашения, просто, чтобы увидеть размер, который должна иметь срока для переполнения.

![](/files/-LjrE0uF1YEYDnV0HYTI)

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

![](/files/-LjrE0uINE3CaotCXamF)

Предположительно, строка **0xCCCCCCCC** просто должна перезаписать адрес возврата. Я мог бы попробовать эту строку. Для этого я устанавливаю **IDA** как **JUST IN TIME DEBUGGER**. Я открываю консоль с правами администратора и иду в папку, где находится исполняемый файл **IDA**.

**-I#** установит **IDA** как отладчик времени исполнения (**0** - для выключения и **1** - для включения)

![](/files/-Ljtu1KaZ-BmgQsmKjjs)

![](/files/-LjrE0uOI6pdEaULXNYd)

При запуске скрипта, я вижу, что он переходит к выполнению кода, по адресу **0xCCCCCCCC**, который я помещаю в него же. Этим я перезаписываю адрес возврата.

![](/files/-Ljtu1Kq0FltS7FE1_9b)

Здесь, я вижу, что сейчас регистр **ESP** указывает чуть ниже значения **0xCCCCCCCC**. Поэтому, если я добавлю больше кода ниже, и вместо перехода на адрес **0xCCCCCCCC**, программа будет переходить на инструкцию **JMP ESP**, чтобы начать выполнять указанный код (Какие же хорошие были времена, когда не было **DEP**)

Я ищу в списке модулей библиотеку **MYPEPE.DLL**.

![](/files/-LjrE0uTTiERJqZsJtMz)

Мы сделаем щелчок правой кнопкой, чтобы проанализировать нашу библиотеку и загружаем символы для неё. Тем временем, в любом месте кода мы запускаем плагин **KEYPATCHER** и не принимая соглашение, мы видим, что инструкции **JMP ESP** соответствует последовательности байтов **FF E4**.

![](/files/-LjrE0uWhcS-ARyPCD9c)

Когда анализ закончится, мы увидим, что в списке функций появляется функция **MYPEPE**. Я иду к одной из них.

![](/files/-LjrE0u_gAWchK94rW1t)

Модуль выглядит хорошо. Давайте посмотрим, есть ли здесь инструкция **JMP ESP?**

Выбираем пункт **SEARCH FOR→ SEQUENCE OF BYTES** и вводим значение **FF E4**.

![](/files/-Ljtu1LP88Kf5fzp1wxL)

Здесь, по адресу **0x004010BA** есть инструкция **JMP ESP**. Мы не можем использовать нуль, но поскольку система добавляет нуль в конце, когда программа исполняет функцию **STRCPY**, мы не будем добавлять нуль.

![](/files/-Ljtu1LWqIYa6CgbYVec)

Проблема заключается в том, что инструкция **JMP ESP** служит только для перехода если мы добавим ещё код ниже. Но мы не можем добавить больше кода из-за того, что адрес инструкции **JMP ESP** заканчивается нулем. Так что мы будем переходить на инструкцию **RET**. Чуть ниже это указатель на нашу строку, которая была передана нами как аргумент.

![](/files/-LjrE0uj5Bqx9jpkf3DF)

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

![](/files/-Ljtu1LkAGOLEXWn8dfj)

Скажем так - "Это хороший **RET**". Давайте добавим его.

![](/files/-LjrE0upiIf2cNIyxE1Z)

Давайте теперь попробуем с ним.

![](/files/-Ljtu1LupezWu7NzB5X4)

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

Я подготовил шеллкод, который запустит калькулятор:

**import** struct shellcode = **"\xB8\x40\x50\x03\x78\xC7\x40\x04"** + **"calc"** + **"\x83\xC0\x04\x50\x68\x24\x98\x01\x78\x59\xFF\xD1"**

fruta = shellcode + **"A"** \* (36-len(shellcode)) + **"\x3a\x10\x40"**

\#0x40103a ret

![](/files/-LjrE0uvPH7Dl2T7lcNN)

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

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

До встрече в 35 части.

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

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

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

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

12.03.2018

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