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

# Часть 61

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

Мы рассмотрим другой случай в уязвимом драйвере. Теперь поговорим о **INTEGER OVERFLOW**.

Многие спрашивают меня, почему бы не проанализировать его непосредственно в **C** или **C++**. Проблема состоит в том, что некоторые из них уже сделаны на **C** и **C++**. Методы те же самые. Поэтому перенос их на **PYTHON** не только приносит что-то новое, но и заставляет нас практиковаться в **PYTHON** и библиотеке **CTYPES**, что является для нас важным.

Для тех, кто хочет увидеть методы, исходный код доступен здесь:

<https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/tree/master/Exploit>

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

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

![](/files/-LjtdyvBXd8tDtSwYZjR)

Здесь у нас есть блок, который перемещает нас в **IOCTL**, где вызывается **INTEGER OVERFLOW**.

Давайте посмотрим, что **IOCTL** действительно прибывает сюда, в самое начало.

![](/files/-Ljtu6nIv8gtwV2rKL9i)

Регистр **EAX** у нас равен **0x0022201F**.

![](/files/-Ljtu6ncptyl67DB4hQo)

И чтобы идти по правильному пути, я изменяю регистр **EDX**, содержащий наш **IOCTL** код. Он должен быть больше, чем регистр **EAX**.

![](/files/-LjtdyvroLbgsn6wIatL)

Затем программа передает наше значение в регистр **EAX** и вычитает значение **0x00222023**, и если оно не равен нулю, программа вычитает еще **4**, что остаётся в регистре **ECX**. Затем идут инструкции **PUSH 4** - **POP ECX**. Если результат равен нулю, программа перейдёт к правильному блоку.

```
IOCTL – 0x222023 – 0x4 = 0

IOCTL = 0x222023 + 0x4

Python>hex(0x222023 + 4)
0x222027
```

Этот **IOCTL** будет тем, который перенесет нас в блок, где срабатывает **INTEGER OVERFLOW**. Давайте проанализируем его.

![](/files/-Ljtu6o0_SiZgITZZGxJ)

Мы видим, что, как и в предыдущем случае, программа передает в функцию два аргумента: первый это адрес структуры **IRP**, а второй - **\_IO\_STACK\_LOCATION**.

Поскольку мы уже импортировали структуру \_**IO**\_**STACK**\_**LOCATION**, здесь программа перемещает её начальный адрес и начинается работа с ее смещениями. Поле **0x10**. Давайте посмотрим, что это. Я нажимаю **T** и выбирает соответствующую структуру.

![](/files/-Ljtu6oDIDhRVIq1WcAd)

Мы видим, что

![](/files/-Ljtu6oP8wEnESiH_s_w)

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

![](/files/-Ljtu6obLFUYO6Kz5PM5)

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

![](/files/-Ljtu6ooEiG-iEe-4IME)

Здесь мы видим эти два аргумента. Программа также устанавливает переменную **STATUS** в нуль. А в стеке есть буфер с именем **KERNELBUFFER**. Давайте посмотрим его длину.

![](/files/-LjtdywXWNB2T5mYswkH)

Длина равна **512** \* **4**, так как каждый компонент является **DWORD** (**DD**), поэтому общая длина будет равна.

```
Python>hex(512 * 4)
0x800
```

Программа инициализируйте в нуль этот буфер, сначала записав здесь первые **4** байта регистра **EDI**, который равен нулю. А затем вызывает функцию **MEMSET** из оставшихся **0x7FC** байтов, добавив **4**к месту назначения в инструкции **LEA**, чтобы программа записывала данные начиная на **4** байтов больше.

![](/files/-LjtdywbPcrNl28jxnM5)

![](/files/-Ljtu6pOr84YzXTp5pux)

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

![](/files/-Ljtu6p_8nl6fjuL8hWW)

Посмотрим, что она делает. Здесь сказано следующее.

![](/files/-Ljtu6pmF_fcZGHB27Nk)

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

![](/files/-Ljtu6q-7MO7lSJjKTov)

Затем печатается **4** значения: размер, который мы передали из пользовательского буфера, указатель на пользовательский буфер, адрес **KERNELBUFFER** и его размер.

![](/files/-Ljtu6qCS60a-7N42qKm)

Мы видим, что **IDA** говорит нам, что есть конструкция **TRY - EXCEPT** или другими словами, в этом блоке есть исключение. Программа переходит на нижний блок. Поэтому из верхнего блока есть три стрелки. Две нормальные для сравнения и другая для **TRY – EXCEPT**.

![](/files/-LjtdyxAT5hWMyO9v8tF)

Мы видим, что блок принимает размер, который передается в регистре **EBX** и сравнивает его с константой **0x800**, которая находится в регистре **ESI**.

![](/files/-LjtdyxHRzDlzXK10435)

Но прежде, в мой размер добавляет значение **4**. И если он будет ниже, все будет нормально.

![](/files/-LjtdyxPpJny4CZLcwgO)

Мы уже видим проблему. Eсли мы передадим в качестве размера, например число **0XFFFFFFFF**, при добавлении **4** произойдет **INTEGER OVERFLOW**, и результат будет таким.

```
Python>hex((0xffffffff + 4))
0x100000003L
```

Если мы сократим его до **32** бит как делает процессор, то получим

```
Python>hex((0xffffffff + 4) & 0xffffffff)
0x3L
```

Это дает нам в результате **3**. И это меньше, чем **0x800**, даже при сравнении без знака.

Затем программа берет исходный размер и делает с ним **SHR** или другими словами делит его на **4** с учетом знака. Это делается потому, что копируется **DWORDS**, а индекс идет один за другим. Поэтому размер - это общее число, разделенное на **4**.

![](/files/-LjtdyxXA9Q9ItixScWP)

![](/files/-Ljtu6rFG1HcvgrGKfBN)

Если бы наш размер был равен **0XFFFFFFFF**, то разделив его на **4**, это дало бы нам значение **0x3FFFFFFF**.

Мы видим, что это цикл, где регистр **EDI** является счетчиком.

![](/files/-LjtdyxjkHwgglevvScz)

Условие выхода заключается в том, чтобы регистр EDI не был меньше, другими словами, чтоб он был больше или равен значению выхода. Если цикл начинается с нуля и увеличивается на один каждый раз, это будет давать много возвратов цикла, пока значение не достигнет **0x3FFFFFFF**.

![](/files/-Ljtu6rd61TYhoSy2z1A)

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

![](/files/-LjtdyxznQBxt-F2fYV_)

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

![](/files/-LjtdyyE1gzYZJjUqJMc)

Наконец, она скопируйте в буфер ядра. Программа развернет выполнение с помощью регистра **EDI**, который является счетчиком умноженным на **4**, то есть она скопирует **4** \* **4** байтов.

Затем она складывает значение **4** с адресом буфера пользовательского режима, увеличивает регистр **EDI**, сохраняет его в переменную **COUNT** и готово. Это всё, что нужно. Поэтому мы можем спровоцировать переполнение стека, контролируя его большим размером. Мы даже можем выйти из него до того, как программа разрушит весь стек, так как это дает нам возможность выйти из цикла, обработанного нами.

С этими данными мы можем перезаписывать адрес возврата без проблем.

![](/files/-Ljtu6sE4dcs3LlKE3Oy)

Перед тем, как сделать это в **PYTHON**, я запускаю исполняемый файл эксплойта, чтобы проверить, что он reversee, и при анализе мы используем **IOCTL** код равный **0x222027**.

Затем программа достигает блока.

![](/files/-LjtdyyU9tdVcXkC6wXI)

![](/files/-Ljtu6shneWG4i35qeOK)

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

Здесь мы видим его содержимое.

![](/files/-Ljtu6sus86SwsOOzgYy)

Если я создам сегмент, я уже могу сгруппировать буквы **A** вместе, нажав **A**.

![](/files/-Ljtu6t98IlkbKVRvTZV)

И я вижу наш **DWORD** где-то здесь.

![](/files/-LjtdyyzRf5gUCLpZHOA)

Я добираюсь до функции, которая вызывает **INTEGER OVERFLOW**.

![](/files/-Ljtu6taKh5kHpFYxXiK)

Мы видим, что размер который мы передаём равен **0xFFFFFFFF**.

Регистр **EAX** = **3,** что меньше, чем **0x800**.

![](/files/-Ljtu6toiAZoBEswheTB)

После инструкции **SHR**.

![](/files/-LjtdyzN4hbAdZg665fE)

Программа читает содержимое буфера пользовательского режима и оно равно **0x41414141**.

![](/files/-LjtdyzWcigcNLG1cZaC)

Поскольку это не выходная константа равная **0x0BAD0B0B0**, программа копирует ее в буфер ядра стека.

Я устанавливаю **BP** здесь для того, чтобы программа остановилась, когда закончиться копирование.

![](/files/-Ljtu6uPDn5Au_l_TxVF)

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

![](/files/-Ljtu6uckP0axx5wKxQo)

Как только я создаю сегмент, я создаю код клавишей **C** и создаю функцию с помощью **CREATE FUNCTION**, и теперь это смотрится лучше.

![](/files/-LjtdyzpfV0GQGX3_fg_)

Здесь мы видим калькулятор с правами **SYSTEM**.

![](/files/-Ljtu6v4w8pW1-UruPII)

Теперь идея сделать то же самое, но только в **PYTHON**.

![](/files/-Ljtu6vHt6LA02Z6dnNU)

Я вижу, что буфер состоит из **2088** десятичных байт, когда элементом является байтом. Если это **DWORD**, то он должен быть умножен на 4

```
Python>hex(2088)
0x828
```

Другими словами, мой буфер будет равен **0x828** + адрес для перезаписи адреса возврата.

Я вижу, что адаптация скрипта переполнения стека работает.

![](/files/-Ljtu6vZ9d2FZw5Z5DEQ)

Я меняю **IOCTL**; Я установил размер пользовательского буфера равным **-1**. Я передаю указатель на пользовательский буфер чтобы перезаписать адрес возврата. В эксплойте на **C** я создал два буфера: один для перезаписи адреса возврата и другой с шеллкодом, в который я поместил всё в одном.

```python
Data = shellcode + ((0x828 -len(shellcode)) * "A") + struct.pack(">L", int(buf)) + struct.pack(">L", 0x0BAD0B0B0)
```

Это шеллкод. Затем он заполняется **0x828** байтами минус длина шелл-кода умноженного на "**A**". Затем идет указатель на тот же буфер, который используется для перезаписи по адресу возврата, и выходной **DWORD** равный **0x0BAD0B0B0**.

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

![](/files/-Ljtdz-Yu1uuNF2ntDDf)

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

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

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

21.12.2018

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