Драйверы режима ядра: Часть 8: Базовая техника: Работа с памятью. Совместно используемый раздел — Архив WASM.RU
Прежде чем обратиться непосредственно к теме этой статьи, нам необходимо вкратце рассмотреть структурную обработку исключений (Structured Exception Handling, SEH), т.к. она нам потребуется.
8.1 Структурная обработка исключений
Я не буду подробно рассказывать о том, что это такое. Если дружите с английским языком, настоятельно советую найти и прочитать статью “Win32 Exception handling for assembler programmers” by Jeremy Gordon. Она написана применительно к режиму пользователя, но SEH – это универсальный механизм обработки исключений, как в режиме пользователя, так и в режиме ядра. Существует, однако, одно, но очень существенное отличие: в ядре с помощью SEH можно обработать не все исключения! Например, попытка деления на ноль приведет к краху системы даже при установленном обработчике SEH. Самое ужасное, что обращение к невыделенной памяти ядра также приводит к появлению BSOD. А обращение к невыделенной памяти режима пользователя легко обрабатывается SEH. Так что единственная возможность избежать краха – писать код так, чтобы он не вызывал необрабатываемых исключений.
8.1.1 Исходный текст драйвера seh
Код (Text):
;@echo off ;goto make ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; ; SEH – Структурная обработка исключений. ; ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .386 .model flat, stdcall option casemap:none ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; В К Л Ю Ч А Е М Ы Е Ф А Й Л Ы ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: include \masm32\include\w2k\ntstatus.inc include \masm32\include\w2k\ntddk.inc include \masm32\include\w2k\ntoskrnl.inc includelib \masm32\lib\w2k\ntoskrnl.lib include \masm32\Macros\Strings.mac include seh0.inc ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; С Т Р У К Т У Р Ы ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: SEH STRUCT SafeEip dd ? ; Точка, с которой будет продолжено выполнение потока PrevEsp dd ? ; Предыдущее значение esp PrevEbp dd ? ; Предыдущее значение ebp SEH ENDS ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; Н Е И Н И Ц И А Л И З И Р О В А Н Н Ы Е Д А Н Н Ы Е ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .data? seh SEH <> ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; К О Д ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .code ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; BuggyReader ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: BuggyReader proc xor eax, eax mov eax, [eax] ; !!! Без SEH - BSOD !!! ret BuggyReader endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; BuggyWriter ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: BuggyWriter proc mov eax, MmUserProbeAddress mov eax, [eax] mov eax, [eax] mov byte ptr [eax], 0 ; !!! Без SEH - BSOD !!! ret BuggyWriter endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; ExceptionHandler ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ExceptionHandler proc C uses esi pExcept:DWORD, pFrame:DWORD, pContext:DWORD, pDispatch:DWORD mov esi, pExcept invoke DbgPrint, $CTA0("\nSEH: An exception %08X has occured\n"), \ [esi][EXCEPTION_RECORD.ExceptionCode] .if [esi][EXCEPTION_RECORD.ExceptionCode] == 0C0000005h ; Если произошло исключение типа EXCEPTION_ACCESS_VIOLATION, ; то выводим дополнительную информацию. invoke DbgPrint, $CTA0(" Access violation at address: %08X\n"), \ [esi][EXCEPTION_RECORD.ExceptionAddress] .if [esi][EXCEPTION_RECORD.ExceptionInformation][0] ; Попытка чтения или записи ? invoke DbgPrint, $CTA0(" The code tried to write to address %08X\n\n"), \ [esi][EXCEPTION_RECORD.ExceptionInformation][4] .else invoke DbgPrint, $CTA0(" The code tried to read from address %08X\n\n"), \ [esi][EXCEPTION_RECORD.ExceptionInformation][4] .endif .endif lea eax, seh push (SEH PTR [eax]).SafeEip push (SEH PTR [eax]).PrevEsp push (SEH PTR [eax]).PrevEbp mov eax, pContext pop (CONTEXT PTR [eax]).regEbp pop (CONTEXT PTR [eax]).regEsp pop (CONTEXT PTR [eax]).regEip xor eax, eax ret ExceptionHandler endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; DriverEntry ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING invoke DbgPrint, $CTA0("\nSEH: Entering DriverEntry\n") ;:::::::::::::::::::::::::::::::: ; Ставим SEH “вручную” ;:::::::::::::::::::::::::::::::: assume fs:nothing push offset ExceptionHandler push fs:[0] mov fs:[0], esp assume fs:error mov seh.SafeEip, offset SafePlace mov seh.PrevEbp, ebp mov seh.PrevEsp, esp invoke BuggyReader SafePlace: assume fs:nothing pop fs:[0] add esp, sizeof DWORD assume fs:error ;::::::::::::::::::::::::::::::::::::::::::::::: ; Используем макросы. Это чуть-чуть проще <img src="styles/smiles_s/smile3.gif" class="mceSmilie" alt=":smile3:" title="Smile3 :smile3:"> ;::::::::::::::::::::::::::::::::::::::::::::::: _try invoke BuggyWriter _finally invoke DbgPrint, $CTA0("\nSEH: Leaving DriverEntry\n") mov eax, STATUS_DEVICE_CONFIGURATION_ERROR ret DriverEntry endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: end DriverEntry :make set drv=seh \masm32\bin\ml /nologo /c /coff %drv%.bat \masm32\bin\link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native %drv%.obj del %drv%.obj echo. pause
8.1.2 Устанавливаем SEH-фрейм
Код (Text):
assume fs:nothingПо умолчанию masm запрещает использование регистра fs. Снимаем это ограничение с помощью директивы assume.
Код (Text):
push offset ExceptionHandler push fs:[0] mov fs:[0], espУстанавливаем так называемый SEH-фрейм, который определяет адрес обработчика исключений. Начиная с этого момента обрабатываемые исключения, происходящие в потоке (каждый поток имеет свои собственные обработчики, если конечно они установлены) будут приводить к вызову системой этого обработчика. В данном случае - это процедура ExceptionHandler, код которой мы рассмотрим позже. Для получения информации обо всех обработчиках установленных в текущем потоке отладчик SoftICE предоставляет команду xframe.
Код (Text):
assume fs:errorЗапрещаем дальнейшее использование регистра fs, как было установлено по умолчанию.
Код (Text):
mov seh.SafeEip, offset SafePlace mov seh.PrevEbp, ebp mov seh.PrevEsp, espДля того чтобы после обработки исключения наш обработчик смог возобновить выполнение потока мы должны сохранить значения регистров esp, ebp и указать на точку, с которой поток продолжит выполнение. Сохраняем эти три значения в структуре seh и вызываем процедуру BuggyReader, которая попытается прочитать двойное слово по адресу 00000000.
Код (Text):
BuggyReader proc xor eax, eax mov eax, [eax] ret BuggyReader endpОбращение по нулевому указателю являлось столь частой ошибкой, что Microsoft пришлось отвести 64 килобайта памяти в диапазоне 00000000-0000FFFFh и сделать этот регион недоступным. Обращение к любому байту в этом диапазоне приводит к исключению типа EXCEPTION_ACCESS_VIOLATION.
8.1.3 Обрабатываем исключение
При попытке чтения с адреса 00000000 в процедуре BuggyReader происходит исключение, и мы попадаем в установленный нами обработчик.
Код (Text):
mov esi, pExcept invoke DbgPrint, $CTA0("\nSEH: An exception %08X has occured\n"), \ [esi][EXCEPTION_RECORD.ExceptionCode] .if [esi][EXCEPTION_RECORD.ExceptionCode] == 0C0000005h invoke DbgPrint, $CTA0(" Access violation at address: %08X\n"), \ [esi][EXCEPTION_RECORD.ExceptionAddress] .if [esi][EXCEPTION_RECORD.ExceptionInformation][0] ; Попытка чтения или записи ? invoke DbgPrint, $CTA0(" The code tried to write to address %08X\n\n"), \ [esi][EXCEPTION_RECORD.ExceptionInformation][4] .else invoke DbgPrint, $CTA0(" The code tried to read from address %08X\n\n"), \ [esi][EXCEPTION_RECORD.ExceptionInformation][4] .endif .endifПервым делом наш обработчик выводит соответствующее отладочное сообщение и, если произошло исключение типа EXCEPTION_ACCESS_VIOLATION, кое-какую дополнительную информацию. Затем приступаем к собственно обработке исключения.
Код (Text):
lea eax, seh push (SEH PTR [eax]).SafeEip push (SEH PTR [eax]).PrevEsp push (SEH PTR [eax]).PrevEbp mov eax, pContext pop (CONTEXT PTR [eax]).regEbp pop (CONTEXT PTR [eax]).regEsp pop (CONTEXT PTR [eax]).regEipУ нас обработка исключения заключается в простом восстановлении значений в регистрах esp, ebp и помещении в регистр eip адреса, с которого безопасно продолжить выполнение потока. Эти данные обработчик извлекает из предварительно заполненной нами структуры seh и помещает их в структуру CONTEXT, указатель на которую передается системой.
Код (Text):
xor eax, eax retВозвращаем значение ExceptionContinueExecution равное нулю, которое сообщает системе, что она должна перезагрузить контекст потока и продолжить его выполнение. Т.к. значение eip равно адресу метки SafePlace, а значения в регистрах esp и ebp восстановлены, то поток продолжит своё выполнение с метки SafePlace, как ни в чем не бывало.
8.1.4 Удаляем SEH-фрейм
Код (Text):
SafePlace: assume fs:nothing pop fs:[0] add esp, sizeof DWORD assume fs:errorВосстанавливаем старое значение в fs:[0] и корректируем указатель стека (подробности см. в вышеупомянутой статье).
8.1.5 Используем макросы для установки/удаления SEH-фрейма
Проделаем ту же самую операцию с помощью макросов, которые я написал для упрощения работы с SEH. Макросы определены в файле seh0.inc. Описывать их я не буду, т.к. делают они то же самое, что мы только что разобрали. Если в обработчике исключений не требуется какой-то особой обработки, то при использовании макросов _try/_finally весь вышеприведенный код сведется к трем строчкам. Точкой SafePlace, в данном случае, будет строка, в которой стоит макрос _finally.
Код (Text):
_try invoke BuggyWriter _finallyНа этот раз вызовем процедуру BuggyWriter, которая попытается записать двойное слово по адресу MmUserProbeAddress (7FFF0000h).
Код (Text):
BuggyWriter proc mov eax, MmUserProbeAddress mov eax, [eax] mov eax, [eax] mov byte ptr [eax], 0 ret BuggyWriter endp64Кб памяти в диапазоне 7FFF0000h – 7FFFFFFFh являются недоступным регионом, зарезервированным для предотвращения передачи буферов через границу пользовательского и системного пространства. Стартовый адрес этого региона содержится в экспортируемой ядром переменной MmUserProbeAddress.
Вооружившись знаниями о структурной обработке исключений, переходим к следующему примеру, где нам потребуется из драйвера обратиться к памяти режима пользователя. Подобные обращения крайне желательно заключать в SEH-фрейм.
8.2 Разделяемый объект “раздел”
Windows предоставляет богатейший набор механизмов для обмена данными между двумя и более процессами (Interprocess Communications, IPC): буфер обмена, DDE, оконные сообщения (в частности WM_COPYDATA), почтовые ящики (mailslot), сокеты (sockets) и др. Все эти механизмы, так или иначе, базируются на объекте “проекция файла” (file-mapping object), представляющем собой блок памяти доступный двум и более процессам для совместного использования. В терминологии DDK проецируемые файлы являются объектами “раздел” (section object), в просторечии называемыми секциями (не путать с секциями PE-файла).
Объект “раздел” является самым низкоуровневым механизмом совместного использования данных. Этот же самый объект используется системой для загрузки исполняемых образов в память, а диспетчер кэша использует его для доступа к данным в кэшированных файлах. Объект “раздел” также позволяет проецировать файлы на диске в адресные пространства процессов и работать с ними не как с файлами, а как с блоками памяти.
Совместное использование данных с помощью объекта “раздел” происходит следующим образом: один процесс создает отображаемый в память файл, вызывая функцию CreateFileMapping. Затем, вызовом функции MapViewOfFile (если спуститься на один уровень глубже, то вызывается NtMapViewOfSection), отображает его представление (view) на свое адресное пространство, а другой процесс через OpenFileMapping открывает тот же самый отображаемый файл и так же отображает его, но уже в свое адресное пространство. В результате одни и те же физические страницы памяти становятся доступными в обоих процессах, что позволяет им легко передавать через эту область большие порции данных, т.к. любые изменения на этих страницах в одном процессе отражаются на представление в другом процессе.
Совместно используемый объект “раздел” может служить средством обмена данными не только для пользовательских процессов, но и для драйверов. Или, как в следующем примере, именованный объект “раздел” будет использован для обмена данными между пользовательским процессом и драйвером.
8.2.1 Исходный текст программы управления драйвером SharedSection
Код (Text):
;@echo off ;goto make ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; ; SharedSection – Клиент драйвера SharedSection.sys ; ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .386 .model flat, stdcall option casemap:none ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; В К Л Ю Ч А Е М Ы Е Ф А Й Л Ы ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: include \masm32\include\windows.inc include \masm32\include\w2k\native.inc include \masm32\include\w2k\ntstatus.inc include \masm32\include\winioctl.inc include \masm32\include\kernel32.inc include \masm32\include\user32.inc include \masm32\include\advapi32.inc include \masm32\include\w2k\ntdll.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\user32.lib includelib \masm32\lib\advapi32.lib includelib \masm32\lib\w2k\ntdll.lib include \masm32\Macros\Strings.mac include ..\common.inc ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; К О Д ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .code ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; CallDriver ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: CallDriver proc local fOk:BOOL local hSCManager:HANDLE local hService:HANDLE local acModulePath[MAX_PATH]:CHAR local _ss:SERVICE_STATUS local hDevice:HANDLE local abyOutBuffer[4]:BYTE local dwBytesReturned:DWORD and fOk, FALSE invoke OpenSCManager, NULL, NULL, SC_MANAGER_ALL_ACCESS .if eax != NULL mov hSCManager, eax push eax invoke GetFullPathName, $CTA0("SharedSection.sys"), sizeof acModulePath, addr acModulePath, esp pop eax invoke CreateService, hSCManager, $CTA0("SharedSection"), $CTA0("One way to share section"), \ SERVICE_START + SERVICE_STOP + DELETE, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, \ SERVICE_ERROR_IGNORE, addr acModulePath, NULL, NULL, NULL, NULL, NULL .if eax != NULL mov hService, eax invoke StartService, hService, 0, NULL .if eax != 0 invoke CreateFile, $CTA0("\\\\.\\SharedSection"), 0, \ 0, NULL, OPEN_EXISTING, 0, NULL .if eax != INVALID_HANDLE_VALUE mov hDevice, eax ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: invoke DeviceIoControl, hDevice, IOCTL_SHARE_MY_SECTION, NULL, 0, NULL, 0, \ addr dwBytesReturned, NULL .if eax != 0 inc fOk .else invoke MessageBox, NULL, $CTA0("Can't send control code to device."), NULL, \ MB_OK + MB_ICONSTOP .endif ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: invoke CloseHandle, hDevice .else invoke MessageBox, NULL, $CTA0("Device is not present."), NULL, MB_ICONSTOP .endif invoke ControlService, hService, SERVICE_CONTROL_STOP, addr _ss .else invoke MessageBox, NULL, $CTA0("Can't start driver."), NULL, MB_OK + MB_ICONSTOP .endif invoke DeleteService, hService invoke CloseServiceHandle, hService .else invoke MessageBox, NULL, $CTA0("Can't register driver."), NULL, MB_OK + MB_ICONSTOP .endif invoke CloseServiceHandle, hSCManager .else invoke MessageBox, NULL, $CTA0("Can't connect to Service Control Manager."), \ NULL, MB_OK + MB_ICONSTOP .endif mov eax, fOk ret CallDriver endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; start ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: start proc local hSection:HANDLE local liSectionSize:_LARGE_INTEGER local oa:OBJECT_ATTRIBUTES local pSectionBaseAddress:PVOID local liViewSize:_LARGE_INTEGER and liSectionSize.HighPart, 0 mov liSectionSize.LowPart, SECTION_SIZE lea ecx, oa InitializeObjectAttributes ecx, offset g_usSectionName, OBJ_CASE_INSENSITIVE, NULL, NULL invoke ZwCreateSection, addr hSection, SECTION_MAP_WRITE + SECTION_MAP_READ, addr oa, \ addr liSectionSize, PAGE_READWRITE, SEC_COMMIT, NULL .if eax == STATUS_SUCCESS and pSectionBaseAddress, NULL and liViewSize.HighPart, 0 and liViewSize.LowPart, 0 invoke ZwMapViewOfSection, hSection, NtCurrentProcess, addr pSectionBaseAddress, 0, \ SECTION_SIZE, NULL, addr liViewSize, ViewShare, 0, PAGE_READWRITE .if eax == STATUS_SUCCESS CTA ".revird ecived a dna sessecorp resu neewteb yromem ", g_szStrToReverse CTA "erahs ot euqinhcet emas eht esu nac uoy ,revewoH " CTA ".sessecorp resu gnoma yromem gnirahs rof desu euqinhcet " CTA0 "nommoc a si elif gnigap eht yb dekcab elif deppam-yromem A" invoke strcpy, pSectionBaseAddress, addr g_szStrToReverse invoke CallDriver .if eax == TRUE invoke MessageBox, NULL, pSectionBaseAddress, \ $CTA0("HOWTO: Share Memory Between User Mode and Kernel Mode"), \ MB_OK + MB_ICONINFORMATION .endif invoke ZwUnmapViewOfSection, NtCurrentProcess, pSectionBaseAddress .else invoke MessageBox, NULL, $CTA0("Can't map section."), NULL, MB_OK + MB_ICONSTOP .endif invoke ZwClose, hSection .else invoke MessageBox, NULL, $CTA0("Can't create section."), NULL, MB_OK + MB_ICONSTOP .endif invoke ExitProcess, 0 ret start endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: end start :make set exe=SharedSection if exist ..\%exe%.exe del ..\%exe%.exe \masm32\bin\ml /nologo /c /coff %exe%.bat \masm32\bin\link /nologo /subsystem:windows %exe%.obj del %exe%.obj move %exe%.exe .. if exist %exe%.exe del %exe%.exe echo. pauseРазберем только ключевые моменты, все остальное должно быть понятно без комментариев.
Код (Text):
and liSectionSize.HighPart, 0 mov liSectionSize.LowPart, SECTION_SIZEПри создании раздела придется указать его размер. Т.к. этот размер может превышать 4Гб, то для задания размера используется переменная типа LARGE_INTEGER, которую мы инициализируем значением SECTION_SIZE равным размеру одной страницы (4Кб). Константа SECTION_SIZE определена в файле common.inc.
Код (Text):
lea ecx, oa InitializeObjectAttributes ecx, offset g_usSectionName, OBJ_CASE_INSENSITIVE, NULL, NULLС макросом InitializeObjectAttributes мы уже знакомы – как-то раз мы его уже использовали. Поскольку для последующего вызова ZwCreateSection требуется заполненная соответствующим образом структура OBJECT_ATTRIBUTES, то мы это и делаем, пользуясь этим макросом.
Раздел, который мы собираемся совместно использовать должен быть именованным, для того чтобы его можно было открыть по имени. Имя раздела определено в файле common.inc таким образом:
Код (Text):
.const CCOUNTED_UNICODE_STRING "\\BaseNamedObjects\\UserKernelSharedSection", g_usSectionName, 4Объект ”раздел” попадет в каталог BaseNamedObjects пространства имен диспетчера объектов, в который обычно помещаются именованные объекты создаваемые пользовательскими процессами.
Код (Text):
invoke ZwCreateSection, addr hSection, SECTION_MAP_WRITE + SECTION_MAP_READ, addr oa, \ addr liSectionSize, PAGE_READWRITE, SEC_COMMIT, NULLВызовом функции ZwCreateSection, создаем именованный объект “раздел” размером SECTION_SIZE доступный для чтения и записи. Если раздел будет создан, то в переменной hSection получим его описатель.
Код (Text):
and pSectionBaseAddress, NULL and liViewSize.HighPart, 0 and liViewSize.LowPart, 0 invoke ZwMapViewOfSection, hSection, NtCurrentProcess, addr pSectionBaseAddress, 0, SECTION_SIZE, \ NULL, addr liViewSize, ViewShare, 0, PAGE_READWRITEОтображаем представление всего раздела в память. Здесь довольно много параметров – все они подробно описаны в DDK. Т.к. переменная pSectionBaseAddress инициализирована нулевым значением, система сама определит по какому виртуальному адресу ей удобнее отобразить раздел и вернет этот адрес в этой же переменной. Инициализированная нулем переменная liViewSize определяет, что раздел будет отображен полностью.
Код (Text):
CTA ".revird ecived a dna sessecorp resu neewteb yromem ", g_szStrToReverse CTA "erahs ot euqinhcet emas eht esu nac uoy ,revewoH " CTA ".sessecorp resu gnoma yromem gnirahs rof desu euqinhcet " CTA0 "nommoc a si elif gnigap eht yb dekcab elif deppam-yromem A" invoke strcpy, pSectionBaseAddress, addr g_szStrToReverseКопируем в полученное представление перевернутую строку. Задачей драйвера будет привести эту строку к читабельному виду.
Код (Text):
invoke CallDriver .if eax == TRUE invoke MessageBox, NULL, pSectionBaseAddress, \ $CTA0("HOWTO: Share Memory Between User Mode and Kernel Mode"), \ MB_OK + MB_ICONINFORMATION .endifЗначение TRUE возвращенное процедурой CallDriver означает, что драйвер справился со своей задачей. Показываем результаты его работы. В процедуре CallDriver мы проводим обычные операции регистрации и запуска драйвера и посылаем ему управляющий код IOCTL_SHARE_MY_SECTION.
Код (Text):
invoke ZwUnmapViewOfSection, NtCurrentProcess, pSectionBaseAddress .endif invoke ZwClose, hSectionПриводим систему в исходное состояние.
8.2.2 Исходный текст драйвера SharedSection
Код (Text):
;@echo off ;goto make ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; ; SharedSection – использует объект “раздел”, созданный программой управления. ; ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .386 .model flat, stdcall option casemap:none ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; В К Л Ю Ч А Е М Ы Е Ф А Й Л Ы ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: include \masm32\include\w2k\ntstatus.inc include \masm32\include\w2k\ntddk.inc include \masm32\include\w2k\ntoskrnl.inc include \masm32\include\w2k\native.inc includelib \masm32\lib\w2k\ntoskrnl.lib include \masm32\Macros\Strings.mac include ..\common.inc include seh0.inc ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; Н Е И З М Е Н Я Е М Ы Е Д А Н Н Ы Е ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .const CCOUNTED_UNICODE_STRING "\\Device\\SharedSection", g_usDeviceName, 4 CCOUNTED_UNICODE_STRING "\\DosDevices\\SharedSection", g_usSymbolicLinkName, 4 ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; К О Д ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .code ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; DispatchCreateClose ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: DispatchCreateClose proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP mov eax, pIrp mov (_IRP PTR [eax]).IoStatus.Status, STATUS_SUCCESS and (_IRP PTR [eax]).IoStatus.Information, 0 fastcall IofCompleteRequest, pIrp, IO_NO_INCREMENT mov eax, STATUS_SUCCESS ret DispatchCreateClose endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; DispatchControl ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: DispatchControl proc uses esi edi pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP local hSection:HANDLE local oa:OBJECT_ATTRIBUTES local pSectionBaseAddress:PVOID local liViewSize:LARGE_INTEGER invoke DbgPrint, $CTA0("\nSharedSection: Entering DispatchControl\n") mov esi, pIrp assume esi:ptr _IRP mov [esi].IoStatus.Status, STATUS_UNSUCCESSFUL and [esi].IoStatus.Information, 0 IoGetCurrentIrpStackLocation esi mov edi, eax assume edi:ptr IO_STACK_LOCATION .if [edi].Parameters.DeviceIoControl.IoControlCode == IOCTL_SHARE_MY_SECTION invoke DbgPrint, $CTA0("SharedSection: Opening section object\n") lea ecx, oa InitializeObjectAttributes ecx, offset g_usSectionName, OBJ_CASE_INSENSITIVE, NULL, NULL invoke ZwOpenSection, addr hSection, SECTION_MAP_WRITE + SECTION_MAP_READ, addr oa .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("SharedSection: Section object opened\n") and pSectionBaseAddress, NULL and liViewSize.HighPart, 0 and liViewSize.LowPart, 0 invoke ZwMapViewOfSection, hSection, NtCurrentProcess, addr pSectionBaseAddress, 0, \ SECTION_SIZE, NULL, addr liViewSize, ViewShare, 0, PAGE_READWRITE .if eax == STATUS_SUCCESS invoke DbgPrint, \ $CTA0("SharedSection: Section mapped at address %08X\n"), pSectionBaseAddress _try invoke _strrev, pSectionBaseAddress mov [esi].IoStatus.Status, STATUS_SUCCESS invoke DbgPrint, $CTA0("SharedSection: String reversed\n") _finally invoke ZwUnmapViewOfSection, NtCurrentProcess, pSectionBaseAddress invoke DbgPrint, $CTA0("SharedSection: Section at address %08X unmapped \n"), \ pSectionBaseAddress .else invoke DbgPrint, \ $CTA0("SharedSection: Couldn't map view of section. Status: %08X\n"), eax .endif invoke ZwClose, hSection invoke DbgPrint, $CTA0("SharedSection: Section object handle closed\n") .else invoke DbgPrint, $CTA0("SharedSection: Couldn't open section. Status: %08X\n"), eax .endif .else mov [esi].IoStatus.Status, STATUS_INVALID_DEVICE_REQUEST .endif push [esi].IoStatus.Status assume edi:nothing assume esi:nothing fastcall IofCompleteRequest, esi, IO_NO_INCREMENT invoke DbgPrint, $CTA0("SharedSection: Leaving DispatchControl\n") pop eax ret DispatchControl endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; DriverUnload ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: DriverUnload proc pDriverObject:PDRIVER_OBJECT invoke IoDeleteSymbolicLink, addr g_usSymbolicLinkName mov eax, pDriverObject invoke IoDeleteDevice, (DRIVER_OBJECT PTR [eax]).DeviceObject ret DriverUnload endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; В Ы Г Р У Ж А Е М Ы Й П Р И Н Е О Б Х О Д И М О С Т И К О Д ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .code INIT ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; DriverEntry ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING local status:NTSTATUS local pDeviceObject:PDEVICE_OBJECT mov status, STATUS_DEVICE_CONFIGURATION_ERROR invoke IoCreateDevice, pDriverObject, 0, addr g_usDeviceName, FILE_DEVICE_UNKNOWN, \ 0, TRUE, addr pDeviceObject .if eax == STATUS_SUCCESS invoke IoCreateSymbolicLink, addr g_usSymbolicLinkName, addr g_usDeviceName .if eax == STATUS_SUCCESS mov eax, pDriverObject assume eax:ptr DRIVER_OBJECT mov [eax].MajorFunction[IRP_MJ_CREATE*(sizeof PVOID)], offset DispatchCreateClose mov [eax].MajorFunction[IRP_MJ_CLOSE*(sizeof PVOID)], offset DispatchCreateClose mov [eax].MajorFunction[IRP_MJ_DEVICE_CONTROL*(sizeof PVOID)], offset DispatchControl mov [eax].DriverUnload, offset DriverUnload assume eax:nothing mov status, STATUS_SUCCESS .else invoke IoDeleteDevice, pDeviceObject .endif .endif mov eax, status ret DriverEntry endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: end DriverEntry :make set drv=SharedSection \masm32\bin\ml /nologo /c /coff %drv%.bat \masm32\bin\link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native /ignore:4078 %drv%.obj del %drv%.obj move %drv%.sys .. echo. pauseПри работе с разделяемыми ресурсами, в общем случае, требуется позаботиться о синхронизации, чтобы пишущий и читающий потоки не могли иметь одновременного доступа к разделяемому ресурсу. В данном же случае поток всего один и синхронизации не требуется.
Код (Text):
lea ecx, oa InitializeObjectAttributes ecx, offset g_usSectionName, OBJ_CASE_INSENSITIVE, NULL, NULL invoke ZwOpenSection, addr hSection, SECTION_MAP_WRITE + SECTION_MAP_READ, addr oaПолучив управляющий код IOCTL_SHARE_MY_SECTION, драйвер пытается открыть объект раздел с именем определенным в переменной g_usSectionName (см. common.inc).
Код (Text):
.if eax == STATUS_SUCCESS and pSectionBaseAddress, NULL and liViewSize.HighPart, 0 and liViewSize.LowPart, 0 invoke ZwMapViewOfSection, hSection, NtCurrentProcess, addr pSectionBaseAddress, 0, SECTION_SIZE, \ NULL, addr liViewSize, ViewShare, 0, PAGE_READWRITE .if eax == STATUS_SUCCESSЕсли описатель раздела получен, проецируем его представление. Вряд ли Вы найдете здесь какие-либо отличия от аналогичной части в программе управления. Но…
После вызова ZwMapViewOfSection переменная pSectionBaseAddress будет содержать значение из диапазона пользовательских адресов, а не адрес в области ядра, как Вы, возможно, ожидали. Это принципиальный момент, т.к. обращаться по этому адресу можно будет только в контексте того процесса, в адресное пространство которого отображен раздел. Поскольку драйвер SharedSection одноуровневый (а Вы помните, что обработка IRP типа IRP_MJ_DEVICE_CONTROL проходит в таком драйвере в контексте потока инициировавшего эту операцию), то мы находимся в контексте нашей программы управления.
Виртуальный адрес представления также будет отличаться от адреса представления сделанного в программе управления, но физические страницы, на которые отображён раздел будут совпадать. В нашем случае страница одна и на ней "лежит" перевернутая строка.
Код (Text):
_try invoke _strrev, pSectionBaseAddress mov [esi].IoStatus.Status, STATUS_SUCCESS _finallyУстановив SEH-фрейм, вызываем функцию _strrev, которая переворачивает строку. Теперь её можно будет легко прочитать.
Код (Text):
invoke ZwUnmapViewOfSection, NtCurrentProcess, pSectionBaseAddress .endif invoke ZwClose, hSection .endifОсвобождаем занятые ресурсы. В программе управления функция DeviceIoControl вернет код успеха и на экране отобразится восстановленная строка.
Рис. 8-1. Результат работы программы SharedSection.exe
Исходный код драйверов в архиве.
© Four-F
Драйверы режима ядра: Часть 8: Базовая техника: Работа с памятью. Совместно используемый раздел
Дата публикации 13 ноя 2003