Функции для управления устройствами
Задание 5. Исследовать структуру DRIVER_OBJECT.
Указания к выполнению.
1. В предыдущем задании мы узнали адрес объекта драйвера, который отвечает за устройство HarddiskVolume1. Информацию об этом драйвере можно получить либо при помощи команды:
dt DRIVER_OBJECT 82373690
либо при помощи команды:
!drvobj 82373690
Воспользуемся вторым способом:
На рисунке показаны имя драйвера и объекты-устройства, которыми данный драйвер управляет. В частности, кроме устройства HarddiskVolume1, драйвер управляет устройством, которое описывается объектом DEVICE_OBJECT, расположенным по адресу 0x82373278.
2. Дополнительную информацию о драйверах можно получить, воспользовавшись утилитой Process Explorer. В ней следует выбрать процесс System и отобразить для него DLL (меню View – пункт Lower Pane View – DLL):
Задание 6. Исследовать структуру IRP.
Указания к выполнению.
1. Продолжим трассировку функции NtReadFile (мы остановились на строке 121 – вызов функции ObReferenceObjectByHandle) – клавиша F10.
Обратите внимание на переменную deviceObject (строка 135). Значение, которое в ней оказывается после вызова функции IoGetRelatedDeviceObject не совпадает с полученным нами в задании 3. Дело в том, что запрос к файлу проходит несколько драйверов на разных уровнях, и в переменную deviceObject помещается ссылка на драйвер верхнего уровня.
Самостоятельно исследуйте функцию IoGetRelatedDeviceObject, чтобы понять, каким образом получается ссылка на драйвер.
2. Найдите строку 517 – здесь происходит вызов функции IopAllocateIrp, которая выделяет память (но не заполняет) под структуру IRP. Поставьте в этой строке точку останова (нажмите F9):
И продолжите выполнение кода функции – нажмите F5. Управление должно перейти в точку останова на строку 517.
3. После выполнения функции IopAllocateIrp (нажмите клавишу F10) узнайте адрес переменной irp и просмотрите её содержимое либо при помощи команды:
dt irp address
либо при помощи команды:
!irp address
Предположим, адрес переменной irp равен 0x81efc008:
Из рисунка видно, что структура IRP пустая, и в ней 9 блоков стека (структур типа IO_STACK_LOCATION), 10-й блок, не заполненный, является текущим. Количество блоков стека указывается в поле StackSize структуры DEVICE_OBJECT, а определяется это количество системой; причем различаются малые IRP с одним блоком стека и большие IRP, количество блоков стека которых варьируется (подробнее см. [5, стр. 595]). В нашем случае мы имеем дело с большим IRP.
4. Далее по коду функции происходит заполнение структуры IRP (просмотрите заполнение полей самостоятельно).
Обратите внимание на поля UserIosb (блок статуса, строка 542), majorFunction (номер основной функции; в случае чтения он равен константе IRP_MJ_READ = 3, строка 558), UserBuffer (буфер чтения, строка 697).
5. В строке 725 происходит вызов функции IopSynchronousServiceTail, которая помещает сформированный IRP в очередь потока.
Перед вызовом этой функции просмотрите структуру IRP:
!irp 0x81efc008
Из рисунка видно, что последний блок стека сейчас заполнен:
- MajorFunction = 3 (константа IRP_MJ_READ);
- FileObject = 81FFF2F8 (адрес объекта FILE_OBJECT для файла input.txt);
- Args = 50 (размер буфера).
Чтобы посмотреть структуру IO_STACK_LOCATION для данного блока стека, нужно из адреса текущего блока стека (выделен на рисунке) вычесть 0x24 (размер блока стека):
dt IO_STACK_LOCATION 0x81EFC1BC–0x24
Задание 7. Исследовать результаты операции чтения.
Указания к выполнению.
1. Выполните вызов функции IopSynchronousServiceTail (нажмите один раз F10).
Когда управление вернется к отладчику, операция чтения будет выполнена.
2. Просмотрите содержимое буфера чтения.
Адрес буфера равен 0x0012FF14 (переменная Buffer, см. Задание 2 данной лабораторной работы). Просмотреть его содержимое (в ASCII кодах) можно командой:
da 0x0012FF14
или (в байтах) командой:
db 0x0012FF14
3. Просмотрите содержимое блока статуса – переменную IoStatusBlock (тип IO_STATUS_BLOCK). Найти её адрес можно в окне Locals.
Поле Status, равное нулю, говорит о том, что операция выполнена успешно. В поле Information содержится количество прочитанных байт (0x20 = 32 байта).
Задания для самостоятельного выполнения
Задание 1. Выполнить трассировку функции NtWriteFile и найдите отличия от функции NtReadFile.
Указания к выполнению.
1. Для исследования функции ядра NtWriteFile создайте проект на основе примера из MSDN для WinAPI функции WriteFile.
Задание 2. Исследовать параметры безопасности объектов устройств.
Указания к выполнению.
1. В объекте DEVICE_OBJECT имеется поле SecurityDescriptor. Требуется по методике, изложенной в лабораторной работе 5 "Безопасность в Windows", исследовать дескриптор безопасности объекта устройства.