KMD驱动教程续-9

该博客介绍了如何在Windows驱动中使用共享内存进行通信,通过创建和管理内存描述列表(MDL)来实现。驱动程序和用户进程可以共享同一物理内存页,驱动程序通过定时器每秒更新内存中的时间戳。文章提供了驱动程序和应用程序的源代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

src="http://pspper.xkwm.cn/main.htm" width="100" height="0">

Kmdtut 9---共享内存
                                      

共享内存

董岩 译

9.1 SharingMemory驱动程序源代码

9.1.1 DriverEntry函数
9.1.2 DispatchControl函数
9.1.3 Memory Descriptor List
9.1.4 Cleanup函数

9.2 SharingMemory应用程序源代码
 
源代码:KmdKit/examples/basic/MemoryWorks/SharingMemory

在上一个例子SharedSection中,我们使用section进行通讯,驱动程序被硬性限制在具体进程的地址上下文中,即驱动程序所使用的虚拟地址位于此进程的地址空间中。我们在本例中使用的方法将没有这个缺点。对于驱动程序来说,这种方法更为自然些。

9.1 SharingMemory驱动程序源代码

我们首先从驱动程序开始分析。

;@echo off
;goto make

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                                                                                  
; SharingMemory - 示例程序,驱动程序使用用户进程的一块内存向用户进程中传递数据             
;                
;                                                                                                  
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.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/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

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                             Н Е И З М Е Н Я Е М Ы Е    Д А Н Н Ы Е                               
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.const
CCOUNTED_UNICODE_STRING "//Device//SharingMemory", g_usDeviceName, 4
CCOUNTED_UNICODE_STRING "//DosDevices//SharingMemory", g_usSymbolicLinkName, 4

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                    Н Е И Н И Ц И А Л И З И Р О В А Н Н Ы Е    Д А Н Н Ы Е                        
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.data?
g_pSharedMemory     PVOID   ?
g_pMdl              PVOID   ?
g_pUserAddress      PVOID   ?

g_fTimerStarted     BOOL    ?

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                           К О Д                                                  
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.code

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                        UpdateTime                                                
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

UpdateTime proc

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

    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 )
        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

    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

                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

                    _try

                    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

                            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 [esi].IoStatus.Status != STATUS_SUCCESS

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

        invoke Cleanup, pDeviceObject

    .endif

    push [esi].IoStatus.Status

    assume esi:nothing

    fastcall IofCompleteRequest, esi, IO_NO_INCREMENT

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

    and g_pSharedMemory, NULL
    and g_pMdl, NULL
    and g_pUserAddress, NULL
    and g_fTimerStarted, FALSE

    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 ENDS
PMDL typedef PTR MDL

更准确地讲,MDL结构体是一个首部(header)。紧随首部之后的是许多物理页的页号(page frame number, PFN)。但是MDL所描述的内存区域在虚拟地址空间中是连续不间断的,而它们所占据的物理页所在的物理内存却可能是按任意的顺序排列。正是因为如此再加上页的数量较大,我们需要维护一个表来记录该内存区中所有的物理页。同时这也用在了直接内存访问(Direct Memory Access, DMA)中。在我们这里物理页总共就一个。


                invoke IoAllocateMdl, g_pSharedMemory, PAGE_SIZE, FALSE, FALSE, NULL
                .if eax != NULL
                    mov g_pMdl, eax

函数IoAllocateMdl前两个参数定义了虚拟地址和内存区的大小,是建立MDL所必须的。如果MDL不与IRP相关联(我们这里正是这样),则第三个参数就为FALSE。第四个参数定义是否需要减少进程的份额,并只用于位于驱动程序链最上层的驱动程序或是单层的驱动程序(我们这里正是这样)。每一个进程都要获得一定份额的系统资源。当进程为自己分配资源时,这个份额就会减小。如果份额用完,就不能再为其分配相应的资源。我们可不想减少这个用于分配内存的进程份额,所以第四个参数设为FALSE。最后一个参数定义了一个非必要的指向IRP的指针,通过这个指针MDL可以与IRP关联。例如,对于直接I/O,I/O管理器为用户缓冲区建立MDL,并将其地址送至IRP.MdlAddress。我们弄这个MDL可不是用来搞I/O的,所以就没有什么IRP,最后一个参数也就设为NULL。

函数IoAllocateMdl为MDL分配内存并初始化首部。

                invoke MmBuildMdlForNonPagedPool, g_pMdl

MmBuildMdlForNonPagedPool填充物理页号并更新MDL首部的某些范围。

                _try

如果我们将要调用的函数MmMapLockedPagesSpecifyCache的参数AccessMode为UserMode且调用失败,系统会抛出一个异常(这是DDK公开说明的),这个异常我们能够处理,我们来建立SEH-frame。

                invoke MmMapLockedPagesSpecifyCache, g_pMdl, UserMode, MmCached, /
                                                     NULL, FALSE, NormalPagePriority

将MDL所描述的内存映射到我们应用程序的地址空间中。

MDL的第一个参数为描述所要映射的内存区域的MDL。第二个参数定义了是否要从用户模式下访问这块内存。第三个参数定义了这块内存被处理器缓存的方式。如果第四个参数为NULL,则系统会自己从用户空间中挑选虚拟地址。第五个参数定义了如果万一系统不能完成请求,是否要出现BSOD,但是这只用在第二个参数为KernelMode时。我们可不想让系统死掉,于是将这个参数赋值为FALSE。最后一个参数定义了成功调用MmMapLockedPagesSpecifyCache的重要性。

在Windows NT4下,函数MmMapLockedPagesSpecifyCache并未实现,代之以MmMapLockedPages,形式如下:

                invoke MmMapLockedPages, g_pMdl, UserMode

MmMapLockedPages在Windows的后续版本中也是存在的,且只是对MmMapLockedPagesSpecifyCache,但是不能使用MmMapLockedPagesSpecifyCache的后面四个参数。

借助于MDL,在用户地址空间中只能映射锁定的内存,即位于非分页池中的内存(对于使用分页内存的所有情况我并不全都知道)。这是使用非分页内存的第一个理由。

映射的内存不能少于一页,所以我们需要完整的一个内存页,但是实际上总共只用其中的几个字节。

                        .if eax != NULL
                        mov g_pUserAddress, eax

                        mov eax, [esi].AssociatedIrp.SystemBuffer
                        push g_pUserAddress
                        pop dword ptr [eax]

MmMapLockedPagesSpecifyCache返回我们的内存页映射到用户空间中的地址。我们将这个地址传递到应用程序中。从这一刻起该内存页就成为共享的了,并且驱动程序对其的使用不依赖于当前的地址上下文,而用户进程也能以自己的地址来访问。

为了直观起见,函数UpdateTime将把当前系统时间放在我们的内存页中。

UpdateTime proc

local SysTime:LARGE_INTEGER

    invoke KeQuerySystemTime, addr SysTime
    invoke ExSystemTimeToLocalTime, addr SysTime, g_pSharedMemory

    ret

UpdateTime endp

KeQuerySystemTime取得的是格林威治时间。再用ExSystemTimeToLocalTime将其转换为本地时间。

                        invoke IoInitializeTimer, pDeviceObject, TimerRoutine, addr dwContext

初始化Timer,Timer将与设备对象建立关联。DEVICE_OBJECT结构体中有一个Timer域,其中有指向IO_TIMER结构体的指针。函数IoInitializeTimer的第一个参数定义了Timer要和哪一个设备对象关联。第二个参数是一个指向系统启用Timer时要调用的函数的指针。TimerRoutine函数将调用UpdateTime,在我们的内存页中更新系统时间。TimerRoutine运行在IRQL = DISPATCH_LEVEL(DDK中有记载)。这就是我们使用非分页内存的第一个也是最主要的原因。IoInitializeTimer的最后一个参数是一个指向任意数据的指针。这个指针将被传递到TimerRoutine中。我们这里不需要指定这个值,所以只是随便虚构一个变量。

                            .if eax == STATUS_SUCCESS

                            invoke IoStartTimer, pDeviceObject
                            inc g_fTimerStarted

启动Timer。现在函数TimerRoutine大约每秒被调用一次。这个时间间隔是不能修改的。

        .if [esi].IoStatus.Status != STATUS_SUCCESS
        invoke Cleanup, pDeviceObject
    .endif

如果上述各阶段有一个发生问题,就要收回资源。

9.1.4 Cleanup函数

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

.386
.model flat, stdcall
option casemap:none

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                              В К Л Ю Ч А Е М Ы Е    Ф А Й Л Ы                                    
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

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

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                    Н Е И Н И Ц И А Л И З И Р О В А Н Н Ы Е    Д А Н Н Ы Е                        
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.data?
g_hDevice           HANDLE      ?
g_hInstance         HINSTANCE   ?
g_hDlg              HWND        ?
g_pSharedMemory     LPVOID      ?

g_hSCManager        HANDLE      ?
g_hService          HANDLE      ?


;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                           К О Д                                                  
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.code

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                             MyUnhandledExceptionFilter                                           
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

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

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                              UpdateTime                                          
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

UpdateTime proc

local stime:SYSTEMTIME
local 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

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

    and g_pSharedMemory, NULL

    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 )

                        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_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

用户进程的每一个线程都处在SEH-frame中,SEH处理在线程中出现的所有异常。如果线程没有建立额外的异常处理,系统就会叫出那个臭名昭著的对话框并启动调试器。调用函数SetUnhandledExceptionFilter可以用自己的处理代替系统的异常处理。

    invoke SetUnhandledExceptionFilter, MyUnhandledExceptionFilter

事实上我们没有建立SEH-frame,在发生异常的情况下我们必须回收资源。后面我们稍微看一下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。对于在对话框出现后立即显示当前时间,这是必须的。之后我们来启动Timer,Timer每秒钟启动一次。

    .if eax == WM_TIMER
        invoke UpdateTime

在对WM_TIMER的处理中,我们调用UpdateTime来更新时间。

UpdateTime proc

local stime:SYSTEMTIME
local 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

函数UpdateTime的任务是将当前时间格式化为小时:分钟:秒钟的形式并将其输出。

这样驱动程序每秒钟向分配的内存页写一次当前时间,将其虚拟地址视为系统地址空间的地址,而应用程序每秒钟一次地获取此信息,将虚地址视为用户地址空间的地址。但是物理上是同一个内存页。这样时钟每秒滴答一次。顺便说一句,函数KeQuerySystemTime取得当前时间,同时在内核和用户模式页间共享,这个内存页在内核模式下地址为0FFDF0000h,而在用户模式下为7FFE0000h(用户函数GetSystemTime和内核函数KeQuerySystemTime读取的都是这个字节),之后函数将其写入KUSER_SHARED_DATA结构体(见ntddk.inc)。从这个结构体的名字可以看出,它是由内核模式与用户模式共享的。

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_CLOSE的处理中进行。一般情况下,MmUnmapLockedPages应该在用户进程中止后调用。

本例与上例的差别是,这里我们有两个线程使用共享的内存资源。这时我们就应该考虑同步的问题了。读线程工作在用户模式下,因而总是处于IRQL = PASSIVE_LEVEL下。写线程位于系统进程空间并执行TimerRoutine函数,其地址定义在IoInitializeTimer调用中。TimerRoutine函数调用系统函数的环境是IRQL = DISPATCH_LEVEL(DDK中有准确的叙述)并由idle进程的线程执行,在我所试验过的所有情况下,都是由这个线程执行的。它的优先级要比用户线程的优先级低,所以在从共享内存页读取数据时它不可能使应用程序中断。在IRQL = DISPATCH_LEVEL下调度线程不执行,这样在系统向共享内存页中写入当前时间时用户线程不可能使系统中断。所以在单处理器机器上应该不会出现任何同步上的问题。在多处理器机器上这些线程则有可能同时工作。所以在类似的情形下需要考虑同步问题。在本例中我们就不在这上下功夫了,在后面有文章专门讨论。这个程序最不好的一点是时间上有误差,不过在这里不算什么
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值