Драйверы режима ядра: Часть 8: Базовая техника: Работа с памятью. Совместно используемый раздел — Архив WASM.RU

Все статьи

Драйверы режима ядра: Часть 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


 ;@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

     ;:::::::::::::::::::::::::::::::::::::::::::::::
     ; Используем макросы. Это чуть-чуть проще :-)
     ;:::::::::::::::::::::::::::::::::::::::::::::::

     _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-фрейм.


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


 ;@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

     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

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


         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

Исходный код драйверов в архиве.

2002-2013 (c) wasm.ru