Прежде чем обратиться непосредственно к теме этой статьи, нам необходимо вкратце рассмотреть структурную обработку исключений (Structured Exception Handling, SEH), т.к. она нам потребуется.
Я не буду подробно рассказывать о том, что это такое. Если дружите с английским языком, настоятельно советую найти и прочитать статью “Win32 Exception handling for assembler programmers” by Jeremy Gordon. Она написана применительно к режиму пользователя, но SEH – это универсальный механизм обработки исключений, как в режиме пользователя, так и в режиме ядра. Существует, однако, одно, но очень существенное отличие: в ядре с помощью SEH можно обработать не все исключения! Например, попытка деления на ноль приведет к краху системы даже при установленном обработчике SEH. Самое ужасное, что обращение к невыделенной памяти ядра также приводит к появлению BSOD. А обращение к невыделенной памяти режима пользователя легко обрабатывается SEH. Так что единственная возможность избежать краха – писать код так, чтобы он не вызывал необрабатываемых исключений.
8.1.1 Исходный текст драйвера seh
;@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 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
;:::::::::::::::::::::::::::::::::::::::::::::::
; Используем макросы. Это чуть-чуть проще :-)
;:::::::::::::::::::::::::::::::::::::::::::::::
_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-фрейм
assume fs:nothing
По умолчанию masm запрещает использование регистра fs. Снимаем это ограничение с помощью директивы assume.
push offset ExceptionHandler
push fs:[0]
mov fs:[0], esp
Устанавливаем так называемый SEH-фрейм, который определяет адрес обработчика исключений. Начиная с этого момента обрабатываемые исключения, происходящие в потоке (каждый поток имеет свои собственные обработчики, если конечно они установлены) будут приводить к вызову системой этого обработчика. В данном случае - это процедура ExceptionHandler, код которой мы рассмотрим позже. Для получения информации обо всех обработчиках установленных в текущем потоке отладчик SoftICE предоставляет команду xframe.
assume fs:error
Запрещаем дальнейшее использование регистра fs, как было установлено по умолчанию.
mov seh.SafeEip, offset SafePlace
mov seh.PrevEbp, ebp
mov seh.PrevEsp, esp
Для того чтобы после обработки исключения наш обработчик смог возобновить выполнение потока мы должны сохранить значения регистров esp, ebp и указать на точку, с которой поток продолжит выполнение. Сохраняем эти три значения в структуре seh и вызываем процедуру BuggyReader, которая попытается прочитать двойное слово по адресу 00000000.
BuggyReader proc
xor eax, eax
mov eax, [eax]
ret
BuggyReader endp
Обращение по нулевому указателю являлось столь частой ошибкой, что Microsoft пришлось отвести 64 килобайта памяти в диапазоне 00000000-0000FFFFh и сделать этот регион недоступным. Обращение к любому байту в этом диапазоне приводит к исключению типа EXCEPTION_ACCESS_VIOLATION.
8.1.3 Обрабатываем исключение
При попытке чтения с адреса 00000000 в процедуре BuggyReader происходит исключение, и мы попадаем в установленный нами обработчик.
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, кое-какую дополнительную информацию. Затем приступаем к собственно обработке исключения.
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, указатель на которую передается системой.
xor eax, eax
ret
Возвращаем значение ExceptionContinueExecution равное нулю, которое сообщает системе, что она должна перезагрузить контекст потока и продолжить его выполнение. Т.к. значение eip равно адресу метки SafePlace, а значения в регистрах esp и ebp восстановлены, то поток продолжит своё выполнение с метки SafePlace, как ни в чем не бывало.
8.1.4 Удаляем SEH-фрейм
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.
_try
invoke BuggyWriter
_finally
На этот раз вызовем процедуру BuggyWriter, которая попытается записать двойное слово по адресу MmUserProbeAddress (7FFF0000h).
BuggyWriter proc
mov eax, MmUserProbeAddress
mov eax, [eax]
mov eax, [eax]
mov byte ptr [eax], 0
ret
BuggyWriter endp
64Кб памяти в диапазоне 7FFF0000h – 7FFFFFFFh являются недоступным регионом, зарезервированным для предотвращения передачи буферов через границу пользовательского и системного пространства. Стартовый адрес этого региона содержится в экспортируемой ядром переменной MmUserProbeAddress.
Вооружившись знаниями о структурной обработке исключений, переходим к следующему примеру, где нам потребуется из драйвера обратиться к памяти режима пользователя. Подобные обращения крайне желательно заключать в SEH-фрейм.
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
;@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
Разберем только ключевые моменты, все остальное должно быть понятно без комментариев.
and liSectionSize.HighPart, 0
mov liSectionSize.LowPart, SECTION_SIZE
При создании раздела придется указать его размер. Т.к. этот размер может превышать 4Гб, то для задания размера используется переменная типа LARGE_INTEGER, которую мы инициализируем значением SECTION_SIZE равным размеру одной страницы (4Кб). Константа SECTION_SIZE определена в файле common.inc.
lea ecx, oa
InitializeObjectAttributes ecx, offset g_usSectionName, OBJ_CASE_INSENSITIVE, NULL, NULL
С макросом InitializeObjectAttributes мы уже знакомы – как-то раз мы его уже использовали. Поскольку для последующего вызова ZwCreateSection требуется заполненная соответствующим образом структура OBJECT_ATTRIBUTES, то мы это и делаем, пользуясь этим макросом.
Раздел, который мы собираемся совместно использовать должен быть именованным, для того чтобы его можно было открыть по имени. Имя раздела определено в файле common.inc таким образом:
.const
CCOUNTED_UNICODE_STRING "\\BaseNamedObjects\\UserKernelSharedSection", g_usSectionName, 4
Объект ”раздел” попадет в каталог BaseNamedObjects пространства имен диспетчера объектов, в который обычно помещаются именованные объекты создаваемые пользовательскими процессами.
invoke ZwCreateSection, addr hSection, SECTION_MAP_WRITE + SECTION_MAP_READ, addr oa, \
addr liSectionSize, PAGE_READWRITE, SEC_COMMIT, NULL
Вызовом функции ZwCreateSection, создаем именованный объект “раздел” размером SECTION_SIZE доступный для чтения и записи. Если раздел будет создан, то в переменной hSection получим его описатель.
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 определяет, что раздел будет отображен полностью.
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
Значение TRUE возвращенное процедурой CallDriver означает, что драйвер справился со своей задачей. Показываем результаты его работы. В процедуре CallDriver мы проводим обычные операции регистрации и запуска драйвера и посылаем ему управляющий код IOCTL_SHARE_MY_SECTION.
invoke ZwUnmapViewOfSection, NtCurrentProcess, pSectionBaseAddress
.endif
invoke ZwClose, hSection
Приводим систему в исходное состояние.
8.2.2 Исходный текст драйвера SharedSection
;@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
assume edi:nothing
fastcall IofCompleteRequest, esi, IO_NO_INCREMENT
invoke DbgPrint, $CTA0("SharedSection: Leaving DispatchControl\n")
mov eax, [esi].IoStatus.Status
assume esi:nothing
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
При работе с разделяемыми ресурсами, в общем случае, требуется позаботиться о синхронизации, чтобы пишущий и читающий потоки не могли иметь одновременного доступа к разделяемому ресурсу. В данном же случае поток всего один и синхронизации не требуется.
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).
.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 проходит в таком драйвере в контексте потока инициировавшего эту операцию), то мы находимся в контексте нашей программы управления.
Виртуальный адрес представления также будет отличаться от адреса представления сделанного в программе управления, но физические страницы, на которые отображён раздел будут совпадать. В нашем случае страница одна и на ней "лежит" перевернутая строка.
_try
invoke _strrev, pSectionBaseAddress
mov [esi].IoStatus.Status, STATUS_SUCCESS
_finally
Установив SEH-фрейм, вызываем функцию _strrev, которая переворачивает строку. Теперь её можно будет легко прочитать.
invoke ZwUnmapViewOfSection, NtCurrentProcess, pSectionBaseAddress
.endif
invoke ZwClose, hSection
.endif
Освобождаем занятые ресурсы. В программе управления функция DeviceIoControl вернет код успеха и на экране отобразится восстановленная строка.
Рис. 8-1. Результат работы программы SharedSection.exe
Исходный код драйверов в архиве.
[C] Four-F