虚拟设备驱动程序结构(Vxd教程3)

[复制链接]
查看11 | 回复0 | 2011-4-11 10:41:29 | 显示全部楼层 |阅读模式
<span class=\"contentad\"></span><FONT face=Arial,Helvetica><FONT color=#ffff99>虚拟设备驱动程序结构</FONT></FONT></CENTER> 由 Ryo 翻译,发表于 <a href=\"http://asm.yeah.net\">http://asm.yeah.net</a>,英文版本来自 [<a href=\"http://win32asm.cjb.net/\">Iczelion\'s Win32 Assembly Homepage</a>] <br/><FONT face=Arial,Helvetica><FONT size=-1>现在大家对vmm和vxd有了一定的了解,接下来我们来看一看如何编写vxd代码。首先,你必须具备<B><FONT color=#66ff99>Windows 95/98 Device Driver Development Kit</FONT></B>。Window95 ddk只有MSDN 订户才能拿到,但Windows98 ddk却可以免费从Microsoft公司取得。尽管Windows 98 ddk是面向WDM的,但你还是可以用它来开发VxD程序。你可以从<A href=\"http://www.microsoft.com/hwdev/ddk/install98ddk.htm?\">http://www.microsoft.com/hwdev/ddk/install98ddk.htm?</A>下载Window98 ddk。</FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT size=-1>你可以下载整个软件包(大约30M),也可以只下载你感兴趣的部分。如果你没有下载整个软件包,那么别忘了下载<B><FONT color=#ffff99>other.exe</FONT></B></FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1>里面的Window95 ddk documentation。Windows98 ddk 包含了6.11d版的MASM。你需要把它升级为最新版。如果你不知道到哪里去下载最新的版本,可以去我的<A href=\"http://win32asm.cjb.net/\">主页</A>上查一查。</FONT></FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1>Window9x DDK包含了一些Masm32包所不具有的重要库文件。</FONT></FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1>你可以在<A href=\"http://freeshell.org/~michael/files/firstvxd.zip\">这里</A>下载这一章的例子。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT color=#66ffff><FONT size= 0>LE文件格式</FONT></FONT></FONT><FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1>VxD采用线性可执行文件格式(LE)。这种文件格式是为OS/2 2.0版设计的。它同时包含16位和32位代码,这点也是VxD程序的需要。回想VxD在Windows3.x的时代,在那时,从Dos启动Windows,Windows在把机器转到保护模式之前需要在实模式下做一些初始化。实模式的16位代码必须和32位代码一起放在可执行文件中。所以LE文件格式理所当然的选择。幸运的,Windows NT驱动程序不必在实模式下初始化,所以它们不必使用LE文件格式。它们用的是PE文件格式。</FONT></FONT></FONT><FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1></FONT></FONT></FONT>
<FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>在LE文件中,代码和数据被存放在几类运行属性不同的</FONT><B><FONT color=#ffff99>段</FONT></B><FONT color=#ffffff>中。以下是一些可用的</FONT><B><FONT color=#ffff99>段类</FONT></B><FONT color=#ffffff>。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>LCODE</FONT></B><FONT color=#ffffff>页面锁定的代码和数据段 这种段被锁定在内存里。换句话说,这段永远不会被放到硬盘上去,所以你一定要谨慎的使用这种段类以免浪费宝贵的内存。那些每时每刻都必须放在内存中的代码和数据应该放在这个段里。尤其是那些硬件中断处理程序。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>PCODE</FONT></B><FONT color=#ffffff> 可调页代码段 VMM可以对这种段实行调页处理,在这种段里的代码不必时刻放在内存里,当VMM需要物理内存的时候,它就会把这段放到硬盘上去。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>PDATA</FONT></B><FONT color=#ffffff> 可调页数据段</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>ICODE</FONT></B><FONT color=#ffffff> 仅用于的初始化段
这种段里的代码仅仅用来进行VxD的初始化。当初始化完成后,VMM就把这段从内存中释放。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>DBOCODE</FONT></B><FONT color=#ffffff> 仅用于调试的代码数据段
当你要调试VxD程序时,就要用到这种段里的代码和数据,例如,它包含要调试的消息的处理代码。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>SCODE</FONT></B><FONT color=#ffffff> 静态代码和数据段
这种段时刻存在于内存中,即使VxD已经卸载,这种段对某些动态的VxD程序很有用,这些VxD程序需要在某一Windows进程里不停的加载/卸载而又要纪录上次的环境和状态。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>RCODE</FONT></B><FONT color=#ffffff> 实模式初始化代码数据段
这种段包含实模式初始化需要的16位代码和数据。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>16ICODE</FONT></B><FONT color=#ffffff>16ICODE USE16保护模式初始化数据段
这是一个16位的段,它包含VxD要从保护模式拷贝到V86模式的代码。例如,如果你要把一些V86的代码拷贝到一个虚拟机上时,你想拷贝的代码就要放在这里。如果你把它放在其他的段里,编译程序就会产生错误的代码,例如,它会产生32位代码而不是16位代码。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>MCODE</FONT></B><FONT color=#ffffff> 锁定的消息字串
这种段包含了由VMM消息宏帮助编译的消息字串,这有助于你构造你的驱程的国际版本。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>这并不意味着你的VxD程序必须包含以上</FONT><B><FONT color=#ffff99>所有</FONT></B><FONT color=#ffffff>的段,你可以选择你的VxD程序需要的段。例如,如果你的VxD程序不进行实模式初始化,那么就不必包含</FONT><B><FONT color=#ffff99>RCODE</FONT></B><FONT color=#ffffff>段。</FONT></FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>大多数时候,你要用到</FONT><B><FONT color=#ffff99>LCODE</FONT></B><FONT color=#ffffff>, </FONT><B><FONT color=#ffff99>PCODE</FONT></B><FONT color=#ffffff>和</FONT><B><FONT color=#ffff99>PDATA</FONT></B><FONT color=#ffffff>段。作为一个VxD程序编写者,为你的代码和数据选择合适的段取决于你自己的判断。总的来说,你应该尽可能多的使用</FONT><B><FONT color=#ffff99>PCODE</FONT></B><FONT color=#ffffff>和</FONT><B><FONT color=#ffff99>PDATA</FONT></B><FONT color=#ffffff>因为这样VMM就可以在需要的时候把段调入调出内存。另外,硬件中断程序及其所用到的服务必须放在</FONT><B><FONT color=#ffff99>LCODE</FONT></B><FONT color=#ffffff>段里。</FONT></FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1>你不能直接地使用这些段类,你要用这些段类来定义段,这些段的定义被存放在模块定义文件(.def)中。下面是一个标准的模块定义文件:</FONT></FONT></FONT> <BLOCKQUOTE><TT><FONT color=#ffff99><FONT size=-1>VXD FIRSTVXD</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1>SEGMENTS</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _LPTEXT CLASS \'LCODE\' PRELOAD NONDISCARDABLE</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _LTEXT CLASS \'LCODE\' PRELOAD NONDISCARDABLE</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _LDATA CLASS \'LCODE\' PRELOAD NONDISCARDABLE</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _TEXT CLASS \'LCODE\' PRELOAD NONDISCARDABLE</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _DATA CLASS \'LCODE\' PRELOAD NONDISCARDABLE</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> CONST CLASS \'LCODE\' PRELOAD NONDISCARDABLE</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _TLS CLASS \'LCODE\' PRELOAD NONDISCARDABLE</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _BSS CLASS \'LCODE\' PRELOAD NONDISCARDABLE</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _LMGTABLE CLASS \'MCODE\' PRELOAD NONDISCARDABLE IOPL</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _LMSGDATA CLASS \'MCODE\' PRELOAD NONDISCARDABLE IOPL</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _IMSGTABLE CLASS \'MCODE\' PRELOAD DISCARDABLE IOPL</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _IMSGDATA CLASS \'MCODE\' PRELOAD DISCARDABLE IOPL</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _ITEXT CLASS \'ICODE\' DISCARDABLE</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _IDATA CLASS \'ICODE\' DISCARDABLE</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _PTEXT CLASS \'PCODE\' NONDISCARDABLE</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _PMSGTABLE CLASS \'MCODE\' NONDISCARDABLE IOPL</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _PMSGDATA CLASS \'MCODE\' NONDISCARDABLE IOPL</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _PDATA CLASS \'PDATA\' NONDISCARDABLE SHARED</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _STEXT CLASS \'SCODE\' RESIDENT</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _SDATA CLASS \'SCODE\' RESIDENT</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _DBOSTART CLASS \'DBOCODE\' PRELOAD NONDISCARDABLE CONFORMING</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _DBOCODE CLASS \'DBOCODE\' PRELOAD NONDISCARDABLE CONFORMING</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _DBODATA CLASS \'DBOCODE\' PRELOAD NONDISCARDABLE CONFORMING</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _16ICODE CLASS \'16ICODE\' PRELOAD DISCARDABLE</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> _RCODE CLASS \'RCODE\'</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1>EXPORTS</FONT></FONT></TT> <br/><TT><FONT color=#ffff99><FONT size=-1> FIRSTVXD_DDB @1</FONT></FONT></TT></BLOCKQUOTE><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>第一个声明定义了VxD的名称,一个VxD的名称</FONT><B><FONT color=#ffff99>必须</FONT></B><FONT color=#ffffff>是全部大写的,我曾经试过用小写,结果VxD除了把自己载入内存外什么也不干。</FONT></FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>接下来是段的定义,段的定义包括三个部分:段的名称,段类和要求的段的运行属性。你可以看到很多段都基于相同的段类,例如,</FONT><B><FONT color=#ffff99>_LPTEXT</FONT></B><FONT color=#ffffff>, </FONT><B><FONT color=#ffff99>_LTEXT</FONT></B><FONT color=#ffffff>, </FONT><B><FONT color=#ffff99>_LDATA</FONT></B><FONT color=#ffffff>都是基于</FONT><B><FONT color=#ffff99>LCODE</FONT></B><FONT color=#ffffff>段类而且属性也完全一样。这样定义段有利于使代码更容易理解。如:</FONT><B><FONT color=#ffff99>LCODE</FONT></B><FONT color=#ffffff>可以包含代码和数据,对于一个程序员来说,如果他能把数据放到</FONT><B><FONT color=#ffff99>_LDATA</FONT></B><FONT color=#ffffff>段里,把代码放到</FONT><B><FONT color=#ffff99>_LTEXT</FONT></B><FONT color=#ffffff> 段里,就会显得很容易理解。最后,这两个段都会被编译到最后的可执行程序的同一个段内。</FONT></FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>一个VxD程序导出且仅导出一个标记:它的设备描述块(DDB)。DDB实际上是一个结构,它包含了VMM需要知道的所有的VxD信息。你</FONT><B><FONT color=#ffff99>必须</FONT></B><FONT color=#ffffff>在模块定义文件中导出DDB。</FONT></FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1>在大多数时候,你可以把上面的.DEF文件用到你的新建的VxD项目中去。你只要把.DEF文件里第一行和最后一行的VxD名字改掉就可以了。在一个汇编的VxD项目中,段的定义是不必要的,段的定义主要用于C的VxD项目编写,但用在汇编里也是可以的。你会得到一大堆警告的信息但是它能汇编成功。你也可以删掉你在你的项目里没有用到的段定义从而去掉这些讨厌的警告信息。</FONT></FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1>vmm.inc包含了许多用于定义你的源文件中的段的宏:</FONT></FONT></FONT> <br/> <CENTER>
_<B><FONT face=Arial,Helvetica><FONT size=-1>LTEXT</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT
size=-1>VxD_LOCKED_CODE_SEG</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT size=-1>_PTEXT</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT
size=-1>VxD_PAGEABLE_CODE_SEG</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT
size=-1>_DBOCODE</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT
size=-1>VxD_DEBUG_ONLY_CODE_SEG</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT size=-1>_ITEXT</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT
size=-1>VxD_INIT_CODE_SEG</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT size=-1>_LDATA</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT
size=-1>VxD_LOCKED_DATA_SEG</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT size=-1>_IDATA</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT
size=-1>VxD_IDATA_SEG</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT size=-1>_PDATA</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT
size=-1>VxD_PAGEABLE_DATA_SEG</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT size=-1>_STEXT</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT
size=-1>VxD_STATIC_CODE_SEG</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT size=-1>_SDATA</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT
size=-1>VxD_STATIC_DATA_SEG</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT
size=-1>_DBODATA</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT
size=-1>VxD_DEBUG_ONLY_DATA_SEG</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT
size=-1>_16ICODE</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT
size=-1>VxD_16BIT_INIT_SEG</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT size=-1>_RCODE</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT
size=-1>VxD_REAL_INIT_SEG</FONT></FONT></B></CENTER>
<FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>每个宏都有它相对应的结束宏,例如,如果你要在你的源文件中定义一个</FONT><B><FONT color=#ffff99>_LTEXT</FONT></B><FONT color=#ffffff>段,你应该这样写:</FONT></FONT></FONT> <BLOCKQUOTE><B><FONT face=Arial,Helvetica><FONT color=#66ff99><FONT size=-1>VxD_LOCKED_CODE_SEG</FONT></FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT size=-1>(把你的代码写在这里)</FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT color=#66ff99><FONT size=-1>VxD_LOCKED_CODE_ENDS</FONT></FONT></FONT></B>
</BLOCKQUOTE><FONT face=Arial,Helvetica><FONT color=#66ffff><FONT size= 0>VxD结构</FONT></FONT></FONT><FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1>现在你了解了LE文件里的段,我们可以继续来看一下源文件。你会发现VxD程序有一个特点,那就是它用了很多的宏。你可以看到在VxD中宏几乎无处不在,这都成为一个习惯了。这些宏用来隐藏一些底层的细节,也增加了源程序的可移植性。如果你有兴趣,你可以看一看像vmm.inc这一类的库文件中的这些宏的定义。</FONT></FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1>下面是VxD源文件结构:</FONT></FONT></FONT> <br/> <BLOCKQUOTE><B><FONT face=Arial,Helvetica><FONT color=#66ff99><FONT size=-1>.386p</FONT></FONT></FONT></B> <br/><B><FONT face=Arial,Helvetica><FONT color=#66ff99><FONT size=-1>include vmm.inc</FONT></FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT color=#66ff99><FONT size=-1>DECLARE_VIRTUAL_DEVICE FIRSTVXD,1,0, FIRSTVXD_Control, UNDEFINED_DEVICE_ID, UNDEFINED_INIT_ORDER</FONT></FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT color=#66ff99><FONT size=-1>Begin_control_dispatch FIRSTVXD</FONT></FONT></FONT></B> <br/><B><FONT face=Arial,Helvetica><FONT color=#66ff99><FONT size=-1>End_control_dispatch FIRSTVXD</FONT></FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT color=#66ff99><FONT size=-1>end</FONT></FONT></FONT></B>
</BLOCKQUOTE>
<br/><FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1>这段源程序给人的第一印象就是:它并不像一个汇编源程序。那是因为它用了很多宏。让我们来分析一下源程序以便你能很快理解它。</FONT></FONT></FONT> <BLOCKQUOTE><B><FONT face=Arial,Helvetica><FONT color=#ffcc33><FONT size=-1>.386p</FONT></FONT></FONT></B></BLOCKQUOTE><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>告诉编译器我们要使用包括CPU特权指令的80386指令系统。你也可以使用</FONT><B><FONT color=#ffcc33>.486p</FONT></B><FONT color=#ffffff>或者</FONT><B><FONT color=#ffcc33>.586p</FONT></B><FONT color=#ffffff>.</FONT></FONT></FONT> <BLOCKQUOTE><B><FONT face=Arial,Helvetica><FONT color=#ffcc33><FONT size=-1>include vmm.inc</FONT></FONT></FONT></B></BLOCKQUOTE><FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1>你的每个VxD源程序都必须包含imm.inc,因为它包含了你在源程序里所要用到的宏的定义。你还可以根据需要包含其他的库文件。</FONT></FONT></FONT> <BLOCKQUOTE><B><FONT face=Arial,Helvetica><FONT color=#ffcc33><FONT size=-1>DECLARE_VIRTUAL_DEVICE FIRSTVXD,1,0, FIRSTVXD_Control, UNDEFINED_DEVICE_ID, UNDEFINED_INIT_ORDER</FONT></FONT></FONT></B></BLOCKQUOTE><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>正如我们刚才说的,VMM通过VxD程序的</FONT><B><FONT color=#ffff99>设备描述块(DDB)</FONT></B><FONT color=#ffffff>来获取它所需要知道的关于VxD的所有信息。一个设备描述块是一个结构,它包含了许多关于VxD的重要信息,比如VxD的名字,它的设备ID,它的VxD服务函数入口(如果有的话),等等。你可以在imm.inc里查一查这个结构,它被定义为</FONT><B><FONT color=#ffff99>VxD_Desc_Block</FONT></B><FONT color=#ffffff>。你必须在</FONT><B><FONT color=#ffff99>.DEF</FONT></B><FONT color=#ffffff> 文件里导出这个结构。这个结构有22个数据,但是你只用填写其中的几个。然后vmm.inc包含的一个宏会为你初始化并填写这些数据。这个宏叫做</FONT><B><FONT color=#ffff99>DECLARE_VIRTUAL_DEVICE</FONT></B><FONT color=#ffffff>。它的格式如下:</FONT></FONT></FONT>
<B><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#66ff99>Declare_Virtual_Device</FONT><FONT color=#ffcc33> Name, MajorVer, MinorVer, CtrlProc, DeviceID, InitOrder, V86Proc, PMProc, RefData</FONT></FONT></FONT></B>
<FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>你可以看到:VxD源程序中的标号是不区分大小写的,你可以用大写,小写或者混合起来用都可以。让我们来看一下</FONT><B><FONT color=#ffff99>Declare_virtual_device</FONT></B><FONT color=#ffffff>里的每一个参数。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#66ff99>Name</FONT></B><FONT color=#ffffff>VxD的名字最多8个字符。它</FONT><B><FONT color=#ffff99>必须</FONT></B><FONT color=#ffffff>是大写!在系统中的所有VxD程序里,它们的名字不能重复,每个VxD的名字应该是唯一的。这个宏同时也会根据这个名字产生DDB的名字,产生的办法就是:在这个名字的后面加上</FONT><B><FONT color=#ffff99>_DDB</FONT></B><FONT color=#ffffff>。所以如果你的VxD的名字是</FONT><B><FONT color=#ffff99>FIRSTVXD</FONT></B><FONT color=#ffffff>,</FONT><B><FONT color=#ffff99>Declare_Virtual_Device</FONT></B><FONT color=#ffffff>这个宏就会把DDB的名字定为</FONT><B><FONT color=#ffff99>FIRSTVXD_DDB</FONT></B><FONT color=#ffffff>。记住,你还要在.DEF文件里导出DDB。所以你必须使DDB的名字和.DEF文件定义中的相同。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#66ff99>MajorVer</FONT></B><FONT color=#ffffff> 和 </FONT><B><FONT color=#66ff99>MinorVer</FONT></B><FONT color=#ffffff> 你的VxD的主要的和次要的版本。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#66ff99>CtrlProc</FONT></B><FONT color=#ffffff>你的VxD程序的</FONT><B><FONT color=#ffff99>设备控制函数</FONT></B><FONT color=#ffffff>的名字。设备控制函数是一个接受和处理VxD程序的控制消息的函数。你可以把设备控制函数看作Window函数的等价物。既然我们要用</FONT><B><FONT color=#ffff99>Begin_Control_Dispatch</FONT></B><FONT color=#ffffff>这个宏来生成我们的设备控制函数,那么我们应该使用一个标准格式的名字,那就是</FONT><B><FONT color=#99ff99>VxD的名字</FONT><FONT color=#ffff99>_Control</FONT></B><FONT color=#ffffff>。 </FONT><B><FONT color=#ffff99>Begin_Control_Dispatch</FONT></B><FONT color=#ffffff>这个宏把</FONT><B><FONT color=#ffff99>_Control</FONT></B><FONT color=#ffffff> 加到它后面的那个名字上(而我们又通常把VxD的名字写在它后面)作为设备控制函数的名字,所以我们就应该把VxD的名字加上</FONT><B><FONT color=#ffff99>_Control</FONT></B><FONT color=#ffffff>作为</FONT><B><FONT color=#66ff99>CtrlProc</FONT></B><FONT color=#ffffff>参数的值。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#66ff99>DeviceID </FONT></B><FONT color=#ffffff> 你的VxD程序的16位唯一标识符当且仅当你的VxD程序需要处理以下情况时你需要用到这个ID:</FONT></FONT></FONT>
<FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1>你的VxD程序导出一些供其他VxD程序使用的VxD服务。因为20H中断接口用设备ID来定位/区分VxD程序,所以一个唯一的ID对你的VxD程序是必要的。</FONT></FONT></FONT>
<FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1>Your VxD
你的VxD程序要在初始化中断2FH,1607H时通知实模式程序它的存在。</FONT></FONT></FONT>
<FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1>Some
一些实模式软件(TSR)要用中断2FH,1605H来加载你的VxD程序。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>如果你的VxD程序不需要一个唯一的设备ID,你可以把这一项设为</FONT><B><FONT color=#ffff99>UNDEFINED_DEVICE_ID </FONT></B><FONT color=#ffffff>,如果你需要它,你可以去Microsoft要一个。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#66ff99>InitOrder</FONT></B><FONT color=#ffffff> 初始化的顺序,简单的说,就是加载的顺序。VMM就按照这个次序来加载VxD程序。每个VxD程序都有一个加载次序号,例如:</FONT></FONT></FONT> <B><TT><FONT color=#ffffff><FONT
size=-1>VMM_INIT_ORDER EQU
000000000H</FONT></FONT></TT></B> <br/><B><TT><FONT color=#ffffff><FONT
size=-1>DEBUG_INIT_ORDER EQU
000000000H</FONT></FONT></TT></B> <br/><B><TT><FONT color=#ffffff><FONT
size=-1>DEBUGCMD_INIT_ORDER EQU
000000000H</FONT></FONT></TT></B> <br/><B><TT><FONT color=#ffffff><FONT
size=-1>PERF_INIT_ORDER EQU
000900000H</FONT></FONT></TT></B> <br/><B><TT><FONT color=#ffffff><FONT
size=-1>APM_INIT_ORDER EQU
001000000H</FONT></FONT></TT></B>
<br/><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>你可以看到:</FONT><B><FONT color=#ffff99>VMM</FONT></B><FONT color=#ffffff>, </FONT><B><FONT color=#ffff99>DEBUG</FONT></B><FONT color=#ffffff>和</FONT><B><FONT color=#ffff99>DEBUGCMD</FONT></B><FONT color=#ffffff>是首先加载的VxD程序,然后是</FONT><B><FONT color=#ffff99>PERF</FONT></B><FONT color=#ffffff>和</FONT><B><FONT color=#ffff99>APM</FONT></B><FONT color=#ffffff>。初始化顺序值越低的VxD程序越先被加载。如果你的VxD程序在初始化时需要用到其他VxD程序提供的服务,那么你必须把初始化顺序的值设得比你所要调用的那个VxD程序的大,这样,当你的VxD程序加载时,你所要的VxD就已经在内存中为你准备好了。如果不想去管你的VxD的初始化顺序,就把这个参数填写为</FONT><B><FONT color=#ffff99>UNDEFINED_INIT_ORDER </FONT></B><FONT color=#ffffff>。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>V86Proc</FONT></B><FONT color=#ffffff>和</FONT><B><FONT color=#ffff99>PMProc</FONT></B><FONT color=#ffffff>你的程序可以导出供V86和保护模式程序使用的API,这两个参数就是用来填写这些API的地址。记住,VxD程序除了监控系统虚拟机外,还要监控一个或多个运行在DOS或者保护模式下的虚拟机程序。理所当然的,VxD程序要为DOS和保护模式程序提供API支持。如果你不导出这些API,你可以不填这两个参数。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#66ff99>RefData</FONT></B><FONT color=#ffffff>
</FONT><B><FONT color=#ffff99>输入输出监视器(IOS)</FONT></B><FONT color=#ffffff>要用到的参考数据。只有一种情况下你要用到这个参数:当你在为IOS编写一个层驱动程序时。否则,你可以不填这个参数。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>接下来是</FONT><B><FONT color=#ffff99>Begin_Control_Dispatch</FONT></B><FONT color=#ffffff>宏。</FONT></FONT></FONT> <BLOCKQUOTE><B><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffcc33>Begin_control_dispatch</FONT><FONT color=#66ff99> FIRSTVXD</FONT></FONT></FONT></B> <br/><B><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffcc33>End_control_dispatch </FONT><FONT color=#66ff99>FIRSTVXD</FONT></FONT></FONT></B></BLOCKQUOTE><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>这两个宏定义了设备控制函数,当VxD的控制消息发生时,VMM就调用这个函数。你必须填写设备控制函数名字的前半部分,在本例中,我们用的是</FONT><B><FONT color=#66ff99>FIRSTVXD</FONT></B><FONT color=#ffffff>。这个宏会在你输入的前半部分后加上</FONT><B><FONT color=#ffff99>_Control</FONT></B><FONT color=#ffffff>作为设备控制函数的名字。这个名字一定要和你在</FONT><B><FONT color=#ffff99>Declare_virtual_device</FONT></B><FONT color=#ffffff> 宏中给参数</FONT><B><FONT color=#66ff99>CtrlProc</FONT></B><FONT color=#ffffff>填的名字一致。设备控制函数总是放在锁定段(</FONT><B><FONT color=#ffff99>VxD_LOCKED_CODE_SEG</FONT></B><FONT color=#ffffff>)内的。上面定义的设备控制函数什么也不干。你需要说明你的VxD程序要响应什么控制消息,以及处理这个消息的函数,你可以用</FONT><B><FONT color=#ffff99>Control_Dispatch</FONT></B><FONT color=#ffffff>宏来实现这一点。</FONT></FONT></FONT> <BLOCKQUOTE><FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>Control_Dispatch </FONT></B><I><FONT color=#66ffff>message, function</FONT></I></FONT></FONT></BLOCKQUOTE><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>例如,如果你的VxD程序只要处理</FONT><B><FONT color=#ffff99>Device_Init </FONT></B><FONT color=#ffffff>消息,你的设备控制程序要这样写:</FONT></FONT></FONT> <BLOCKQUOTE><B><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffcc33>Begin_Control_Dispatch</FONT><FONT color=#ffffff></FONT><FONT color=#66ff99> FIRSTVXD</FONT></FONT></FONT></B> <br/><B><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff> </FONT><FONT color=#ffcc33>Control_Dispatch</FONT><FONT color=#ffffff> Device_Init, OnDeviceInit</FONT></FONT></FONT></B> <br/><B><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffcc33>End_Control_Dispatch</FONT><FONT color=#66ff99>FIRSTVXD</FONT></FONT></FONT></B></BLOCKQUOTE><FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>OnDeviceInit</FONT></B><FONT color=#ffffff>就是要处理</FONT><B><FONT color=#ffff99>Device_Init</FONT></B><FONT color=#ffffff>消息的函数的名字。你可以给你的函数取任何你想取的名字。</FONT></FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>你可以用</FONT><B><FONT color=#ffff99>end</FONT></B><FONT color=#ffffff> 直接地结束你的VxD源程序。</FONT></FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#ffffff>综上所述,一个VxD程序至少包含一个设备控制块和一个设备控制函数。你要用</FONT><B><FONT color=#ffff99>Declare_Virtual_Device</FONT></B><FONT color=#ffffff>宏来定义一个设备控制块,用</FONT><B><FONT color=#ffff99>Begin_Control_Dispatch</FONT></B><FONT color=#ffffff>宏来定义一个设备控制程序。你必须在.def文件中的</FONT><B><FONT color=#ffff99>EXPORTS</FONT></B><FONT color=#ffffff>下面填写设备控制块的名字,从而导出该设备控制块。</FONT></FONT></FONT> <FONT face=Arial,Helvetica><FONT color=#66ffff><FONT size= 0>编译VxD</FONT></FONT></FONT><FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1>编译的过程和编译普通的win32程序一样。先调用ml.exe编译asm源文件,然后用link.exe来连接object文件。不同的地方是ml.exe和link.exe后所带的命令行参数不同。</FONT></FONT></FONT>
<FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#66ffff>ml</FONT></B><FONT color=#ffffff> -</FONT><B><FONT color=#ffff99>coff -c -Cx -DMASM6 -DBLD_COFF -DIS_32 </FONT><FONT color=#66ff99>firstvxd.asm</FONT></B></FONT></FONT>
<FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>-coff</FONT></B><FONT color=#ffffff> 表明COFF数据格式</FONT></FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>-c</FONT></B><FONT color=#ffffff> 只汇编,不调用连接程序来连接,这样我们就可以在调用link.exe的时候使用跟多的参数。</FONT></FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>-Cx</FONT></B><FONT color=#ffffff> 保存公共,外部标记。</FONT></FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>-Dtext</FONT></B><FONT color=#ffffff> 定义一个文本宏,例如,-DBLD_COFF定义了一个文本宏BLD_COFF,这个宏用来作为编译的条件。如果你有兴趣,你可以在库文件中查找BLD_COFF,自己亲眼看看它对汇编过程起什么作用。上面的命令行定义了三个文本宏:BLD_COFF,IS_32和MASM6。如果你对C编程熟悉的话,你会知道这些定义相当于完成以下功能:</FONT></FONT></FONT> <BLOCKQUOTE><B><FONT face=Arial,Helvetica><FONT color=#66ff99><FONT size=-1>#define BLD_COFF </FONT></FONT></FONT></B> <br/><B><FONT face=Arial,Helvetica><FONT color=#66ff99><FONT size=-1>#define IS_32</FONT></FONT></FONT></B> <br/><B><FONT face=Arial,Helvetica><FONT color=#66ff99><FONT size=-1>#define MASM6</FONT></FONT></FONT></B></BLOCKQUOTE><B><FONT face=Arial,Helvetica><FONT size=-1><FONT color=#66ffff>link </FONT><FONT color=#ffff99>-vxd -def:firstvxd.def </FONT><FONT color=#66ff99>firstvxd.obj</FONT></FONT></FONT></B>
<FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>-vxd </FONT></B><FONT color=#ffffff>表明我们要根据obj文件来生成一个VxD文件。</FONT></FONT></FONT> <br/><FONT face=Arial,Helvetica><FONT size=-1><B><FONT color=#ffff99>-def:.DEF file</FONT></B><FONT color=#ffffff> 指定该VxD文件的模式定义文件。</FONT></FONT></FONT>
<FONT face=Arial,Helvetica><FONT color=#ffffff><FONT size=-1>我觉得用makefile很方便,如果你不喜欢用makefile,你也可以创建批处理文件来自动完成编译过程。我的makefile如下:</FONT></FONT></FONT>
<B><FONT face=Arial,Helvetica><FONT color=#ffcc33><FONT size=-1>NAME=firstvxd</FONT></FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT color=#ffcc33><FONT size=-1>$(NAME).vxd:$(NAME).obj</FONT></FONT></FONT></B> <br/><B><FONT face=Arial,Helvetica><FONT color=#ffcc33><FONT size=-1> link -vxd -def:$(NAME).def $(NAME).obj</FONT></FONT></FONT></B>
<B><FONT face=Arial,Helvetica><FONT color=#ffcc33><FONT size=-1>$(NAME).obj:$(NAME).asm</FONT></FONT></FONT></B> <br/><B><FONT face=Arial,Helvetica><FONT color=#ffcc33><FONT size=-1> ml -coff -c -Cx -DMASM6 -DBLD_COFF -DIS_32 $(NAME).asm</FONT></FONT></FONT></B> <br/><br/>
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题

0

回帖

4882万

积分

论坛元老

Rank: 8Rank: 8

积分
48824836
热门排行