您的位置:首页 > 职场人生

应用层与驱动层共享内存应用实例

2010-10-12 10:23 218 查看
在前一个例子SharedSection中,我们共享内存区通讯。这个驱动紧紧关联到用户模式进程的地址空间,也就是驱动所用的虚拟地址在进程空间地址中。这个例子中我们用的这个方法,没有这个缺点,对于驱动来说这个方法更适合。

9.1 SharingMemory驱动的源码

首先,驱动的功能。

;@echo off

;goto make

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

;

; SharingMemory - How to share memory between kernel-mode driver and its user-mode client

;

; This method is applicable only for highest-level or monolithic driver

; because of while processing IRP such driver's type is in the context

; of the requested user process which address space driver maps the memory buffer into.

;

; Written by Four-F (four-f@mail.ru)

;

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.386

.model flat, stdcall

option casemap:none

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; I N C L U D E F I L E S

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

include \masm32\include\w2k\ntstatus.inc

include \masm32\include\w2k\ntddk.inc

include \masm32\include\w2k\ntoskrnl.inc

include \masm32\include\w2k\hal.inc

includelib \masm32\lib\w2k\ntoskrnl.lib

includelib \masm32\lib\w2k\hal.lib

include \masm32\Macros\Strings.mac

include ..\common.inc

include seh0.inc

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; C O N S T A N T S

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.const

CCOUNTED_UNICODE_STRING "\\Device\\SharingMemory", g_usDeviceName, 4

CCOUNTED_UNICODE_STRING "\\DosDevices\\SharingMemory", g_usSymbolicLinkName, 4

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; U N I N I T I A L I Z E D D A T A

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.data?

g_pSharedMemory PVOID ?

g_pMdl PVOID ?

g_pUserAddress PVOID ?

g_fTimerStarted BOOL ?

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; N O N D I S C A R D A B L E C O D E

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.code

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; UpdateTime

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

UpdateTime proc

; This routine is called from TimerRoutine at IRQL DISPATCH_LEVEL !

; The routine itself and all memory it touches must be in nonpaged memory.

; The memory pointed by g_pSharedMemory and the driver's code (except INIT or PAGED sections)

; is in nonpaged memory. KeQuerySystemTime and ExSystemTimeToLocalTime can be called at any IRQL.

; So, no problem here.

local SysTime:LARGE_INTEGER

invoke KeQuerySystemTime, addr SysTime

invoke ExSystemTimeToLocalTime, addr SysTime, g_pSharedMemory

ret

UpdateTime endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; TimerRoutine

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

TimerRoutine proc pDeviceObject:PDEVICE_OBJECT, pContext:PVOID

; This routine is called at IRQL DISPATCH_LEVEL !

invoke UpdateTime

ret

TimerRoutine endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; Cleanup

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Cleanup proc pDeviceObject:PDEVICE_OBJECT

.if g_fTimerStarted

invoke IoStopTimer, pDeviceObject

invoke DbgPrint, $CTA0("SharingMemory: Timer stopped\n")

.endif

.if ( g_pUserAddress != NULL ) && ( g_pMdl != NULL )

; If the call to MmMapLockedPages or MmMapLockedPagesSpecifyCache specified user mode,

; the caller must be in the context of the original process before calling MmUnmapLockedPages.

; Cleanup routine is called either from DispatchCleanup or DispatchControl.

; So we always in appropriate process context.

invoke MmUnmapLockedPages, g_pUserAddress, g_pMdl

invoke DbgPrint, $CTA0("SharingMemory: Memory at address %08X unmapped\n"), g_pUserAddress

and g_pUserAddress, NULL

.endif

.if g_pMdl != NULL

invoke IoFreeMdl, g_pMdl

invoke DbgPrint, $CTA0("SharingMemory: MDL at address %08X freed\n"), g_pMdl

and g_pMdl, NULL

.endif

.if g_pSharedMemory != NULL

invoke ExFreePool, g_pSharedMemory

invoke DbgPrint, $CTA0("SharingMemory: Memory at address %08X released\n"), g_pSharedMemory

and g_pSharedMemory, NULL

.endif

ret

Cleanup endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; DispatchCleanup

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

DispatchCleanup proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP

; We MUST unmap the memory mapped into the user process before it exits

; It's better to do it as early as possible.

; The driver recieves IRP_MJ_CLEANUP while user mode app just calls CloseHandle.

invoke DbgPrint, $CTA0("\nSharingMemory: Entering DispatchCleanup\n")

invoke Cleanup, pDeviceObject

mov eax, pIrp

mov (_IRP PTR [eax]).IoStatus.Status, STATUS_SUCCESS

and (_IRP PTR [eax]).IoStatus.Information, 0

fastcall IofCompleteRequest, pIrp, IO_NO_INCREMENT

invoke DbgPrint, $CTA0("SharingMemory: Leaving DispatchCleanup\n")

mov eax, STATUS_SUCCESS

ret

DispatchCleanup endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; 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 dwContext:DWORD

invoke DbgPrint, $CTA0("\nSharingMemory: 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_GIVE_ME_YOUR_MEMORY

.if [edi].Parameters.DeviceIoControl.OutputBufferLength >= sizeof PVOID

invoke ExAllocatePool, NonPagedPool, PAGE_SIZE

.if eax != NULL

mov g_pSharedMemory, eax

invoke DbgPrint, \

$CTA0("SharingMemory: %X bytes of nonpaged memory allocated at address %08X\n"), \

PAGE_SIZE, g_pSharedMemory

; The memory g_pSharedMemory points to contains garbage

; because of the memory allocated in kernel doesn't zeroed out

; So, if you want to do some string operations in such buffer

; it may be better to fill it with the zeroes before.

; In this example it's not required

invoke IoAllocateMdl, g_pSharedMemory, PAGE_SIZE, FALSE, FALSE, NULL

.if eax != NULL

mov g_pMdl, eax

invoke DbgPrint, \

$CTA0("SharingMemory: MDL allocated at address %08X\n"), g_pMdl

invoke MmBuildMdlForNonPagedPool, g_pMdl

; If AccessMode is UserMode and the specified pages cannot be mapped,

; the routine raises an exception. Callers that specify UserMode

; must wrap the call to MmMapLockedPagesSpecifyCache in a try/except block.

_try

; Under NT4 use MmMapLockedPages instead of MmMapLockedPagesSpecifyCache

; invoke MmMapLockedPages, g_pMdl, UserMode

invoke MmMapLockedPagesSpecifyCache, g_pMdl, UserMode, MmCached, \

NULL, FALSE, NormalPagePriority

.if eax != NULL

mov g_pUserAddress, eax

invoke DbgPrint, \

$CTA0("SharingMemory: Memory mapped into user space at address %08X\n"), g_pUserAddress

mov eax, [esi].AssociatedIrp.SystemBuffer

push g_pUserAddress

pop dword ptr [eax]

invoke UpdateTime

invoke IoInitializeTimer, pDeviceObject, TimerRoutine, addr dwContext

.if eax == STATUS_SUCCESS

; Our TimerRoutine routine will be called once per second.

invoke IoStartTimer, pDeviceObject

inc g_fTimerStarted

invoke DbgPrint, $CTA0("SharingMemory: Timer started\n")

mov [esi].IoStatus.Information, sizeof PVOID

mov [esi].IoStatus.Status, STATUS_SUCCESS

.endif

.endif

_finally

.endif

.endif

.else

mov [esi].IoStatus.Status, STATUS_BUFFER_TOO_SMALL

.endif

.else

mov [esi].IoStatus.Status, STATUS_INVALID_DEVICE_REQUEST

.endif

assume edi:nothing

; If something went wrong do cleanup

.if [esi].IoStatus.Status != STATUS_SUCCESS

invoke DbgPrint, $CTA0("SharingMemory: Something went wrong\:\n")

invoke Cleanup, pDeviceObject

.endif

fastcall IofCompleteRequest, esi, IO_NO_INCREMENT

invoke DbgPrint, $CTA0("SharingMemory: Leaving DispatchControl\n")

mov eax, [esi].IoStatus.Status

assume esi:nothing

ret

DispatchControl endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; DriverUnload

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

DriverUnload proc pDriverObject:PDRIVER_OBJECT

invoke IoDeleteSymbolicLink, addr g_usSymbolicLinkName

mov eax, pDriverObject

invoke IoDeleteDevice, (DRIVER_OBJECT PTR [eax]).DeviceObject

ret

DriverUnload endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; D I S C A R D A B L E C O D E

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.code INIT

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; DriverEntry

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING

local status:NTSTATUS

local pDeviceObject:PDEVICE_OBJECT

mov status, STATUS_DEVICE_CONFIGURATION_ERROR

; Explicity initialize global variables

and g_pSharedMemory, NULL

and g_pMdl, NULL

and g_pUserAddress, NULL

and g_fTimerStarted, FALSE

; Create exclusive device

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_CLEANUP*(sizeof PVOID)], offset DispatchCleanup

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=SharingMemory

\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

9.1.1 DriverEntry例程

mov [eax].MajorFunction[IRP_MJ_CREATE*(sizeof PVOID)], offset DispatchCreateClose mov [eax].MajorFunction[IRP_MJ_CLEANUP*(sizeof PVOID)], offset DispatchCleanup mov [eax].MajorFunction[IRP_MJ_CLOSE*(sizeof PVOID)], offset DispatchCreateClose mov [eax].MajorFunction[IRP_MJ_DEVICE_CONTROL*(sizeof PVOID)], offset DispatchControl除了通常的请求IRP_MJ_CREATE, IRP_MJ_CLOSE IRP_MJ_DEVICE_CONTROL外,还处理IRP_MJ_CLEANUP请求。当一个用户模式代码调用CloseHandle,驱动初始化例程发送IRP_MJ_CLEANUP请求,表明驱动将要关闭。然后收到IRP_MJ_CLOSE请求,驱动才真正关闭。在这个例子中,为了尽可能快的释放资源,因此发送IRP_MJ_CLEANUP并处理。

9.1.2 DispatchControl例程

invoke ExAllocatePool, NonPagedPool, PAGE_SIZE .if eax != NULL mov g_pSharedMemory, eax 收到IOCTL_GIVE_ME_YOUR_MEMORY控制代码,分配一页大小的内存,当请求收到的时候驱动会把这一页空间映射到驱动管理程序地址空间。为什么必须使用非分页内存而且这页内存在用户模式进程地址空间中是可见的。

不管驱动的当前环境ExAllocatePool返回系统范围内存地址,可以在进程地址空间范围内访问共享的内存。当驱动处理IRP_MJ_DEVICE_CONTROL请求在驱动管理程序的地址空间中。在映射合适的内存页前需要MDL(Memory Descriptor List。我不知道如何把他翻译为俄语。注:原作者是俄国人)。

9.1.3 Memory Descriptor List

MDL是个结构用来描述物理内存页区域。

MDL STRUCT Next PVOID ? _Size SWORD ? MdlFlags SWORD ? Process PVOID ? MappedSystemVa PVOID ? StartVa PVOID ? ByteCount DWORD ? ByteOffset DWORD ?MDL ENDSPMDL typedef PTR MDL特别指出MDL结构的头部是指向内存页数组的双字,每一个代表物理内存页的编号(page frame number, PFN)。MDL描述的虚拟地址空间是连续的而他所占的物理页是随机分布的。这就是为什么所需要的页数组包含在整个物理内存页区域的页组成的链表。还需要组成直接内存访问(Direct Memory Access, DMA),因此,MDL包含所有内存控制必须的信息。在这个例子中只有一页。

invoke IoAllocateMdl, g_pSharedMemory, PAGE_SIZE, FALSE, FALSE, NULL .if eax != NULL mov g_pMdl, eax前两个参数分别标识IoAllocateMdl函数分配的虚拟地址和想要创建MDL内存块的大小。如果没有涉及到IRP MDL(这个例子中就是这样),第三个参数是FALSE,第四个参数说明是否必须降低进程限额并且驱动出于驱动链的顶层,或者出于中间层(这个例子中是这样)。系统为每个进程分配限额资源。当一个进程分配一个资源,限额降低。如果限额为零,相关的资源不在有效。我们不想让进程分配内存是降低限额,因此第四个参数的值为FALSE。最后一个参数标识与MDL相关的IRP请求指针。例如I/O控制器为客户缓冲创建MDL并且地址发送到IRP.MdlAddress。我们没有用到I/O操作控制MDL,所以IRP指针为空,最后一个参数的值为NULL。

IoAllocateMdl函数为MDL分配内存并且初始化他的标题。

invoke MmBuildMdlForNonPagedPool, g_pMdlMmBuildMdlForNonPagedPool函数填充一组空的物理页并且跟新MDL头部。

_try如果在用户模式调用MmMapLockedPagesSpecifyCache函数失败会发生系统异常(DDK中有明确的解释),通过SHE来处理这个异常。

invoke MmMapLockedPagesSpecifyCache, g_pMdl, UserMode, MmCached, \ NULL, FALSE, NormalPagePriority映射内存,在用户驱动管理程序中描述MDL。第一个参数标识内存映射MDL的区域。第二个参数说明内存应用在用户模式还是内核模式。第三个参数标识内存缓冲的类型。如果第四个参数为空系统会自动确定一个虚拟地址在用户地址空间中。第五个参数定义当系统突然不能对请求进行安全处理是是否产生蓝屏(BSOD),只有在第二个参数选折内核模式才行。然而这个参数为FALSE,因为我们不想在任何情况下破坏系统。最后一个参数说明MmMapLockedPagesSpecifyCache函数成功返回的重要性。

在Windows NT4中没有MmMapLockedPagesSpecifyCache函数,而是用MmMapLockedPages函数代替。

invoke MmMapLockedPages, g_pMdl, UserModeMmMapLockedPages函数在后面版本的windows中很少用,被这个MmMapLockedPagesSpecifyCache函数取代。但是后面四个参数是不可用的。根据MDL的帮助文件地址空间规定映射仅一个块,在非分页内存中(到现在我还不知道如何利用他在分页内存中)。这就是我们为什么需要非分页内存的第一个原因。

映射不能小于一页,因此,我们需要一整页,实际上只用了一些字节。

.if eax != NULL mov g_pUserAddress, eax mov eax, [esi].AssociatedIrp.SystemBuffer push g_pUserAddress pop dword ptr [eax]MmMapLockedPagesSpecifyCache从用户范围内返回地址,打印这个页,传送给驱动管理程序。因此从现在起这页内存变为共享内存。驱动无论在什么进程环境中都可以访问他,而用户进程将提供给他地址。

invoke UpdateTimeUpdateTime例程将把当前系统时间通知到共享内存上。

UpdateTime proc local SysTime:LARGE_INTEGER invoke KeQuerySystemTime, addr SysTime invoke ExSystemTimeToLocalTime, addr SysTime, g_pSharedMemory ret UpdateTime endpKeQuerySystemTime函数告诉系统时间。ExSystemTimeToLocalTime将当前时间转换为当地时间。

invoke IoInitializeTimer, pDeviceObject, TimerRoutine, addr dwContext初始化定时器,驱动根据定时器来控制设备。DEVICE_OBJECT 时钟结构,是一个指针指向IO_TIMER结构。IoInitializeTimer函数的第一个参数标识设备对象相连的时钟,第二个参数指向一个时钟回调函数,TimerRoutine例程将要在共享页面上跟新系统时间,TimerRoutine执行在IRQL = DISPATCH_LEVEL级别(DDK中有清楚的描述)。这就是需要用非分页内存的第二个也是主要的原因。这个函数的最后一个参数指向一个额外数据,是TimerRoutine的索引,我们并不需要额外数据所以这个变量是虚构的。

.if eax == STATUS_SUCCESS invoke IoStartTimer, pDeviceObject inc g_fTimerStarted定时器开始,每秒TimerRoutine例程会被调用一次,间隔值没变。

.if [esi].IoStatus.Status != STATUS_SUCCESS invoke Cleanup, pDeviceObject .endif如果状态返回错误,释放分配的资源。

9.1.4Cleanup 例程

Cleanup proc pDeviceObject:PDEVICE_OBJECT .if g_fTimerStarted invoke IoStopTimer, pDeviceObject .endif .if ( g_pUserAddress != NULL ) && ( g_pMdl != NULL ) invoke MmUnmapLockedPages, g_pUserAddress, g_pMdl and g_pUserAddress, NULL .endif .if g_pMdl != NULL invoke IoFreeMdl, g_pMdl and g_pMdl, NULL .endif .if g_pSharedMemory != NULL invoke ExFreePool, g_pSharedMemory and g_pSharedMemory, NULL .endif ret Cleanup endp不用做跟多的解释,将会释放所有的资源。在定义的内存映射空间,MmUnmapLockedPages函数的翻转操作在特定的进程地址空间中是很合适的。

9.2 SharingMemory驱动管理程序源码

;@echo off

;goto make

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

;

; SharingMemory.asm

;

; Client of SharingMemory.sys driver

;

; Written by Four-F (four-f@mail.ru)

;

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.386

.model flat, stdcall

option casemap:none

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; I N C L U D E F I L E S

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

include \masm32\include\windows.inc

include \masm32\include\kernel32.inc

include \masm32\include\user32.inc

include \masm32\include\advapi32.inc

includelib \masm32\lib\kernel32.lib

includelib \masm32\lib\user32.lib

includelib \masm32\lib\advapi32.lib

include \masm32\include\winioctl.inc

include \masm32\Macros\Strings.mac

include ..\common.inc

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; E Q U A T E S

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

IDD_MAIN equ 1000

IDC_TIME equ 1001

IDI_ICON equ 1002

TIMER_ID equ 100

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; C O N S T A N T S

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.const

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; I N I T I A L I Z E D D A T A

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.data

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; U N I N I T I A L I Z E D D A T A

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.data?

g_hDevice HANDLE ?

g_hInstance HINSTANCE ?

g_hDlg HWND ?

g_pSharedMemory LPVOID ?

g_hSCManager HANDLE ?

g_hService HANDLE ?

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; C O D E

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.code

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; MyUnhandledExceptionFilter

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

MyUnhandledExceptionFilter proc

; Just cleanup every possible thing

local _ss:SERVICE_STATUS

invoke KillTimer, g_hDlg, TIMER_ID

; The most important thing here is CloseHandle

; If something went wrong we must close device handle

; to let the driver know it should unmap memory.

; The driver should do it before application exits

; otherwise the system may crash!

; So, in driver we unmap memory by processing IRP_MJ_CLEANUP not IRP_MJ_CLOSE

; because of IRP_MJ_CLEANUP is processed before CloseHandle exits.

invoke CloseHandle, g_hDevice

invoke ControlService, g_hService, SERVICE_CONTROL_STOP, addr _ss

invoke DeleteService, g_hService

invoke CloseServiceHandle, g_hService

invoke CloseServiceHandle, g_hSCManager

mov eax, EXCEPTION_EXECUTE_HANDLER

ret

MyUnhandledExceptionFilter endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; UpdateTime

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

UpdateTime proc

local stime:SYSTEMTIME

local buffer[64]:CHAR

.if g_pSharedMemory != NULL

; It can't be zero but who cares...

invoke FileTimeToSystemTime, g_pSharedMemory, addr stime

movzx eax, stime.wHour

movzx ecx, stime.wMinute

movzx edx, stime.wSecond

invoke wsprintf, addr buffer, $CTA0("%02d:%02d:%02d"), eax, ecx, edx

invoke SetDlgItemText, g_hDlg, IDC_TIME, addr buffer

.endif

ret

UpdateTime endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; D I A L O G P R O C E D U R E

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

DlgProc proc uses esi edi hDlg:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

mov eax, uMsg

.if eax == WM_TIMER

invoke UpdateTime

.elseif eax == WM_INITDIALOG

push hDlg

pop g_hDlg

invoke LoadIcon, g_hInstance, IDI_ICON

invoke SendMessage, hDlg, WM_SETICON, ICON_BIG, eax

invoke SetWindowText, hDlg, $CTA0("Kernel Timer")

invoke UpdateTime

invoke SetTimer, hDlg, TIMER_ID, 1000, NULL

.elseif eax == WM_COMMAND

mov eax, wParam

.if ax == IDCANCEL

invoke EndDialog, hDlg, 0

.endif

.elseif eax == WM_DESTROY

invoke KillTimer, hDlg, TIMER_ID

.else

xor eax, eax

ret

.endif

xor eax, eax

inc eax

ret

DlgProc endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

; start

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

start proc uses esi edi

local acModulePath[MAX_PATH]:CHAR

local _ss:SERVICE_STATUS

local dwBytesReturned:DWORD

; explicity set for sure

and g_pSharedMemory, NULL

; The very first thing we have to do is to install exception handler

invoke SetUnhandledExceptionFilter, MyUnhandledExceptionFilter

invoke OpenSCManager, NULL, NULL, SC_MANAGER_ALL_ACCESS

.if eax != NULL

mov g_hSCManager, eax

push eax

invoke GetFullPathName, $CTA0("SharingMemory.sys"), sizeof acModulePath, addr acModulePath, esp

pop eax

invoke CreateService, g_hSCManager, $CTA0("SharingMemory"), $CTA0("Another way how to share memory"), \

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 g_hService, eax

invoke StartService, g_hService, 0, NULL

.if eax != 0

invoke CreateFile, $CTA0("\\\\.\\SharingMemory"), GENERIC_READ, \

0, NULL, OPEN_EXISTING, 0, NULL

.if eax != INVALID_HANDLE_VALUE

mov g_hDevice, eax ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

invoke DeviceIoControl, g_hDevice, IOCTL_GIVE_ME_YOUR_MEMORY, NULL, 0, \

addr g_pSharedMemory, sizeof g_pSharedMemory, \

addr dwBytesReturned, NULL

.if ( eax != 0 ) && ( dwBytesReturned == sizeof g_pSharedMemory )

; Here g_pSharedMemory contains the pointer

; to mapped by the driver memory buffer

invoke GetModuleHandle, NULL

mov g_hInstance, eax

invoke DialogBoxParam, g_hInstance, IDD_MAIN, NULL, addr DlgProc, 0

.else

invoke MessageBox, NULL, $CTA0("Can't send control code to device."), \

NULL, MB_OK + MB_ICONSTOP

.endif

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

invoke CloseHandle, g_hDevice

.else

invoke MessageBox, NULL, $CTA0("Device is not present."), NULL, MB_ICONSTOP

.endif

invoke ControlService, g_hService, SERVICE_CONTROL_STOP, addr _ss

.else

invoke MessageBox, NULL, $CTA0("Can't start driver."), NULL, MB_OK + MB_ICONSTOP

.endif

invoke DeleteService, g_hService

invoke CloseServiceHandle, g_hService

.else

invoke MessageBox, NULL, $CTA0("Can't register driver."), NULL, MB_OK + MB_ICONSTOP

.endif

invoke CloseServiceHandle, g_hSCManager

.else

invoke MessageBox, NULL, $CTA0("Can't connect to Service Control Manager."), NULL, MB_OK + MB_ICONSTOP

.endif

invoke ExitProcess, 0

start endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

;

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

end start

:make

set exe=SharingMemory

if exist ..\%scp%.exe del ..\%scp%.exe

if exist rsrc.obj goto final

\masm32\bin\rc /v rsrc.rc

\masm32\bin\cvtres /machine:ix86 rsrc.res

if errorlevel 0 goto final

pause

exit

:final

if exist rsrc.res del rsrc.res

\masm32\bin\ml /nologo /c /coff %exe%.bat

\masm32\bin\link /nologo /subsystem:windows %exe%.obj rsrc.obj

del %exe%.obj

move %exe%.exe ..

if exist %exe%.exe del %exe%.exe

echo.

pause

为了处理出现的异常,每个过程都注册自机的SHE处理函数。如果没有定义自己的异常处理函数系统会默认弹出一个错误对话框并调用调试器。调用这个SetUnhandledExceptionFilter函数用自己的异常处理函数取代系统默认的异常处理。

invoke SetUnhandledExceptionFilter, MyUnhandledExceptionFilter

因此,实际上在这个例子中如果出现任何资源异常将会执行释放资源,稍后看MyUnhandledExceptionFilter异常处理函数。

invoke DeviceIoControl, g_hDevice, IOCTL_GIVE_ME_YOUR_MEMORY, NULL, 0, \ addr g_pSharedMemory, sizeof g_pSharedMemory, \ addr dwBytesReturned, NULL如果传给驱动IOCTL_GIVE_ME_YOUR_MEMORY控制代码正确执行,驱动会返回给用户共享内存地址到变量g_pSharedMemory,这个例子中我们不用考虑共享内存大小,因为他明显大于我们的所需要的。前8个字节就是驱动每秒跟新的时间。

.if ( eax != 0 ) && ( dwBytesReturned == sizeof g_pSharedMemory ) invoke GetModuleHandle, NULL mov g_hInstance, eax invoke DialogBoxParam, g_hInstance, IDD_MAIN, NULL, addr DlgProc, 0创建对话框,

.elseif eax == WM_INITDIALOG . . . invoke UpdateTime invoke SetTimer, hDlg, TIMER_ID, 1000, NULL在WM_INITDIALOG消息中调用UpdateTime函数,是为了确保对话框出现后显示的是当前时间,然后设置时钟定时器,每秒触发一次。

.if eax == WM_TIMER invoke UpdateTime在WM_TIMER消息中调用UpdateTime函数跟新时间。

UpdateTime proc local stime:SYSTEMTIMElocal buffer[64]:CHAR .if g_pSharedMemory != NULL invoke FileTimeToSystemTime, g_pSharedMemory, addr stime movzx eax, stime.wHour movzx ecx, stime.wMinute movzx edx, stime.wSecond invoke wsprintf, addr buffer, $CTA0("%02d:%02d:%02d"), eax, ecx, edx invoke SetDlgItemText, g_hDlg, IDC_TIME, addr buffer .endif ret UpdateTime endp这是时间格式化例程,将当前时间转换为Hours: Minutes: Second格式显示。

驱动每隔一秒把当前时间存入共享内存中,涉及到的虚拟地址在系统地址空间中,驱动管理程序每隔一秒从用户模式地址空间中获得时间信息。没有一个共享的物理内存页。KeQuerySystemTime函数获得当前系统时间在用户模式和内核模式之间共享,共享物理页在内核模式地址在0FFDF0000h,用户模式地址在7FFE0000h,用户模式函数GetSystemTime和内核模式函数获得的时间是相同的类型。KUSER_SHARED_DATA结构的标题显示内核模式和用户模式共享数据。

MyUnhandledExceptionFilter proc lpExceptionInfo:PTR EXCEPTION_POINTERS local _ss:SERVICE_STATUS invoke KillTimer, g_hDlg, TIMER_ID invoke CloseHandle, g_hDevice invoke ControlService, g_hService, SERVICE_CONTROL_STOP, addr _ss invoke DeleteService, g_hService invoke CloseServiceHandle, g_hService invoke CloseServiceHandle, g_hSCManager mov eax, EXCEPTION_EXECUTE_HANDLER ret MyUnhandledExceptionFilter endp如果在程序中任何地方出现异常系统就会调用我们定义的MyUnhandledExceptionFilter异常处理函数处理。我们要做的就是释放所有的资源,最重要的是关闭描述符设备。驱动将会依次收到IRP_MJ_CLEANUP 和 IRP_MJ_CLOSE控制代码,并执行释放,最重要的是解除用户地址空间中内存映射。实际上,甚至可以不用异常处理,因为当程序崩溃系统会关闭所有的描述符包括设备描述符。当收到IRP_MJ_CLEANUP请求系统会尽快的释放资源。在这个例子中当收到IRP_MJ_CLEANUP请求就执行这样的操作。在任何情况下,在进程停止前必须调用MmUnmapLockedPages函数。

与前一个共享内存区的例子相比,这个例子中有两个流操作共享内存资源,因此需要考虑同步问题。读操作在用户模式中,意味着IRQL = PASSIVE_LEVEL,写操作在系统进程中进行并调用TimerRoutine例程,并且我们已经调用IoInitializeTimer进行初始化。在IRQL=DISPATCH_LEVEL级别下调用TimerRoutine例程(DDK中有清楚说明),在任何情况下运行在空闲进程(idle process)中,因为他的执行权限低于用户权限,当他从共享页中读数据是不能挂起驱动管理程序,由于当IRQL = DISPATCH_LEVEL,用户可以中断将当前时间写入共享内存的操作。因此出问题时单处理器机器定时器不能被唤醒,在多处理器机器上能够同步执行这些操作,因此在这样的情况下必须考虑同步问题。在这个例子中我们并没有考虑,因为这是我们后面的文章中要写的主题。幸运的是大多数环境下对话框会显示出时间。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息