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

# Часть 52

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

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

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

Чтобы получать информацию из пользовательского режима, мы должны научить наш драйвер отвечать на входные и выходные управляющие коды устройства (**IOCTL**), которые могут быть доставляться из пользовательского режима используя **API DEVICEIOCONTROL**. Мы уже видели, как наш драйвер может изменить процедуру загрузки, используя структуру **DRIVER\_OBJECT** и изменять указатель, который там храниться. Обработка **IOCTL** очень похожа. Нам просто нужно подготовить еще несколько процедур.

![](/files/-Ljtu2ZCKfKqkx1Ha9DN)

Первое, что мы должны сделать в нашей точке входа - создать **DEVICE OBJECT**.

Я не буду объяснять всю теорию об этом. Кто хочет узнать больше, почитайте это:

<https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/introduction-to-device-objects>

![](/files/-LjrT85hkuezwtt2sFyR)

И это должно быть так. В нашем первом драйвере, мы могли только запускать и останавливать его и не могли получать управляющие команды из пользовательского режима. Поэтому теперь мы должны создать **DEVICE OBJECT**, используя **API IOCREATEDEVICE**.

Функция, которую вызывает наша **DRIVERENTRY**, аналогична

`status = IoCreateDevice(DriverObject,0,&deviceNameUnicodeString,FILE_DEVICE_HELLOWORLD, 0,TRUE,&interfaceDevice);`

![](/files/-Ljtu2Zq7lUWtxqJuEyE)

**Parameters**

> **DriverObject \[in]**
>
> Pointer to the driver object for the caller. Each driver receives a pointer to its driver object in a parameter to its [DriverEntry](https://msdn.microsoft.com/es-es/library/windows/hardware/ff544113) routine.

`NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)`

Как мы увидели, **DRIVERENTRY** получает два аргумента. Первый - указатель на структуру **DRIVER OBJECT**, которая передается в качестве первого аргумента функции **IOCREATEDEVICE**.

> **DeviceName \[in, optional]**
>
> Optionally points to a buffer containing a null-terminated Unicode string that names the device object.
>
> WCHAR deviceNameBuffer\[] = L"\Device\HelloWorld";
>
> UNICODE\_STRING deviceNameUnicodeString;

В нашем коде **DEVICENAME** соответствует имени устройства, а затем копируется в переменную **DEVICENAMEUNICODESTRING**, который передается как аргумент **API**.

> **DeviceType \[in]**
>
> Specifies one of the system-defined FILE\_DEVICE\_XXX constants that indicate the type of device (such as FILE\_DEVICE\_DISK or FILE\_DEVICE\_KEYBOARD) or a vendor-defined value for a new type of device.

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

`#define FILE\_DEVICE\_HELLOWORLD 0x00008337`

> **DeviceObject \[out]**
>
> Pointer to a variable that receives a pointer to the newly created [DEVICE\_OBJECT](https://msdn.microsoft.com/es-es/library/windows/hardware/ff543147) structure. TheDEVICE\_OBJECT structure is allocated from nonpaged pool.

Это указатель на **DWORD**, где **API** будет содержать указатель. Поэтому система говорит, что **OUT** – используется для выходного параметра.

Это самые важные функции. Давайте теперь посмотрим на код в **IDA,** теперь, когда мы знаем эти **API**.

Мы видим, что функция, которая вызывает наш **DRIVERENTRY**, аналогична

![](/files/-LjrT85qcOn47x08wWpF)

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

![](/files/-Ljtu2_QS-nuLACTNgmF)

Функция начинается с тех же двух указателей на структуры типа **\_DRIVER\_OBJECT** и **\_UNICODE\_STRING**.

Остальные - это переменные. Поскольку у нас есть символы, это не очень сложный случай. Но хорошо понемногу привыкать к реальным случаям, когда у нас нет символов.

В переменную **VAR\_4** сохраняется **COOKIE** для защиты стека.

![](/files/-LjrT865DR51tWaKI2cB)

![](/files/-Ljtu2aLfGaLefuKkY1o)

Здесь программа копирует имя устройства **UNICODE** размером **9 DWORD** (**0x24** байта) в назначение, которым является переменная **DEVICENAMEBUFFER**, длина которой составляет **19 WORDS**, т.е. **19** \* **2**, всего **38** байт в десятичном формате или **0x26** байт в шестнадцатеричном, поэтому всё, что копируется, намного меньше, чем буфер.

![](/files/-LjrT86KjKgkYMTeWb_S)

```
Python>hex(0x19 * 2)
0x32
```

![](/files/-MX3mJerpiIHHTcINdy9)

Затем копируется **DOS\_DEVICE\_NAME** размером **0xB DWORDS,** т.е. **0xB** \* **4** - это **0x2C** байт в шестнадцатеричной системе в общей сложности

![](/files/-Ljtu2amIeQjCtAFpvox)

И буфер назначения это **DEVICELINKBUFFER**. Давайте посмотрим его длину.

![](/files/-Ljtu2b3pWFjCBIfg0xC)

Это **23** \* **2** в десятичной системе, т.е. **46** байт, т.е **0x2E** в шестнадцатеричной системе, так что здесь тоже нет переполнения.

![](/files/-Ljtu2bNpd1HLngxzNNN)

Проблема в том, что в **DEVICENAMEBUFFER** находится имя устройства, а в **DEVICELINKBUFFER** - имя устройства **DOS**.

![](/files/-Ljtu2bY4tPYuoqgs38w)

Затем идёт вызов функции **DBGPRINT**, которая печатает сообщение "**DRIVERENTRY CALLED**".

![](/files/-Ljtu2blRs4TBtYIL6vD)

Давайте продолжим со следующего: преобразуем строку **UNICODE** в ту, которая имеет тип **\_UNICODE\_STRING**. Для этого существует следующий **API RTLINITUNICODESTRING**.

![](/files/-LjrT86elIIAeB1RJbrS)

У нас есть вызов в **RTLINITUNICODESTRING**

![](/files/-LjrT86i9tNnwyku7sfg)

> WCHAR deviceNameBuffer\[] = L"\Device\HelloWorld";

Источник **DEVICENAMEBUFFER** является указателем на буфер, который имеет строку юникода, а назначение - указатель на структуру **UNICODE\_STRING**. Эта структура, которую мы уже видели, имеет три поля, два слова (**LENGHT** и **MAXIMUMLENGHT**, а третья должна быть указателем на строку юникода.

Это означает, что **API** скопирует адрес этого исходного буфера в третье поле структуры, добавит **LENGHT** и **MAXIMUMLENGHT** в соответствующие поля и преобразует общий буфер со строкой **UNICODE** в структуру **UNICODE\_STRING**.

![](/files/-Ljtu2cIbOuWb2j1ptRf)

![](/files/-Ljtu2cRByK62iifnJdm)

![](/files/-LjrT86s9FJiDhQHFayb)

Это структура типа **UNICODE\_STRING** из **8** байт. Так как они представляют собой два слова для **LENGHT** и **DWORD** для копирования указателя на буфер с помощью строки юникода.

Тогда есть вызов к **API IOCREATEDEVICE**, про которую мы говорили.

![](/files/-LjrT86xPKDM3WIbp4Y5)

Мы видели, что самый дальний аргумент, т.е. последний, был указателем на **DWORD**, который использовался как выход. Так что **API** хранит там указатель. Мы видим, что программа устанавливает нуль в переменную **INTERFACEDEVICE**, а затем с помощью инструкции **LEA** находит указатель на эту переменную, где будет записан указатель.

![](/files/-Ljtu2d0hClWmpVr4JLP)

Затем идет инструкция **PUSH 1**, которая является исключительным аргументом, который мы не видели раньше, потому что это не имело большого значения. Затем появляется инструкция **PUSH EDI**.Мы видим, что в регистре **EDI** есть нуль, поскольку раньше была выполнена инструкция **XOR EDI, EDI**.

![](/files/-LjrT878QzppRRLTRYNN)

Это также не очень важно. Затем идёт инструкция **PUSH 8337H**, которая является константой **DEVICETYPE**, которую мы определили в исходном коде.

> \#define FILE\_DEVICE\_HELLOWORLD 0x00008337

Затем появляется указатель на структуру с **\_UNICODE\_STRING** с **DEVICENAME**

![](/files/-Ljtu2dTlwphx4P80037)

Затем идет другая инструкция **PUSH EDI**, которая равна нулю **DEVICEEXTENSIONSIZE** и в конце регистр **EBX** является указателем на **DRIVEROBJECT**.

![](/files/-Ljtu2descwloa-ZSUY2)

Давайте запомним, что это указатель на структуру **\_DRIVER\_OBJECT**.

![](/files/-Ljtu2dpMUWgD5QcOh3x)

Хорошо. При выходе из **API** будет создан **DEVICEOBJECT**.

![](/files/-LjrT87OkfF4g9cbczlg)

Если регистр **EAX** имеет отрицательное значение, будет сбой и инструкция **JS** будет переходить на зеленую стрелку. Но у нас всё будет нормально и программа перейдет к функции **DBGPRINT**, которая напечатает "**SUCESS**"

Затем программа будет делать то же самое с другой строкой **UNICODE** при преобразовании ее из буфера со строкой **UNICODE** в структурную форму **\_UNICODE\_STRING**, как и раньше, с помощью **APIRTLINITUNICODESTRING**.

Поэтому **DEVICELINKUNICODESTRING** теперь будет иметь тип **\_UNICODE\_STRING** и будет иметь в своем третьем поле указатель на буфер со строкой **UNICODE L"\DOSDEVICES\HELLOWORLD"**.

![](/files/-LjrT87S5R5zu85PW_yN)

Затем, передаются указатели на два **\_UNICODE\_STRING** в функцию **IOCREATESYMBOLICLINK**. Мы создаем символическую связь между **DEIVCEOBJECT** и пользовательским режимом.

Регистр **EBX** имеет указатель на структуру **DRIVER\_OBJECT**

![](/files/-LjrT87V5nHeydm-o2y0)

Если объект не находится в структурах как раньше, мы переходим в **LOCAL TYPES** и синхронизируем программа так, чтобы объект отображался. Мы нажимаем **T** в каждом из этих полей.

Как и в предыдущем случае, мы устанавливаем пользовательскую подпрограмму, когда загружается драйвер, которая находится по смещению **EBX** + **34H**. Теперь нажимая **T**, мы видим, что это поле **DRIVERUNLOAD**.

![](/files/-LjrT87XyThZpy_iZMRz)

Мы видим, что программа загрузки драйвера не только печатает с помощью **DBGPRINT** строку "**DRIVER UNLOADING**"

![](/files/-LjrT87bLH-phKPMg4M_)

Поскольку раньше мы создавали символическую ссылку с помощью функции **IOCREATESYMBOLICLINK**, когда мы выходим, мы должны удалить ее с помощью функции **IODELETESYMBOLICLINK**, а также, поскольку мы использовали для создания функцию **DEVICEOBJECT** с **IOCREATEDEVICE**, теперь устройство будет удаляться с помощью **IODELETEDEVICE**, иначе возникнут проблемы с его загрузкой.

Последней вещью во входной функции является поле **MAJORFUNCTION**, которое представляет собой массив указателей обратных вызовов (**DWORD**) на разные функций.

![](/files/-Ljtu2fmuozNGEzUYOjw)

![](/files/-LjrT87j-UWQMNYgYWwV)

**MAJORFUNCTION** \[**IRP\_MJ\_CREATE**] - это первая позиция в массиве, т.е. **MAJORFUNCION**\[**0x0**].

Поскольку у нас есть таблица.

![](/files/-Ljtu2gKZZ4yDCNh5-4A)

> **\[IRP\_MJ\_CREATE]** это \*\*0x0
>
> \[IRP\_MJ\_CLOSE] **это** 0x02
>
> \[IRP\_MJ\_DEVICE\_CONTROL] **это** 0x0E\*\*

Три поля инициализируются адресом функции **DRIVERDISPATCH**

![](/files/-LjrT885XcFPKpRzySce)

Значение записывается в положение **0x0**, так как \[**IRP\_MJ\_CLOSE**] равно **0x0** \* **4** = **0**

Затем

**\[IRP\_MJ\_CLOSE]** равно **0x2** \* **4** даёт **8**

И затем

**\[IRP\_MJ\_DEVICE\_CONTROL]** это **0x0E** \* **4** даёт **0x38**

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

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

![](/files/-Ljtu2gqoL5w9bQBZLG1)

![](/files/-Ljtu2h-A9DWpHk2ddZr)

![](/files/-Ljtu2h9tJ93qBInhnka)

Видно, что когда мы делаем вызов из приложения в пользовательском режиме через **DEVICEIOCONTROL** с использованием **IOCTL** используется этот обратный вызов. Так же во всех трех случаях программа переходит к той же функции, поскольку мы перезаписываем на неё указатели на **DRIVERDISPATCH**.

![](/files/-LjrT88Gz8fBRew_4EHc)

Функция получает два аргумента. Знаменитый указатель на **DEVICE\_OBJECT**, а второй - указатель на структуру **IRP**, которая является сложной структурой, и мы увидим её позже.

![](/files/-Ljtu2hT5ELDXYeI60qY)

![](/files/-LjrT88OTsgQy0EQTEMN)

Мы видим, что, как и в предыдущий раз при регистрации и запуске, драйвер печатает **DRIVERENTRY CALLED** и **SUCESS**, а также при выгрузке **Driver UNLOADING**, но теперь также из пользовательского приложения, которое я сделал при его запуске, хотя раньше нужно было нажимать **START SERVICE** для того, чтобы он начал печатать.

![](/files/-Ljtu2ho79QpWxaWq-Tz)

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

```c
#include "stdafx.h"
#include <windows.h>

#define FILE_DEVICE_HELLOWORLD 0x00008337
#define IOCTL_SAYHELLO (ULONG) CTL_CODE( FILE_DEVICE_HELLOWORLD, 0x00, METHOD_BUFFERED, FILE_ANY_ACCESS )

int main()
{
    HANDLE hDevice;
    DWORD nb;
    hDevice = CreateFile(TEXT(".HelloWorld"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    DeviceIoControl(hDevice, IOCTL_SAYHELLO, NULL, 0, NULL, 0, &nb, NULL);

    CloseHandle(hDevice);
    return 0;
}
```

Т.е. когда я вызываю функцию **CREATEFILE,** чтобы иметь хэндл драйвера, драйвер переходит к обработчику через обратный вызов \[**IRP\_MJ\_CREATE**] и печатает следующее:

![](/files/-LjrT88ZYFSve9M-40um)

Затем, когда Вы вызываете с помощью **функции DEVICEIOCONTROL**, передавая его **IOCTL** код.

![](/files/-Ljtu2iCa6oHJHcx97uy)

Драйвер использует обратный вызов \[**IRP\_MJ\_DEVICE\_CONTROL**], а затем проверяет, является ли **IOCTL** код равным в этом случае **IOCTL\_SAYHELLO**

![](/files/-LjrT88etHeXhue5rV2V)

В этом случае драйвер печатает "**HELLO WORLD**"

![](/files/-LjrT88hrpSZaivHD-PA)

И последний код вызывается, когда я вызываю функцию **CLOSEHANDLE** и вызывается соответствующий \[**IRP\_MJ\_CLOSE**]

![](/files/-Ljtu2ifFKlsdcTUh4Jq)

Я синхронизирую структуру **IRP** через **LOCAL TYPES**.

![](/files/-Ljtu2iqyiFSZP31Vt2D)

И я вижу на вкладке **STRUCTURES** ту же самую структуру.

Мы видим, что когда я его отладку, и я поставлю **BP** в функцию обработки, после прибываем в это место

![](/files/-Ljtu2j4GYA5tKLI2w6V)

Драйвер читает из структуры **IRP** часть **TAIL,** которая не определена в **MSDN**, но здесь, после поиска по смещению **EDI+60** и передачи этого значения в регистр **EBX**, его содержимое переходит в регистр **EAX**, который впервые имеет значение \[**IRP\_MJ\_CREATE**], т.е. нуль. И в этом случае драйвер пойдет туда, чтобы напечатать сообщение о том, что произошло создание.

Если я снова нажму **RUN**, драйвер снова остановится со значением регистра **EAX** равным **0x0E** из \[**IRP\_MJ\_DEVICE\_CONTROL**].

![](/files/-Ljtu2jIkDSilO0WFsFd)

Поскольку регистр **EAX** отличается от нуля, драйвер идет сюда.

![](/files/-LjrT88tj3kVJ1Rd0DT5)

И в этом случае драйвер приходит в розовый блок, печатая, что добрался сюда через **IOCTL**.

![](/files/-LjrT88xp6o6awddLFZX)

```c
#define IOCTL_SAYHELLO (ULONG) CTL_CODE(FILE_DEVICE_HELLOWORLD, 0x00, METHOD_BUFFERED, FILE_ANY_ACCESS)
```

В коде **IOCTL** код, который получается из значения **0x8337 FILE\_DEVICE**, выполняется несколькими операциями в соответствии с типом **IOCTL** (в этом случае **METHOD BUFFERED** и т.д. и т.д), Который дает нам **IOCTL** код равным **83370000**.

Здесь драйвер сравнивает это и как есть. Он выходит и печатает сообщение "**HELLO WORLD**!".

![](/files/-Ljtu2jvFc7e8UOz-JqS)

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

![](/files/-Ljtu2k41QRLC777Js2n)

В третий раз, когда мы останавливаемся, мы исполняем функцию **CLOSEHANDLE** и регистр **EAX** равен **2**.

![](/files/-LjrT899OPOjS4SaS1Jg)

И происходит печать.

![](/files/-LjrT89BFE0yoVhhfZQc)

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

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

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

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

22.10.2018

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