Kernel Mode Drivers

Part 8: Basic Technique: Working with Memory

Shared section



Before turning to the subject of this article, we need to consider briefly the structured exception handling (Structured Exception Handling, SEH), as we need it.


8.1 Structured Exception Handling

I will not go into detail about what it is. If you are friends with the English language, I highly recommend finding and reading the article “Win32 Exception handling for assembler programmers” by Jeremy Gordon. It is written for user mode, but SEH is a generic exception handling mechanism for both user and kernel mode. There is, however, one but very significant difference: in the kernel, not all exceptions can be handled with SEH! For example, trying to divide by zero will crash the system even if SEH is installed. The worst thing is that accessing unallocated kernel memory also leads to BSODs. And access to unallocated user mode memory is easily handled by SEH. So the only way to avoid a crash is to write your code so that it doesn't throw unhandled exceptions.

8.1.1 Seh driver source


 ;@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 Setting up the SEH frame


     assume fs:nothing

By default, masm disables the use of the fs register. We remove this restriction using the assume directive.


     push offset ExceptionHandler
     push fs:[0]
     mov fs:[0], esp

We set up the so-called SEH frame, which defines the address of the exception handler. From now on, handled exceptions occurring in a thread (each thread has its own handlers, if installed) will cause the system to call this handler. In this case, it is the ExceptionHandler procedure , the code of which we will look at later. The SoftICE debugger provides the xframe command to get information about all handlers installed on the current thread .


     assume fs:error

We prohibit further use of the fs register, as it was set by default.


     mov seh.SafeEip, offset SafePlace
     mov seh.PrevEbp, ebp
     mov seh.PrevEsp, esp

In order for our handler to be able to resume the execution of the thread after handling the exception, we must save the values ​​of the esp, ebp registers and point to the point from which the thread will continue execution. We store these three values ​​in the seh structure and call the BuggyReader procedure , which will try to read the doubleword at address 00000000.


 BuggyReader proc

     xor eax, eax
     mov eax, [eax]

     ret

 BuggyReader endp

Accessing a null pointer was such a common mistake that Microsoft had to allocate 64 kilobytes of memory in the 00000000-0000FFFFh range and make this region inaccessible. Accessing any byte in this range results in an EXCEPTION_ACCESS_VIOLATION exception.


8.1.3 Handling the Exception

When we try to read from the address 00000000 in the BuggyReader procedure , an exception occurs, and we get into the handler we have installed.


     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

First of all, our handler displays the appropriate debug message and, if an exception of the EXCEPTION_ACCESS_VIOLATION type occurs, some additional information. Then we proceed to the actual handling of the exception.


     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

In this example, handling the exception consists in simply restoring the values ​​in the esp, ebp registers and placing the address in the eip register from which it is safe to continue executing the thread. The handler extracts this data from the seh structure we have previously filled in and places it in the CONTEXT structure, the pointer to which is passed by the system.


     xor eax, eax
     ret

We return the ExceptionContinueExecution value equal to zero, which tells the system that it should reload the thread context and continue its execution. Because the eip value is equal to the address of the SafePlace label, and the values ​​in the esp and ebp registers are restored, then the stream will continue its execution from the SafePlace label, as if nothing had happened.


8.1.4 Delete SEH frame


 SafePlace:

     assume fs:nothing
     pop fs:[0]
     add esp, sizeof DWORD
     assume fs:error

Restore the old value in fs: [0] and adjust the stack pointer (see the above article for details).


8.1.5 Using macros to set / delete an SEH frame

Let's do the same operation using macros that I wrote to simplify the work with SEH. The macros are defined in the seh0.inc file. I will not describe them, because they do the same thing that we just examined. If no special handling is required in the exception handler, using the _try / _finally macros will reduce the entire code above to three lines. The point of SafePlace, in this case, will be the line containing the macro _finally.


     _try

     invoke BuggyWriter

     _finally

This time, let's call the BuggyWriter procedure , which will try to write the double word to MmUserProbeAddress (7FFF0000h).


 BuggyWriter proc


     mov eax, MmUserProbeAddress
     mov eax, [eax]
     mov eax, [eax]
    
     mov byte ptr [eax], 0
     ret

 BuggyWriter endp

64KB of memory in the range 7FFF0000h - 7FFFFFFFh is an inaccessible region reserved to prevent buffers from being passed across the user-system-space boundary. The starting address for this region is contained in the kernel exported variable MmUserProbeAddress.

Armed with the knowledge of structured exception handling, we move on to the next example, where we need to access user mode memory from the driver. It is highly desirable to enclose such calls in an SEH frame.


8.2 Using a section for data exchange

Windows provides the richest set of mechanisms for exchanging data between two or more processes (Interprocess Communications, IPC): clipboard , DDE, window messages (in particular WM_COPYDATA), mailboxes (mailslot), sockets , etc. All these mechanisms, anyway, based on the object "file view" (file-mapping object), which is a block of memory available to two or more processes for sharing. In the terminology of DDK files are projected objects "section" (section called object), colloquially called a section (not to be confused with the PE-file sections).

The section object is the lowest-level data sharing mechanism. This same object is used by the system to load executable images into memory, and the cache manager uses it to access data in cached files. The partition object also allows you to project files on the disk into the address spaces of processes and work with them not as with files, but as with blocks of memory.

Sharing data using a section object works like this: One process creates a memory-mapped file by calling the CreateFileMapping function . Then, a function call MapViewOfFile (if you go one level deeper, it is called NtMapViewOfSection ), displays his presentation (view) to its address space, and the other through a process of OpenFileMappingopens the same displayed file and displays it in the same way, but in its own address space. As a result, the same physical pages of memory become available in both processes, which allows them to easily transfer large chunks of data through this area. any changes to these pages in one process are reflected in the presentation in the other process.

The shared object "section" can serve as a means of communication not only for user processes, but also for drivers. Or, as in the following example, a named section will be used to communicate between the user process and the driver.


8.2.1 The source code of the SharedSection driver control program


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

We will analyze only the key points, everything else should be clear without comments.


     and liSectionSize.HighPart, 0
     mov liSectionSize.LowPart, SECTION_SIZE

When creating a partition, you will have to specify its size. Because Since this size can exceed 4GB, then a variable of the LARGE_INTEGER type is used to set the size, which we initialize with the SECTION_SIZE value equal to the size of one page (4Kb). The SECTION_SIZE constant is defined in the common.inc file.


     lea ecx, oa
     InitializeObjectAttributes ecx, offset g_usSectionName, OBJ_CASE_INSENSITIVE, NULL, NULL

We are already familiar with the InitializeObjectAttributes macro. Since the subsequent call to ZwCreateSection requires a properly filled OBJECT_ATTRIBUTES structure, we do this using this macro.

The section we are going to share must be named so that it can be opened by name. The section name is defined in the common.inc file as follows:


 .const
 CCOUNTED_UNICODE_STRING	"\\BaseNamedObjects\\UserKernelSharedSection", g_usSectionName, 4

The "section" object will go into the BaseNamedObjects directory of the object manager namespace, which usually contains named objects created by user processes.


     invoke ZwCreateSection, addr hSection, SECTION_MAP_WRITE + SECTION_MAP_READ, addr oa, \
                             addr liSectionSize, PAGE_READWRITE, SEC_COMMIT, NULL

By calling the ZwCreateSection function , create a named "section" object of SECTION_SIZE size available for reading and writing. If the section is created, we will get its descriptor in the hSection variable.


         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

We display a representation of the entire section in memory. There are quite a few parameters here - they are all detailed in the DDK. Because the pSectionBaseAddress variable is initialized to zero, the system will itself determine which virtual address it is more convenient for it to display the section and will return this address in the same variable. The zero-initialized liViewSize variable specifies that the entire section will be displayed.


             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

Copy the inverted line into the resulting representation. The task of the driver will be to make this line readable.


             invoke CallDriver
             .if eax == TRUE
                 invoke MessageBox, NULL, pSectionBaseAddress, \
                                 $CTA0("HOWTO: Share Memory Between User Mode and Kernel Mode"), \
                                 MB_OK + MB_ICONINFORMATION
             .endif

A TRUE value returned by the CallDriver procedure indicates that the driver has done its job. We show the results of his work. In the CallDriver procedure , we carry out the usual operations of registering and starting the driver and send it the control code IOCTL_SHARE_MY_SECTION.


             invoke ZwUnmapViewOfSection, NtCurrentProcess, pSectionBaseAddress
         .endif
         invoke ZwClose, hSection

We bring the system to its original state.


8.2.2 SharedSection driver source


 ;@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 esi:nothing
     assume edi: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

When working with shared resources, in general, you need to take care of synchronization so that the writer and reader cannot have simultaneous access to the shared resource. In this case, there is only one thread and no synchronization is required.


         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

Having received the IOCTL_SHARE_MY_SECTION control code, the driver tries to open the section object with the name specified in the g_usSectionName variable (see 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

If a section descriptor is obtained, project its representation. It is unlikely that you will find here any differences from the analogous part in the control program. But…

After the call to ZwMapViewOfSection, the pSectionBaseAddress variable will contain a custom address range, not a kernel -scoped address as you might expect. This is a fundamental point, since it will be possible to access this address only in the context of the process in the address space of which the section is mapped. Since the SharedSection driver is single-level (and you remember that IRP processing of the IRP_MJ_DEVICE_CONTROL type takes place in such a driver in the context of the thread that initiated this operation), we are in the context of our control program.

The virtual address of the view will also differ from the address of the view made in the control program, but the physical pages on which the section is mapped will coincide. In our case, there is only one page and an inverted line "lies" on it.


                 _try

                 invoke _strrev, pSectionBaseAddress
                 mov [esi].IoStatus.Status, STATUS_SUCCESS

                 _finally

With the SEH frame set up, we call the _strrev function , which reverses the string. Now it will be easy to read.


                 invoke ZwUnmapViewOfSection, NtCurrentProcess, pSectionBaseAddress
             .endif
             invoke ZwClose, hSection
         .endif

We release the occupied resources. In the control program, the DeviceIoControl function will return a success code and the restored string will be displayed on the screen.

Rice. 8-1. The result of the program SharedSection.exe

The source code of the drivers in the archive .