Указания к выполнению.
1. В виртуальной машине запустите приложение ReadFile.exe, разработанное в лабораторной работе 5 "Безопасность в Windows".
2. Когда сработает точка останова и управление перейдет к отладчику WinDbg, нужно установить точку останова на функции NtReadFile для процесса ReadFile.exe.
Для этого сначала следует определить адрес объекта EPROCESS для процесса ReadFile.exe при помощи команды:
!process 0 0 ReadFile.exe
В данном случае адрес равен 0x8222F630.
Установить точку останова для функции NtReadFile для процесса ReadFile.exe можно при помощи следующей команды:
bp /p 8222F630 nt!NtReadFile
Проверьте, что команда выполнена верно: в меню Edit отладчика выберите пункт Breakpoints…:
Замечание. После перезапуска процесса ReadFile.exe его адрес может поменяться. В этом случае придется удалить старую точку останова и повторить нахождение адреса объекта EPROCESS и установки новой точки останова.
3. Продолжите выполнение приложения ReadFile.exe (нажмите в отладчике F5). Должна сработать точка останова и открыться исходный код функции NtReadFile.
4. Удостоверьтесь, что поток, вызвавший функцию NtReadFile, действительно принадлежит процессу ReadFile.exe: используя клавишу F10 дойдите в отладчике до строки 112. В этом месте вызывается функция PsGetCurrentThread – получение указателя на текущий поток (посмотрите, как эта функция реализована). Чтобы произошел её вызов ещё раз нажмите F10.
После вызова функции PsGetCurrentThread в переменной CurrentThread содержится адрес объекта ETHREAD текущего потока. Этот адрес можно узнать либо наведя указатель мыши на переменную CurrentThread, либо посмотрев её значение в окне Locals (Alt+3):
В данном примере адрес структуры ETHREAD равен 0x81FB6A10.
Чтобы найти процесс, которому принадлежит текущий поток, найдите поле ThreadsProcess структуры ETHREAD, расположенной по адресу 0x81FB6A10. Это можно сделать либо введя команду:
dt ethread 81FB6A10
либо развернув переменную CurrentThread в окне Locals. На рисунке ниже показан первый способ:
Адрес структуры EPROCESS процесса-владельца текущего потока равен 0x8222F630.
Далее введите команду:
dt eprocess 8222F630
и в поле ImageFileName будет записано имя исполняемого файла:
Ту же информацию можно получить последовательно раскрывая поля структур в окне Locals.
5. Обратите внимание на функцию ObReferenceObjectByHandle в строке 121. Эта функция по заданному дескриптору (handle) возвращает указатель на объект. В данном случае функция ObReferenceObjectByHandle по известному дескриптору файла (параметр FileHandle) возвращает указатель на объект FILE_OBJECT (параметр fileObject).
Кроме того, данная функция проверяет права доступа потока на чтение файла. Если прав недостаточно, функция сообщает об этом, возвращая соответствующее значение (STATUS_ACCESS_DENIED) переменной status.
Указания к выполнению.
1. Функция NtReadFile имеет следующие параметры:
FileHandle – дескриптор файла, информацию из которого требуется прочитать;Event – дескриптор объекта "Событие", которое может быть установлено при завершении операции чтения (необязательный параметр);ApcRoutine, ApcContext – зарезервированные неиспользуемые параметры;IoStatusBlock – указатель на структуру IO_STATUS_BLOCK, в которой будет содержаться статус операции после завершения чтения, в частности, количество реально прочитанных байт (см. файл public\sdk\inc\ntioapi.h, строка 451);Buffer – указатель на область памяти (буфер), в которую будут помещены прочитанные данные;Length – размер буфера в байтах;ByteOffset – определяет для операции чтения смещение в байтах относительно начала файла;Key – необязательный параметр, используемый в случае блокировки файла.Значения параметров можно посмотреть в окне Locals:
Дескриптор файла равен 1C, адрес буфера равен 0x0012FF14, размер буфера – 80 байт (0x50), смещение в файле равно нулю.
Посмотрим, что находится в данный момент в буфере:
dd 12FF14
Пока данные не прочитаны, буфер пуст.
По дескриптору файла можно узнать, что это за файл. Воспользуемся следующей командой:
!handle 1C f 8222F630
где 1C – дескриптор файла;
f – отображение полной информации об объекте;
8222F630 – адрес объекта EPROCESS процесса ReadFile.exe.
Как видно из рисунка, дескриптор 1C описывает файл input.txt.
Указания к выполнению.
1. Как уже отмечалось, в строке 121 вызывается функция ObReferenceObjectByHandle, которая в переменной fileObject возвращает указатель на объект FILE_OBJECT для файла input.txt.
Вследствие оптимизации программного кода ядра значение переменной fileObject невозможно напрямую посмотреть в отладчике (например, окно Locals не показывает эту переменную, наведение указателя мыши также не дает результата). Можно определить адрес объекта FILE_OBJECT, исследуя дизассемблированный код (окно Disassembly отладчика), но мы поступим проще.
В предыдущем задании при помощи команды
!handle 1C f 8222F630
нам удалось по дескриптору определить имя файла. В результате этой же команды выводится и адрес объекта FILE_OBJECT:
Таким образом, адрес объекта FILE_OBJECT для файла input.txt равен 0x81FFF2F8.
2. Изучим информацию, содержащуюся в объекте FILE_OBJECT.
Для этого можно воспользоваться двумя способами.
Способ 1. При помощи команды:
dt FILE_OBJECT 81FFF2F8
Поле Type равно 5 – согласно константам, определенным в файле base\ntos\inc\io.h (строка 35), это тип IO_TYPE_FILE.
В структуре присутствует адрес объекта DEVICE_OBJECT, с которым связан данный файл (см. лекцию 15 "Управление устройствами", рис. 15.1 и рис. 15.2).
Способ 2. При помощи команды:
!fileobj 81FFF2F8
Здесь присутствует та же информация, что и в первом случае.
Указания к выполнению.
1. В предыдущем задании в информации, выводимой об объекте FILE_OBJECT, присутствует адрес объекта DEVICE_OBJECT, на котором находится файл. Этот адрес равен 0x823ADE00.
Чтобы посмотреть информацию об объекте DEVICE_OBJECT по этому адресу, можно опять воспользоваться либо общей командой отображения типов данных dt, либо специальной командой !devobj. Продемонстрируем результат второго способа:.
!devobj 823ADE00
Данная команда сообщает имя драйвера, который управляет устройством (\Driver\Ftdisk – диспетчер томов) и адрес объекта драйвера DRIVER_OBJECT (0x82373690). Кроме того, из рисунка видно, что данное устройство отвечает за том HarddiskVolume1 (диск C).
Указания к выполнению.
1. В предыдущем задании мы узнали адрес объекта драйвера, который отвечает за устройство HarddiskVolume1. Информацию об этом драйвере можно получить либо при помощи команды:
dt DRIVER_OBJECT 82373690
либо при помощи команды:
!drvobj 82373690
Воспользуемся вторым способом:
На рисунке показаны имя драйвера и объекты-устройства, которыми данный драйвер управляет. В частности, кроме устройства HarddiskVolume1, драйвер управляет устройством, которое описывается объектом DEVICE_OBJECT, расположенным по адресу 0x82373278.
2. Дополнительную информацию о драйверах можно получить, воспользовавшись утилитой Process Explorer. В ней следует выбрать процесс System и отобразить для него DLL (меню View – пункт Lower Pane View – DLL):
Указания к выполнению.
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
Указания к выполнению.
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", исследовать дескриптор безопасности объекта устройства.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.