Kmdtut 8---共享Section通讯
共享Section通讯
董岩译
8.1 结构化异常处理
8.1.1 seh驱动程序源代码
8.1.2 建立 SEH-frame
8.1.3 异常处理
8.1.4 卸载SEH-frame
8.1.5 使用宏来建立/卸载SEH-frame
8.2 共享Section通讯
8.2.1 SharedSection应用程序源代码
8.2.2 SharedSection驱动程序源代码
源代码: KmdKit/examples/basic/MemoryWorks/SharedSection
在进入本章主题之前,先来简单地看一下结构化异常处理((Structured Exception Handling, SEH),本章的程序需要这个东东。
8.1 结构化异常处理
我并不打算详细讲结构化异常处理到底是个什么东东。如果您英语够好的话,强烈推荐您找来Jeremy Gordon的《Win32 Exception handling for assembler programmers》来好好读一读。该文所写的内容适用于用户模式,但是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
;:::::::::::::::::::::::::::::::::::::::::::::::
; 使用宏安装seh. 这个简单点儿 :-)
;:::::::::::::::::::::::::::::::::::::::::::::::
_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-frame
assume fs:nothing
小道消息说,masm禁止使用fs寄存器。我们来用伪指令assume去掉此限制。
push offset ExceptionHandler
push fs:[0]
mov fs:[0], esp
我们这里所建立的这个东东叫SEH-frame,用来定义异常处理程序的地址。从此刻开始,对于在线程中(每个线程都有自己私有的处理程序,当然如果它们建立了的话)发生的异常处理,系统将调用此处理程序。这里,这个处理程序是ExceptionHandler,其代码我们后面会分析。要获取当前线程所有异常处理的信息,可以在SoftICE调试器中使用命令xframe。
assume fs:error
根据小道消息,我们不再使用fs寄存器。
mov seh.SafeEip, offset SafePlace
mov seh.PrevEbp, ebp
mov seh.PrevEsp, esp
为了在异常处理之后我们的处理程序能恢复线程的执行,我们应该保存esp、ebp寄存器的内容以及线程继续执行的地址。我们将这三项信息保存在seh结构体中并调用函数BuggyReader。BuggyReader函数试图从地址00000000读取一个DWORD。
BuggyReader proc
xor eax, eax
mov eax, [eax]
ret
BuggyReader endp
NULL指针引用是一个十分常见的错误,微软在00000000-0000FFFFh划出了64k字节的内存区,并使此区域无法访问。访问此区域中的任何一个字节都会导致EXCEPTION_ACCESS_VIOLATION类型的异常。
8.1.3 异常处理
函数BuggyReader从地址00000000读取引发了异常,我们就进入了我们指定的处理程序。
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结构体中,CONTEXT结构体的指针又传递给了系统。
xor eax, eax
ret
我们以ExceptionContinueExecution的值等于零而返回,该值为零就是告诉系统应该恢复线程的上下文并继续执行。即eip的值等于标记SafePlace的地址,而esp和ebp寄存器的值恢复为原值,线程从标记SafePlace处继续其执行。
8.1.5 使用宏来建立/卸载SEH-frame
我们来使用宏来完成这个操作,这个宏是我为了简化SEH的使用而编写的。宏的定义在文件seh0.inc中。这样我以后就不用每次都写这些代码了,这些宏自己就能做,我们只是安排好这些宏就可以了。如果在异常处理中不需要什么处理程序的话,使用宏_try/_finally就把工作简化成了三行代码。标记SafePlace,在这里就是宏_finally所在的那一行。
_try
invoke BuggyWriter
_finally
这次我们调用函数BuggyWriter,此函数试图在地址MmUserProbeAddress (7FFF0000h)写入一个DWORD。
BuggyWriter proc
mov eax, MmUserProbeAddress
mov eax, [eax]
mov eax, [eax]
mov byte ptr [eax], 0
ret
BuggyWriter endp
在区域7FFF0000h – 7FFFFFFFh的64KB内存是非访问区,是用户空间与系统空间界线处的缓冲区。这个区域的起始地址保存在内核导出变量MmUserProbeAddress中。
头脑里武装上了结构化异常处理的知识后,我们来看下一个例子。在这个例子中我们要从驱动程序中转到用户模式内存里。这个转换最好能包含在SEH-frame里。
8.2 内存共享
Windows提供了许多机制来进行进程间通讯(Interprocess Communications, IPC):通讯缓冲、DDE、通讯窗口(WM_COPYDATA就在这里)、mailslot、sockets等等。所有这些机制都是基于文件映射对象(file-mapping object)的,该对象本身是一块两个或多个进程可以访问的内存区,用DDK的术语,映射文件就是section对象,不要把它和PE文件中的section混淆起来。
section对象是最底层的通讯机制,这种对象被系统用来将可执行映象加载到内存,而缓存调度程序用它来访问缓存文件中的数据。section对象还能将磁盘上的文件映射到进程的地址空间中,而且用起来不像是在用文件,而是在用内存块。
借助于section对象来共享数据的情形如下:一个进程调用函数CreateFileMapping创建了一个内存映射文件。之后调用函数MapViewOfFile(如果层次更低就调用NtMapViewOfSection)将其视图(view)映射到自己的地址空间中,而另一个进程通过OpenFileMapping打开这个映射文件,并将其映射到自己的地址空间中。结果同一组物理内存页变为由两个进程访问,这就使得它们能通过这个区域轻松地传递较大量的数据,一个进程对这些页内容的修改会反映到另一个进程中。
共享section这种通讯方法不止可以用在用户进程间,还可以用在驱动程序里。在下面的例子里我们用命名section来在用户进程和驱动程序之间进行通讯。
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
建立section需要指明其大小,对于大小值使用LARGE_INTEGER类型的变量的程序来说,这个值可以超过4GB。我们将这个值初始化为一个内存页的大小即4KB。SECTION_SIZE常量定义在common.inc文件里。
lea ecx, oa
InitializeObjectAttributes ecx, offset g_usSectionName, OBJ_CASE_INSENSITIVE, NULL, NULL
对于宏InitializeObjectAttributes我们已经很熟悉了。后面的ZwCreateSection调用需要填充好的OBJECT_ATTRIBUTES结构体,填充工作我们用这些宏来完成。
我们准备使用的section应该取个名字,这样就可以用名字来打开它。section的名字定义在文件common.inc中,形式如下:
.const
CCOUNTED_UNICODE_STRING "//BaseNamedObjects//UserKernelSharedSection", g_usSectionName, 4
section对象在对象管理器名字空间的BaseNamedObjects目录下,用户进程创建的命名对象一般都在这个目录下。
invoke ZwCreateSection, addr hSection, SECTION_MAP_WRITE + SECTION_MAP_READ, addr oa, /
调用函数ZwCreateSection来创建命名section对象,其大小为SECTION_SIZE,访问方式为可读写。如果section创建成功,则可以从变量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
这样就将section的所有的视图都映射到内存中。这个函数的参数够多的——所有的参数在DDK中都有详细介绍。参数pSectionBaseAddress初始化为NULL,为更方便她映射section,系统自己定义了映射section的虚拟地址,并将这个地址返给此参数。参数liViewSize初始化为零,这就定义了section将被全部映射。
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
CallDriver的返回值为TRUE表示驱动程序已成功完成自己的任务。我们来检查一下其工作的结果。在函数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,驱动程序尝试打开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
如果取得了section的句柄,我们就来映射它的视图。这里与应用程序中的映射视图部分几乎完全相同。但是……
在调用了ZwMapViewOfSection之后,变量pSectionBaseAddress保存的值位于用户地址区域中,而不是核心区域中的地址,这点我们可以预料到。这可是个原则上的问题,即对这个地址的使用将只能在这个进程的上下文中,在映射section的地址空间中。由于SharedSection驱动程序是单层的(您还记得IRP_MJ_DEVICE_CONTROL类型的IRP处理要经过驱动程序进入发出该操作的线程上下文中),所以我们位于我们的应用程序的上下文中。
这里视图的虚拟地址与应用程序中视图的地址将有所不同,但共享section所在的物理页是同一个。我们这里有一个内存页,其中还保存着倒着写的一行文字。
_try
invoke _strrev, pSectionBaseAddress
mov [esi].IoStatus.Status, STATUS_SUCCESS
_finally
建立SEH-frame,调用函数_strrev将文字转反。现在可以轻松的读懂了:
A memory-mapped file backed by the paging file is a common technique used for sharing memory among user processes. However you can use the same technique to share memory between a user process and a device driver.