Iczelion 的 Win32Asm VxD 汇编教程 (六)

    技术2022-05-11  121

    DeviceIoControl接口

    在这一节中我们将要关于学习动态VXD,特别是如何创建,加载和使用。

    点击这里下载例子

    VxD接口

    VxD总共提供了4种接口。

    l         VxD services                    VxD服务

    l         V86 Interface                   V86接口

    l         Protected-mode (PM) Interface   保护模式接口

    l         Win32 DeviceIoControl Interface Win32设备输入输出控制接口

    我们已经知道了VxD服务,V86和保护模式接口是由V86和保护模式程序调用的。因为V86和保护模式程序是16位的,我们不能在Win32应用程序中使用那两种接口。在Windows 95中,微软给Win32应用程序加了另外一个接口所以Win32应用程序可以调用VxD的服务:DeviceIoControl接口(设备输入输出控制接口)

    DeviceIoControl接口

    简单的说,DeviceIoControl接口是一种为Win32程序准备的调用VxD内部函数的方法。不要混淆DeviceIoControl接口调用函数和用VxD服务调用函数,这两种方法是不一样的。比如说,DeviceIoControl function1 也许和Vxd service1是不一样的。你应给把DeviceIoControl函数作为一种只为Win32应用程序提供的单独的函数。

    Win32程序方面:

    首先用CreateFile来打开/加载一个VxD。如果调用成功的话,VxD将会创建/加再到内存中并且CreateFileVxD的句柄返回到eax中。

    接着你调用DeviceIoControlAPI函数来选择要运行的函数。DeviceIoControl函数遵循下面的语法:

    DeviceIoControl PROTO  hDevice:DWORD,/

                                            dwIoControlCode:DWORD,/

                                            lpInBuffer:DWORD,/

                                            nInBufferSize:DWORD,/

                                            lpOutBuffer:DWORD,/

                                            nOutBufferSize:DWORD,/

                                            lpBytesReturned:DWORD,/

                                            lpOverlapped:DWORD

    l         hDevice 是从CreateFile返回的VxD句柄。

    l         dwIoControlCode是用来制定VxD将要进行的操作。你应该在你要选用那种操作之前得到可能的dwIoControlCode值得列表。

    l         lpInBuffer是包含了VxD完成dwIoControlCode所制定操作的数据的缓冲区地址。如果这个操作不需要数据,你可以传为NULL

    l         nInBufferSize是由lpInBuffer所指向的缓冲区的地址的大小byte)。

    l         lpOutBufferVxD程序在操作成功之后要将输出数据输出到的缓冲区。如果这个操作没有任何返回值,这个值可以为NULL

    l         nOutBufferSizelpOutBuffer所指向的缓冲区的大小byte)。

    l         lpBytesReturned是一个dword型变量的地址。这个变量用来接收VxD在lpOutBuffer中写入数据的大小。

    l         如果你想要把操作设成异步的,lpOverlapped是一个OVERLAPPED结构的指针。如果你要一直等直到操作完成,这个值为NULL

    VxD方面:

    VxD程序必须处理w32_deviceIoControl消息。当VxD收到w32_deviceIoControl消息,它的寄存器是如下值:

    l         ebx 是VM的句柄。

    l         esi 是指向DIOCParams结构的指针。DIOCParams包含了从win32程序传送的信息。

    DIOCParams是按照如下定义的:

    DIOCParams STRUC

        Internal1          DD ?

        VMHandle           DD ?

        Internal2          DD ?

        dwIoControlCode    DD ?

        lpvInBuffer        DD ?

        cbInBuffer         DD ?

        lpvOutBuffer       DD ?

        cbOutBuffer        DD ?

        lpcbBytesReturned  DD ?

        lpoOverlapped      DD ?

        hDevice            DD ?

        tagProcess         DD ?

    DIOCParams ENDS

    l         Internal1       是指向Win32应用应用程序用户寄存器结构的指针。

    l         VMHandle        虚拟机句柄

    l         Internal2       是指向设备描述块(DDB)的句柄。

    l         dwIoControlCode, lpvInBuffer, cbInBuffer, lpvOutBuffer, cbOutBuffer, lpcbBytesReturned, lpOverlapped是传送到DeviceIoControl API的参数。

    l         hDevice       ring-3级设备句柄。

    l         tagProces       是过程的标签。

    DIOCParams结构中有所有从Win32应用程序传送到你的VxD的信息。

    你的VxD至少要处理DIOC_Open(传送到dwIoControlCode),那是当Win32程序调用CreateFile打开你的VxD时VWIN32发送给你的VxD的。如果你的VxD准备好了,它必须在eax中返回0而且CreateFile也会成功。如果你的VxD没有准备好,它必须在eas中返回一个非零值而且CreateFile也会失败。除了DIOC_Open,当Win32程序关闭这个设备句柄时,你的VxD将会从VWIN32收到DIOC_Closehandle

    能由CreateFile加载的最小的动态VxD框架:

    .386p include vmm.inc include vwin32.inc

    DECLARE_VIRTUAL_DEVICE DYNAVXD,1,0, DYNAVXD_Control,/      UNDEFINED_DEVICE_ID, UNDEFINED_INIT_ORDER

    Begin_control_dispatch DYNAVXD     Control_Dispatch w32_DeviceIoControl, OnDeviceIoControl End_control_dispatch DYNAVXD

    VxD_PAGEABLE_CODE_SEG BeginProc OnDeviceIoControl     assume esi:ptr DIOCParams     .if [esi].dwIoControlCode==DIOC_Open         xor eax,eax     .endif     ret EndProc OnDeviceIoControl VxD_PAGEABLE_CODE_ENDS

    end

    ;-------------------------------------------------------------------------------------------------------------------------------- ;   Module Definition File ;---------------------------------------------------------------------------------------------------------------------------------

    VXD DYNAVXD DYNAMIC

    SEGMENTS     _LPTEXT      CLASS 'LCODE'    PRELOAD NONDISCARDABLE     _LTEXT       CLASS 'LCODE'    PRELOAD NONDISCARDABLE     _LDATA       CLASS 'LCODE'    PRELOAD NONDISCARDABLE     _TEXT        CLASS 'LCODE'    PRELOAD NONDISCARDABLE     _DATA        CLASS 'LCODE'    PRELOAD NONDISCARDABLE     CONST        CLASS 'LCODE'    PRELOAD NONDISCARDABLE     _TLS         CLASS 'LCODE'    PRELOAD NONDISCARDABLE     _BSS         CLASS 'LCODE'    PRELOAD NONDISCARDABLE     _LMGTABLE    CLASS 'MCODE'    PRELOAD NONDISCARDABLE IOPL     _LMSGDATA    CLASS 'MCODE'    PRELOAD NONDISCARDABLE IOPL     _IMSGTABLE   CLASS 'MCODE'    PRELOAD DISCARDABLE IOPL     _IMSGDATA    CLASS 'MCODE'    PRELOAD DISCARDABLE IOPL     _ITEXT       CLASS 'ICODE'    DISCARDABLE     _IDATA       CLASS 'ICODE'    DISCARDABLE     _PTEXT       CLASS 'PCODE'    NONDISCARDABLE     _PMSGTABLE   CLASS 'MCODE'    NONDISCARDABLE IOPL     _PMSGDATA    CLASS 'MCODE'    NONDISCARDABLE IOPL     _PDATA       CLASS 'PDATA'    NONDISCARDABLE SHARED     _STEXT       CLASS 'SCODE'    RESIDENT     _SDATA       CLASS 'SCODE'    RESIDENT     _DBOSTART    CLASS 'DBOCODE'  PRELOAD NONDISCARDABLE CONFORMING     _DBOCODE     CLASS 'DBOCODE'  PRELOAD NONDISCARDABLE CONFORMING     _DBODATA     CLASS 'DBOCODE'  PRELOAD NONDISCARDABLE CONFORMING     _16ICODE     CLASS '16ICODE'  PRELOAD DISCARDABLE     _RCODE       CLASS 'RCODE'

    EXPORTS     DYNAVXD_DDB  @1

    完整例子:

    下面是一段加载动态VxD并且通过DeviceIoControl API 来调用VxD内部函数的Win32应用程序的源代码。

    ; VxDLoader.asm

    .386 .model flat,stdcall include windows.inc include kernel32.inc includelib kernel32.lib include user32.inc includelib user32.lib

    .data     AppName db "DeviceIoControl",0     VxDName db "//./shellmsg.vxd",0     Success db "The VxD is successfully loaded!",0     Failure db "The VxD is not loaded!",0     Unload db "The VxD is now unloaded!",0     MsgTitle db "DeviceIoControl Example",0     MsgText db "I'm called from a VxD!",0     InBuffer dd offset MsgTitle                   dd offset MsgText .data?     hVxD dd ? .code start:     invoke CreateFile,addr VxDName,0,0,0,0,FILE_FLAG_DELETE_ON_CLOSE,0     .if eax!=INVALID_HANDLE_VALUE         mov hVxD,eax         invoke MessageBox,NULL,addr Success,addr AppName,MB_OK+MB_ICONINFORMATION         invoke DeviceIoControl,hVxD,1,addr InBuffer,8,NULL,NULL,NULL,NULL         invoke CloseHandle,hVxD         invoke MessageBox,NULL,addr Unload,addr AppName,MB_OK+MB_ICONINFORMATION     .else         invoke MessageBox,NULL,addr Failure,NULL,MB_OK+MB_ICONERROR     .endif     invoke ExitProcess,NULL end start

    下面这段源代码是由 vxdloader.asm 调用的动态VxD; ShellMsg.asm

    .386p include vmm.inc include vwin32.inc include shell.inc

    DECLARE_VIRTUAL_DEVICE SHELLMSG,1,0, SHELLMSG_Control,/      UNDEFINED_DEVICE_ID, UNDEFINED_INIT_ORDER

    Begin_control_dispatch SHELLMSG     Control_Dispatch w32_DeviceIoControl, OnDeviceIoControl End_control_dispatch SHELLMSG

    VxD_PAGEABLE_DATA_SEG     pTitle dd ?     pMessage dd ? VxD_PAGEABLE_DATA_ENDS

    VxD_PAGEABLE_CODE_SEG BeginProc OnDeviceIoControl     assume esi:ptr DIOCParams     .if [esi].dwIoControlCode==DIOC_Open         xor eax,eax     .elseif [esi].dwIoControlCode==1         mov edi,[esi].lpvInBuffer         ;-----------------------------------         ; copy the message title to buffer         ;-----------------------------------         VMMCall _lstrlen, <[edi]>         inc eax         push eax         VMMCall _HeapAllocate,<eax,HEAPZEROINIT>         mov pTitle,eax         pop eax         VMMCall _lstrcpyn,<pTitle,[edi],eax>         ;-----------------------------------         ; copy the message text to buffer         ;-----------------------------------         VMMCall _lstrlen, <[edi+4]>         inc eax         push eax         VMMCall _HeapAllocate,<eax,HEAPZEROINIT>         mov pMessage,eax         pop eax         VMMCall _lstrcpyn,<pMessage,[edi+4],eax>         mov edi,pTitle         mov ecx,pMessage         mov eax,MB_OK         VMMCall Get_Sys_VM_Handle         VxDCall SHELL_sysmodal_Message         VMMCall _HeapFree,pTitle,0         VMMCall _HeapFree,pMessage,0         xor eax,eax     .endif     ret EndProc OnDeviceIoControl VxD_PAGEABLE_CODE_ENDS

    end

    分析:

    我们从VxDLoader.asm开始。

    Invoke CreateFile,addrVxDName,0,0,0,0,FILE_FLAG_DELETE_ON_CLOSE,0     .if eax!=INVALID_HANDLE_VALUE         mov hVxD,eax         ....     .else         invoke MessageBox,NULL,addr Failure,NULL,MB_OK+MB_ICONERROR     .endif

    我们调用CreateFile来加载动态VxD。注意FILE_FLAG_DELETE_ON_CLOSE标记。当从CreateFile返回的VxD句柄被关闭的时候,这个标志通知Windows卸载VxD。如果CreateFile成功,我们把VxD句柄保存起来。

    invoke MessageBox,NULL,addr Success,addr AppName,MB_OK+MB_ICONINFORMATION invoke DeviceIoControl,hVxD,1,addr InBuffer,8,NULL,NULL,NULL,NULL invoke CloseHandle,hVxD invoke MessageBox,NULL,addr Unload,addr AppName,MB_OK+MB_ICONINFORMATION

    VxD加载/卸载的时候,这个程序会显示一个消息框。它令dwIoControlCode=1然后调用DeviceIoControl。将InBuffer的地址传给lpInBuffer,将InBuffer的大小传给nInBufferSizeInBuffer是一个包括两个元素的数组:每个元素都是一个字符串的地址。

     

    MsgTitle db "DeviceIoControl Example",0     MsgText db "I'm called from a VxD!",0     InBuffer dd offset MsgTitle                   dd offset MsgText

     

    现在我们看一下这段VxD。

    它只处理w32_deviceIoControl消息。当w32_deviceIoControl消息发送的时候,调用OnDeviceIoControl函数。

     

    BeginProc OnDeviceIoControl     assume esi:ptr DIOCParams     .if [esi].dwIoControlCode==DIOC_Open         xor eax,eax

     

    OnDeviceIoControl 处理DIOC_Openeas中返回0

     

    .elseif [esi].dwIoControlCode==1         mov edi,[esi].lpvInBuffer

     

    它也处理control code 等于1。它做的第一件事是取出在lpyInBuffer中的数据。这个数据是传送到DeviceIoControl API 的lpInBuffer中的两个dword值。它把指向dword数组的地址放到edi中。第一个dword是作为消息框标题的字符串地址。第二个dword是作为消息框文本的字符串地址。

     

            ;-----------------------------------         ; copy the message title to buffer         ;-----------------------------------         VMMCall _lstrlen, <[edi]>         inc eax         push eax         VMMCall _HeapAllocate,<eax,HEAPZEROINIT>         mov pTitle,eax         pop eax         VMMCall _lstrcpyn,<pTitle,[edi],eax>

     

    它调用VMM服务lstrlen来计算消息框标题的长度lstrleneax中返回字符串的长度。我们把这个长度加1来包括结束标记NULL。下一步我们通过调用HeapAllocate来分配一块足够大可以容纳字符串和它的结束标记NULL内存。加上HEAPZEROINIT标记使HeapAllocate将这块内存清零HeapAllocateeax中返回这块内存的地址。我们然后从win32 app的地址空间把字符串拷贝到我们申请的内存中。我们对要做消息框文本的字符串做同样的操作。

     

            mov edi,pTitle         mov ecx,pMessage         mov eax,MB_OK         VMMCall Get_Sys_VM_Handle         VxDCall SHELL_sysmodal_Message

     

    我们把标题和文本的地址分别存在ediecx中。把想要的标记放在eax中,通过调用Get_Sys_VM_handle得到系统VMVM 句柄。然后调用SHELL_sysbodal_Message SHELL_sysModal_Message是系统SHELL_Message的模式版本。它冻结系统直到用户对消息框做出反应。

     

            VMMCall _HeapFree,pTitle,0         VMMCall _HeapFree,pMessage,0

     

    SHELL_sysmodal_Message返回时,我们用_HeapFree释放内存。

     

    总结:

    DeviceIoControl接口使你的win32应用程序使用动态VxD作为一个ring-0 DLL扩展非常理想。

     


    最新回复(0)