VxD入门教程
1.背景知识?????
??为了看懂本篇所给的例子,需要C,?汇编及Windows?设备驱动程序的相关知识。?
??
???
??2.开发工具??
???
??需要?Micros以下采??
用?VC++?6.0?为例)??
???
??3.目的??
???
??利用以上所列的开发工具,编写一个动态装入的?VxD,?利用该VxD可以读取内存?
地址0??
处的中断向量表。??
???
??4.开始??
???
??第一步:??
???
??用?VC++?新建一个工程,取名为?MiniVxd,?类型为?Win32?App?或?Console?App?
因为V??
C++不提供直接生成??
???
??VxD的App?Wizard,所以只好自己在以上两种App中选择一个,然后再进行手工修?
改一下??
,需要修改的内容??
???
??只有一处,即在?Link?的命令行上加上一个选项??
???
???/VXD??
???
??第二步:??
???
??新建一个空文件,在其中输入以下代码:??
???
??TITLE?MINIVXD?-?By?Ding?Kai??
???
??.386p??
???
??include?vmm.inc??
???
???
??MINIVXD_DYNAMIC?EQU?1??
???
??;?定义设备驱动程序块?DDB,?由于是动态装入的,所以设备ID和装入顺序可以取?
未定??
义值.??
???
??DECLARE_VIRTUAL_DEVICE?MINIVXD,?1,?0,?MsgDispatch,UNDEFINED_DEVICE_ID,?
??
???
???Undefined_Init_Order,?0,?0??
???
??VXD_LOCKED_CODE_SEG??
???
??;?定义消息派遣表,本例中只需要一条消息,即W32_DEVICEIOCONTROL,用于从?
Win32Ap??
p中利用??
???
??;?DeviceIoControl?API函数同本VxD进行通讯.??
???
???
??BeginProc?MsgDispatch??
???
??Control_Dispatch?W32_DEVICEIOCONTROL,?W32DeviceIoControl,?sCall,??
???
???
???clc??
???
???ret??
???
??EndProc?MsgDispatch??
???
???
??VXD_LOCKED_CODE_EN???
??END??
???
???
??将文件存盘,文件名取为?VxdStub.asm,?然后用下列命令编译该文件:??
???
??Aml?-coff?-W2?-DBLD_COFF?-DIS_32?-c?-Cx?-DMASM6?-I\95DDK\INC32??
VxdStub.asm??
???
???
??这将会生成?vxdstub.obj,将此OBJ文件加入到工程文件中。??
???
???
??第三步:??
???
??创建一个?C?语言文件,文件名取为?MiniVxd.c,在文件中输入以下代码:??
??#define?WIN40SERVICES??
???
??#pragma?warning?(disable:4229)??
???
??#include??
???
??#define?WANTVXDWRAPS??
???
??#pragma?intrinsic(memcpy)??
???
??#pragma?VxD_LOCKED_DATA_SEG??
???
???
??//?此处可以定义任何在VxD中需要的全局变量.??
???
??char?AnyData[200];??
???
??#pragma?VxD_LOCKED_CODE_SEG??
???
??///////////////////////////////////////////??
???
??DWORD?__stdcall?GetMemory(?DWORD?dwDDB,?PDIOCPARAMETERS?pD?)??
???
??{??
???
???BYTE?*p;??
???
??//?此处忽略了参数检查,实际应用中切不可如此?!!??
???
???p?=?(PBYTE)pD->lpvOutBuffer;??
???
???memcpy(p,?0,?1024);?//?读取中断向量表,共?1024?字节??
???
???return?1024;??
???
??}??
???
??///////////////////////////////////////////////////////??
???
??/*?Win32?interface?*/??
???
??///////////////////////////////////////////////////////??
???
??int?__stdcall??
???
??W32DeviceIoControl(?DWORD?dwIoCtrlCode,?/*ecx*/??
???
??DWORD?dwDDB,?/*ebx*/??
???
??DWORD?pDIOCParams?)?/*esi*/??
???
??{??
???
???PDIOCPARAMETERS?pD;??
???
???pD?=?(PDIOCPARAMETERS)pDIOCParams;??
???
???if?(?dwIoCtrlCode?==?DIOC_OPEN?||??
???
???dwIoCtrlCode?==?DIOC_CLOSEHANDLE)??
???
???return?0L;??
???
???//?Init?returned?value??
???
???if?(pD->lpcbBytesReturned)??
???
???*(DWORD?*)pD->lpcbBytesReturned?=?0;??
???
???if?(dwIoCtrlCode==1)?{?//?1?=?DeviceIoControl代码??
???
???GetMemory(dwDDB,?pD);??
???
??}??
???
??else??
???
???return?ERROR_NOT_SUPPORTED;??
???
???return?0;??
???
??}??
???
??将本文件加入到工程文件中.??
???
???
??第四步:??
???
??创建一个模块定义文件,此文件将要定义最终生成的VxD文件的段属性并输出设备?
驱动??
程序块,??
???
??输入下面的代码,并将文件存为?MiniVxd.def,同时将其加入到工程文件中.??
???
??VXD?MINIVXD?DYNAMIC??
???
??DESCRIPTION?'Written?by?Ding?Kai'??
???
??SEGMENTS??
???
??_LTEXT?CLASS?'LCODE'?PRELOAD?NONDISCARDABLE??
???
??_LDATA?CLASS?'LCODE'?PRELOAD?NONDISCARDABLE??
???
??_TLS?CLASS?'LCODE'?PRELOAD?NONDISCARDABLE??
???
??_BSS?CLASS?'LCODE'?PRELOAD?NONDISCARDABLE??
???
??_ITEXT?CLASS?'ICODE'?DISCARDABLE??
???
??_IDATA?CLASS?'ICODE'?DISCARDABLE??
???
??_PTEXT?CLASS?'PCODE'?NONDISCARDABLE??
???
??_PDATA?CLASS?'PDATA'?NONDISCARDABLE?SHARED??
???
??HEAPSIZE?10240??
???
??STACKSIZE?40960??
???
??EXPORTS??
???
??MINIVXD_DDB?@1??
???
???
??当然,其中有些段在本程序中不存在,此处列出来是想将此文件做为一个模板以?
供以??
后使用。??
???
???
??第五步:??
???
??加入本工程文件需要的最后一个,VxdWarps.clb,?这个文件是DDK的LIB目录中.?
??
???
??第六步:??
???
??所需的四个文件已经全部加入完毕,它们是:??
???
??vxdstub.obj??
???
??minivxd.c??
???
??minivxd.def??
???
??vxdwarps.clb??
???
???
??在进行make之前请检查一下文件路径,?在?Include?中一定要包含?DDK?目录中的?
INC3??
?
2?目录.??
???
??确认无误后可以按?F7?键进行?Make?了。??
???
???
??结果会在?DEBUG?目录或?Release?目录?(依赖于工程文件的设置)?生成?
MiniVxd.vxd??
文件。??
???
???
??至此,一个小小的Vxd文件生成完毕,?可以在程序中用下面的代码与本VxD进行通?
讯.??
???
???
???
??BYTE?buffer[1024];??
???
??DeviceIoControl(hVxd,?1,?NULL,?NULL,?buffer,?1024,?NULL,?0);??
???
???
??结果,当前的中断向量表就会出现在?buffer?数组中。??
???
??本文不再提供完整的测试程序,需要的话请与作者联系??
???
??5.小结??
???
???
??本文用一个简单的示例介绍了如何在?Windows?95?下编写可动态安装的?VxD,?这?
种方??
法及用这种方法??
???
??编写的VxD在Windows?98?下同样适用。然而,在?Windows?NT?不能采用这种方法?
??
???
???
???
???
*****************************?
**??
*******************************************??
???
??标?题:?VxD世界——虚拟的Windows世界??
???
???
??从事Windows系统编程的读者一定听说过VxD──这个在Windows(注:指Windows?
?3.1??
/95/98,??
???
??非Windows?NT)的世界里无所不能的超级武器。迄今为止,越来越多的人有了深?
入了??
解Windows的愿望,??
???
??这也使得VxD技术愈来愈受到重视。VxD,意即Virtual?Something?Driver,这里?
的x就??
是指Something。比如??
???
??说键盘驱动程序VKD、鼠标驱动程序VMD等。在许多人的脑海中,只有硬件开发者?
才会??
用到VxD,其实不??
???
??然,对于软件开发来说,VxD也是无所不能的。在Windows3.1下这点还不明显,?
而在W??
indows95中,随着??
???
??线程与局部化概念的引入,想控制系统的全部资源变得愈来愈困难。举个简单的?
例子??
,由于32位的Windows??
???
??应用程序拥有独立的4GB线性地址空间,在Windows?3.1下常用的Windows应用程?
序之间??
通信及共享变量的??
???
??方法不再完全适用了,想做些“出格”的事,比如说截获别的应用程序的消息,?
变得??
愈发困难了。就在您一??
???
??愁莫展的时候,采用VxD技术或许会令您“豁然开朗”──既然操作系统能控制?
全部资??
源,那么与操作系统??
???
??享有同样最高权限的VxD(或者干脆说作为操作系统一部分的VxD)也一定能够帮?
助您??
完成“出格行为”。??
???
??为什么VxD有如此神通?最根本的原因,是由于VxD运行在系统的Ring?0级,而用?
SDK(??
或现在大家熟知的??
???
??VC++、BC++、Borl?and?C++?Builder、VB、Delphi)开发出的应用程序运?
行在??
Ring?3级。在80x86??
???
??保护模式下,运行在Ring?0级的VxD拥有系统最高权限(操作系统也运行在?
Ring?0级)??
。??
???
??要想真正掌握VxD技术,您必须:??
???
???对80x86的保护模式有清楚的认识;??
???
???深入理解Windows的运行机制;??
???
?
???熟悉VxD本身的运行机制。??
???
??对于保护模式,目前国内市面上有一些参考书籍,希望您能硬着头皮看几遍。虽?
说?不??
能使人茅塞顿开,却也??
???
??对保护模式能有个比较清楚的认识。??
???
???
??让我们先来认识一下Windows本身吧。??
???
???
??虚拟的Windows世界??
???
???
??运行在Windows95下的几种应用程序:DOS应用程序、Win16应用程序、Win32应用?
程序??
。其中DOS?应用程??
???
??序大多运行在字符模式,而Win16/Win32?应用程序却是运行在图形模式下的。为?
了使??
这几种运作方式大相??
???
??径庭的应用程序能“和平共处”,Windows采用了虚拟机(Virtual?machine)的?
方式??
。每个DOS应用程序??
???
??运行在一个独立的DOS?VM中,使得这个愣头青似的DOS应用程序感到自己控制着?
所有的??
资源,它可以肆意??
???
??地在它认为的全屏上涂涂改改,在它以为自己独占的硬盘上读出写入(微软在设?
计DO??
S时怎么也想不到PC机??
???
??硬件的发展如此迅速)。而所有的Windows应用程序,不管是16位的,还是32位?
的,都??
运行在同一个System??
???
???VM中。这是由于Windows?应用程序守规矩得多,熟悉Windows编程的读者一定对?
句柄??
(Handle)这个概念??
???
??有非常深刻的印象。Windows应用程序的一举一动都是通过各种各样的句柄(?
HWND、H??
DC、HANDLE、??
???
??HMOUDLE等)来实现的,正是通过句柄这个中介,使得系统有机会在同一个?
System?VM??
中协调多个Windows??
???
???应用程序。不信吗?好,让我们做个实验(我假定您已经安装了Softice?for??
Win95??
),启动Windows95,启动??
???
??结束之后,不要运行任何DOS应用程序,按下Control+D激活Softice(在有的?
Softic??
e的缺省安装中,激活??
???
??Softice的热键是Alt+D),然后敲入指令:??
???
??:vm??
???
???
??VM?Handle?Status?High?Addr?VM?ID?Client?Regs??
???
??C39200E8?00001862?C3800000?00000001?C3449F70??
???
???
??从Softice的输出可以看到这时只有一个VM,它的ID是1。??
???
???
??按Control+D返回Windows桌面,这时,再运行几个Windows应用程序,不管是?
16位的??
,还是32位的。重复??
???
??刚才的操作,按下Control+D激活Softice,敲入指令VM。我们看到Softice输出?
的结??
果是一样的。也就是说尽??
???
??管有多个Windows应用程序在运行,可是系统中只存在一个VM,这个VM就是?
System?VM??
。??
???
???
??按Control+D返回Windows桌面,这时,从“开始选单”中运行“MS-DOS方式”?
,按??
下Control+D激活??
???
??Softice,然后敲入指令:??
???
??:vm??
???
???
??VM?Handle?Status?High?Addr?VM?ID?Client?Regs??
???
??C8D200E8?00000802?C8C00000?00000002?C0F5FF70??
???
??C39200E8?00005A62?C3800000?00000001?C0F06F70??
???
???
??从Softice的输出可以看到这时系统中存在两个VM,其中一个就是我们刚才看到?
的??
???
??System?VM(VM?ID=1),另外,又多了一个ID=2的VM,这个VM就是DOS?VM,??
???
??也就是说刚才的那个“MS-DOS方式”就是运行在这个DOS?VM中的。??
???
???
??按Control+D返回Windows桌面,从“开始选单”中再运行几个“MS-DOS方式”?
??
???
??,按下Control+D激活Softice,敲入VM指令,你会发现VM增多了,其数量等于?
所??
???
??有的“MS-DOS方式”的数目加1。??
???
???
??现在您信了吧??所有的DOS应用程序都是运行在各自的DOS?VM中,而所有的??
???
??Windows?应用程序都运行在同一个System?VM中。这是非常重要的概念。??
???
???
???
???
???
******************************************?
**??
***************************??
???
??VxD世界——Windows的保护模式??
???
???
??一般来说,80x86(80386及其以后的各代CPU)可以在三种模式下运转:实模式?
,??
???
??保护模式,V86模式。实模式就是古老的MS-DOS的运行环境。Win95只利用了两?
??
???
??种模式:保护模式和V86模式。??
???
???
??为什么要进入保护模式??
???
??保护模式有许多优越性。其中最最直接的好处就是:你的程序可以利用更多的内?
存了??
!??
???
???
??不要以为这是什么大不了的问题,我相信每一个曾在MS-DOS下写程序的人都有?
一??
???
??个苦恼:怎样在程序中开个足够大的数组?动不动就会堆栈溢出,许多事都不能?
做??
???
??了。不要怨Turbo?C、MS?Fortran、Turbo?Pascal,它们也是心有余而力不足。?
这些??
???
???
??烦恼都源自“你的程序是运行在实模式下的”。运行在实模式下的16位程序最多?
只??
???
??能存取1M的内存。你也许会问:我的机器上不是有64M内存吗?是啊,如果说你?
是??
???
??在MS-DOS下运行程序(或者说CPU运转在实模式下),那你只利用了1M内存,??
?
???
??其余的内存都“下岗”了,在这种情况下,你的386/486/586/PⅡ只相当于一个?
跑得??
???
???
??快的8086。??
???
???
??但是保护模式给了我们一个惊喜。理论上,在保护模式下,CPU可以寻址4096M(?
??
???
??即4GB)内存。这就是说,只需把你的程序编译成32位的可执行程序(当然得借?
助??
???
??32位编译器),你就可以在程序中充分利用内存了,这样做的直接结果是:你可?
以??
???
??不用再为堆栈溢出或开不出5000×5000的数组而吃不下饭了。??
???
???
??正是4GB内存存取的实现,使得操作系统有了更加智能化的物质基础,多任务的?
实??
???
??现才可以提到日程上来考虑了。??
???
???
??再深入一些??
???
???
??从硬件结构上说,386由三个寄存器CR0、CR1、CR2控制着CPU的运转。比如说??
???
??,CR0的第0位就是用来判断当前CPU是工作在保护模式还是实模式下。学过??
???
??8088/8086汇编语言的人一定熟悉AX、BX、CX、DX、SI、DI、SP、BP这些16位??
???
??的寄存器,在80386中,这些寄存器被扩展到了32位,即EAX、EBX、ECX、EDX??
???
??、ESI、EDI、ESP、EBP,如果CPU是运转在实模式下,那你只能利用这些32位寄?
??
???
??存器的前16位,而后面的16位就浪费了。??
???
???
??段的概念是我们理解保护模式的关键所在。在实模式下,段寄存器中存放着16位?
的??
???
??段地址,这时,段地址是参与寻址的:把段地址左移4位,加上偏移地址,就是?
20位??
???
???
??的物理地址了。在保护模式下,段寄存器中存放着16位的段选择器(Segment??
???
??Selector),这个值是不直接参与寻址的,而只是一个指向段描述表(?
Segment??
???
??Descriptor?Table)的索引。段描述表(Segment?Descriptor?Table)中存放着?
段描??
???
???
??述符(Segment?Descriptor)。段描述符中有关于段的描述,比如:段在内存中?
的??
???
??位置、段的大小、段的类型(是数据段还是代码段)等等。??
???
???
??当CPU运行在保护模式下时,内存中往往有至少三张段描述表:全局描述表(??
???
??Global?Descriptor?Table,简称GDT)、局部描述表(Local?Descriptor,简称?
LDT)??
???
???
??、中断描述表(Interrupt?Descriptor?Table,简称IDT)。说到这里,我想提?
醒读者??
???
???
??注意:记住GDT、LDT、IDT这三个词的含义,我们在后面会经常用到。??
???
???
??段描述表不可能超过64K(为什么?如果回答不上来,那就再看看前面的讲解)?
,??
???
??每个段描述符(也就是段描述表中的一项)都是8byte长,所以说,每个段描述?
表最??
???
???
??多只能包含8192个段描述符。??
???
???
??在今后的“走进VxD世界”中,我们将对段描述表、段描述符以及分页机制作进?
一??
???
??步的讲解。??
???
???
???
???
*************************?
**??
***************************??
???
??VxD世界——关于“描述表”??
???
???
??前面我们提到了在保护模式下,内存中往往至少有三张表:GDT,LDT,IDT。聪?
明??
???
??的你可能要问:这几张表都在内存的什么地方呢???
???
???
??图1?几个重要的寄存器的示意图??
???
???
??这三张表的位置是由三个寄存器记录的。这三个寄存器分别是:GDTR,LDTR,??
?
???
??IDTR。我们还要补充讲解一个寄存器,那就是TR(Task?Register),这个寄存?
器??
???
??与保护模式的任务管理有关。在386中,这几个寄存器如图1所示。??
???
???
??我们可以看到GDTR和IDTR都分别包含32位的物理地址和16位的权限级别,总共48?
??
???
??位。GDTR和IDTR中的32位地址是线性地址,不是段:偏移(Seg:Offset)的组合?
??
???
??形式,它表明了段开始的地方,如果说系统还没有启动内存分页管理机制的话,?
那??
???
??这个线性地址就是物理地址,可是一旦系统启动了内存分页管理机制,GDTR和??
?
???
??IDTR中的32位线性地址就不再指向物理地址了。??
???
???
??CPU保留了GDT中的第一个描述符,任何试图通过GDT中的第一个描述符来访问内?
??
???
??存的操作都是非法的(还记得Win95下那可怕的蓝屏吗?)。如果指向GDT的段选?
??
???
??择器的Index域为0的话,那就指向空的段选择器。段选择器的示意如图2:??
???
???
??TI:Talbe?Indicator??
???
??RPL:Requestor?Privilege?Level??
???
???
??图2?段选择器??
???
???
??这就是说,在保护模式下,far?NULL指针是非法的。而在实模式下,NULL指针却?
是有??
意义的。??
???
???
??在内存中(当然是指CPU工作在保护模式下啦),GDT和IDT都只有一份,也就是?
??
???
??说,它们是全局的,任何一个任务改变了这两张表,都会对别的任务产生影响。?
??
???
???
??下面我们谈谈IDT、IDT中的每一项,也就是每一个描述符,都定义了256个中断?
中??
???
??的一个。还记得实模式下MS-DOS环境中的中断向量表吧?在保护模式下,中断?
描??
???
??述表IDT代替了中断向量表。虽说中断描述表可以容纳8192个中断描述符,可是?
??
???
??CPU能利用的只有处于前面的256个。所以中断描述表的长度限制应该是7FFh(23?
??
???
??×28-1)。??
???
???
??T=0:Interrupt?gate描述符??
???
??1:Trap?gate描述符??
???
???
??图3?Trap/Interrupt?gate描述符??
???
???
??其实中断描述表(IDT)中可以有两种描述符:Interrupt?gate描述符、Trap??
gate描??
???
???
??述符。图3显示了Trap/Interrupt?gate描述符的结构。这里提到了gate这个词,?
一般??
???
???
??译作“门”。?“中断门”形像地直接表示了中断调用的过程:中断调用就像经?
过一??
???
???
??扇门一样,这个门就是中断描述符,因为中断描述符中有DPL等权限盘查的标志?
,??
???
??所以要想通过这扇门调用相应的中断服务程序是需要一定的资格的。??
???
???
??Trap和Interrupt?gate非常相似,一般来说Trap?gate是用来捕获系统异常,而?
??
???
??Interrupt?gate用来响应中断。在具体的实现上,只有一点不同:Interrupt??
gate会??
将??
???
??IF置为0,这样可以屏蔽硬件中断。但是Trap?gate不会改变IF的值。??
???
???
??寄存器LDTR中存有LDT的位置。与GDT不同的是,内存中可以有多份LDT。每一个?
??
???
??任务都可以有自己的LDT。从图1中我们可以看到,LDTR不包含“地址?/?权限”?
位??
???
??。LDTR中所包含的是一个段选择器。这个段选择器指向GDT中的一项,但不是普?
??
???
??通的一项,这一项是指向某一LDT的描述符。你可能马上意识到,GDT中可以包含?
??
???
??指向不同LDT的描述符,不错,正是这样的。在Windows保护模式下,每一个MS-?
??
???
??DOS应用程序都有自己的LDT,而所有的Windows应用程序都共享一份LDT。是不??
?
???
??是想起些什么来了?对了,这与Windows中VM(虚拟机)的概念多少类似!??
???
???
???
???
???
**********************************
**??
***************************??
???
??VxD世界——分页机制??
???
???
??关于分页机制??
???
???
??在这种机制下,内存被划分为固定长度的“页面”。在保护模式下,“页面”是?
受??
???
??到保护的,并可以被“虚拟”(说穿了,“虚拟”的东西就是说,本来没有的,?
而??
???
??应用程序却误以为有。比如说,你只有64MB内存,而你的程序却傻乎乎地认为它?
可??
???
??以有4GB的内存用,这就是“虚拟”)。??
???
???
??还记得前面讲过的保护模式下“段”的概念吗?在保护模式下,段的长度是可以?
变??
???
??化的,而这里,“页面”的大小是不能变的,虽然它俩都受到保护模式的“保护?
”??
???
??和“虚拟”。那么“页面”的大小是多少呢?答案是4KB。??
???
???
??假如现在有个32位的线性地址,我们来看一下怎么由这个线性地址获得物理地址?
。??
???
???
??图1?线性地址通过页面映射为物理地址??
???
???
??请记住图1表示内容,因为它实在是太重要了。图中的CR3是指寄存器CR3的值。?
如??
???
??果你装了Softice?for?Win95,那就按下Ctrl+D激活Softice,然后敲指令:??
???
???
??CPU;这时会看到CR3的值。??
???
???
??线性地址转换为物理地址的例子??
???
???
??假设现有线性地址8000DD88h,我们来看一下它到底指向何处物理地址。性线地?
址??
???
??8000DD88h应作如下解析:??
???
???
??800?0D?D88??
???
??Page?Table?Index?Page?Index?Page?Offset??
???
??(页面表索引)?(页面索引)?(页面偏移地址)??
???
???
??现在我们需要知道页面表目录的起始地址。于是我们查看寄存器CR3。假设寄存?
器??
???
??CR3=891000h,那么891000h?+?200h?4?=?891800h(想一想为什么要?4,答案?
??
???
??在文章后注释[1]处),现在我们要查看一下线性地址891800h处的值,在?
Softice中??
???
???
??,敲如下的指令:??
???
???
??:?d?891800??
???
???
??假设得到的是493227h,这个值与0FFFFF000h作“与”运算,就得到了我们要找?
??
???
??的Page?Table的起始物理地址493000h,那么493000h?+?4?0Dh?=?49302Ch(想?
??
???
??一想为什么要?4,见注释[2]),然后我们要知道物理地址49302Ch处的值,但?
是在??
???
???
??Softice中,我们无法直接获得存放在物理地址处的值,只能通过其对应的线性?
地址??
???
???
??来查看。在Softice中,敲如下指令:??
???
???
??:?phys?49302C??
???
???
??假设得到的是线性地址89302Ch(有时你会得到两个不同的值,我们取第一个值?
。??
???
??想一想为什么会有两个不同的值?答案在注释[3]),现在我们查看一下线性地?
址??
???
??89302Ch处的值,这也是物理地址49302Ch处的值。在Softice中,敲如下指令:?
??
???
???
??:?d?89302C??
???
???
??假设得到的是3F5000h,这就是我们要找的Page(页面)的物理地址。然后,??
???
??3F5000h?+?0D88h?=?3F5D88h,这就是线性地址8000DD88h对应的物理地址。我?
??
???
??们想在Softice中查看物理地址3F5D88h处的值,那就得找到其相应的线性地址。?
在??
???
??Softice中,敲如下指令:??
???
???
??:?phys?3F5D88??
???
???
??假设我们得到线性地址7F5D88h,让我们看一下这个线性地址7F5D88h处的值。在?
??
???
??softice中,敲如下指令:??
???
???
??:?d?7F5D88??
???
???
??让我们记下结果,然后再看一下线性地址8000DD88h处的值。??
???
???
??:?d?8000DD88??
???
???
??你会发现线性地址7F5D88h与线性地址8000DD88h所指向的值是完全一样的。??
???
???
??一般来说,为了提高地址转换的效率,在CPU里都会有一个页面转换缓冲区(TLB?
??
???
??),里面存放着最近页面转换的信息。??
???
???
??MMU(Memory?Management?Unit)??
???
???
??从386开始,CPU都带有一个内存管理单元(Memory?Management?Unit,简称??
???
??MMU)。??
???
???
??MMU负责提供的功能:虚拟内存、重组段、进程分隔、页面保护、地址转换。??
???
???
??我们在上面的例子中所进行的线性地址到物理地址的转换就是由MMU来完成的。?
??
???
???
??模式转换??
???
???
??我们简单提一下保护模式与实模式之间的切换过程,有个概念就可以了。??
???
???
??由实模式切换到保护模式:检测一下当前CPU是否有能力运行于保护模式下;检?
测??
???
??保护模式环境(DPMI或VCPI);建立IDT;建立GDT;禁止中断,包括NMI;加载?
??
???
??GDTR;设置IDTR;设置CR0的bit?0;清空指令队列;加载段寄存器;允许中断。?
??
???
???
??由保护模式切换到实模式:禁止中断,包括NMI;禁止页面转换;设置数据段寄?
存??
???
??器的值为准实模式下的选择器;重置CR0的bit?0;清空指令队列;设置?
IDTR=0000??
???
??:03FF;允许中断。??
???
???
??[1]因为在Page?Table?Directory中,每一个表项占4个字节。??
???
???
??[2]因为在Page?Idex?Table中,每一个表项占4个字节。??
???
???
??[3]因为线性地址到物理地址的映射关系不是一对一的,这就是说同一处物理地?
址,??
???
???
??可能有多处线性地址指向它。??
???
???
???
???
???
???
********************************?
**??
***************************??
???
??VxD世界——Win95?的线性地址分配??
???
???
??图1是一张描述Win95线性地址空间的图。下面我们对这张图加以详细说明。??
???
???
??图1?Windows?95线性地址分配图??
???
???
??0~4MB:??
???
???
??这部分在图上标的是DOS内存区,其实这是不确切的。我们知道16位的DOS应用程?
??
???
??序只能存取0~1MB的内存空间,那为什么还要把1MB~4MB的3MB内存也算作??
???
??DOS内存区呢?答案就在我们前面讲过的分页机制中。我们再温习一下:一个页?
面??
???
??(Page)是4kB,一个页面表(Page?Talbe)有1024个页面,一个页面表目录(?
??
???
??Page?Table?Directory)有1024个页面表。那么一个页面表目录项就可以映射?
1024×??
???
???
??4k=4MB的线性地址。其实DOS只能利用到0MB~1MB的内存空间,那1MB~4MB??
???
??的地址空间留给谁了呢?关于这个问题,笔者曾经问过Karen?Hazzah、Walter??
?
???
??Oney和Geoff?Chappell,他们的回答是:这一部分是空的。Win95为了图省事,?
就??
???
??把1MB~4MB的线性地址空间也当作DOS内存区,这样Win95在DOS?VM之间切换??
???
??时,就可以页面表目录项(Page?Table?Directory?Entry)为单位来进行。这样?
虽说??
???
???
??浪费了3MB的地址,却换来了DOS?VM切换的高效率。??
???
???
??那图1中标出的①又是指的什么呢?呵呵,Win95有趣得很,为了使系统、Win16?
应??
???
??用程序能与DOS应用程序互相协作,于是在0~1MB之间,其实是紧挨着1MB的下??
?
???
??面,放了一个Win16全局堆(其实是Win16全局堆很小的一部分)。说到这里,笔?
者??
???
??想起了一个深受大家喜爱的DOS下的编辑器Quick?Edit?4.0,这个编辑器有一个?
非常??
???
???
??有趣的功能:能与Windows共享剪贴板。当时我们猜想它一定用到了未公开的?
DPMI??
???
??调用,现在从图1来看,肯定是①部分的Win16全局堆帮了它的忙啦。??
???
???
??同时请注意图1中还有一个②,这部分我们称作Win16全局堆的高端部分。为什么?
要??
???
??在这里安置一个Win16的全局堆?它是用来作什么的呢???
???
???
??这个问题的答案是:为了高效率地切换DOS?VM。??
???
???
??每一个DOS?VM在大于3GB的地址空间都有一个备份,Win95在DOS?VM之间进行??
???
??切换时,只是简单切换一下页面表目录的第一项就可以了。所以说,如果一个?
VxD??
???
??想访问某个DOS?VM,没有必要一定要等到该DOS?VM成为当前VM才能访问,它可??
?
???
??以直接去②访问那个DOS?VM的备份。这个DOS?VM备份的地址我们称作High-??
???
??linear?address。??
???
???
??后面的文章中,我们将详细讲到如何在VxD中访问DOS?VM。??
???
???
???
???
???
***********************?
**??
***************************??
???
??VxD世界——Win95?内存揭秘??
???
???
??上期“走进VxD世界”中我们谈到了Win95中0~4MB线性内存空间分配情况。下面?
??
???
??我们接着讲余下的4MB~4GB线性内存空间的分配。??
???
???
??4MB~2GB??
???
??Win32应用程序的代码、数据和资源都存放在这段内存中了,这部分内存,对于?
每??
???
??个Win32应用程序来说都是私有的。这里是最能体现保护模式分页机制作用的地?
方??
???
??。两个Win32应用程序,对相同的线性地址(4MB~2GB范围内)进行读写,实际?
??
???
??上,它们是在对不同的物理地址进行读写。让我们算一下,这段内存对应多少个?
??
???
??Page?Directory?Entry。我们知道一个Page?Directory?Entry对应4MB线性内存?
,那??
???
???
??4MB~2GB的内存就对应于1024?/?2?-?1?=?511个Page?Directory?Entry。也就?
是说??
???
???
??Win95通过操纵这511个Page?Directory?Entry实现了Win32应用程序“独立”的?
线性??
???
???
??地址空间。打个比方来说吧,现在有511个抽屉,上帝告诉A说:这些抽屉里都是?
金??
???
??币,同时告诉B说:这些抽屉里都是银币。并规定只有上帝能打开抽屉。其实抽?
屉里??
???
???
??可能只有一枚铜板,也可能什么都没有。这时,A想看看某个抽屉里到底是不是?
金币??
???
???
??,于是上帝就背地里临时往那个抽屉中放一个金币,然后打开抽屉让A看,于是?
A就??
???
??信了。这个伎俩同样作用于B,B也相信了那511个抽屉里都是他想要的银币。保?
护??
???
??模式下的操作系统(这里当然指Win95)就相当于上帝,而被骗的A和B就相当于?
运??
???
??行于保护模式下的Win32应用程序。那只操纵抽屉的上帝之手就是分页机制。??
???
???
??2GB~3GB??
???
??这部分内存空间我们称之为“应用程序共享内存区”,这里存放着Ring?3级应用?
程??
???
??序需要共享的数据和代码。其中包括Win95系统DLL(如User32.dll,Kernel32.?
dll等??
???
???
??)、内存映射文件、Win16应用程序以及DPMI调用分配的内存。??
???
???
??通过把要共享的数据映射到2GB~3GB之间的线性内存空间,可以实现Win32应用?
??
???
??程序间的数据共享。??
???
???
??Win16应用程序是需要共享线性地址空间的,这是Windows?3.1的历史遗留问题。?
为??
???
??了使以前运行于Windows?3.1的Win16应用程序能在Win95下有同样的运行效果,?
于??
???
??是Microsoft决定把Win16应用程序放到这段共享内存区来运行。应该说这是比较?
合??
???
??理的决策:既省时省力,又保证了对上一代产品的良好兼容性。??
???
???
??再从分页机制的角度上来思考一下这段“共享内存区”是如何实现的。2GB~3GB?
??
???
??的线性空间对应着256个Page?Directory?Entry。无论谁在运行,Win95都不会改?
变这??
???
???
??256个Page?Directory?Entry,也就是说2GB~3GB的线性地址空间对应的物理地?
址??
???
??是一样的。这样,Win95什么也不用做就实现了2GB~3GB线性地址空间的内存共?
??
???
??享。??
???
???
??3GB~4GB??
???
??这部分内存空间称为“系统内存区”。只有Ring?0级的VMM和VxD可以访问这部分?
??
???
??内存空间。这部分内存也是共享的。虽说Ring?3的应用程序无法直接共享这部分?
内??
???
??存空间(也就是说SDK中讲述的方法无法做到),但是我们还是称之为共享内存?
区??
???
??,至少从分页机制的角度上说,对应于这部分内存的Page?Directory?Entry是不?
变的??
???
???
??。其实Win32应用程序还是有办法访问这部分内存空间的。一般作法是,在VxD中?
分??
???
??配一块内存,然后把指向那块内存的32位地址指针传给Win32应用程序,这样就?
可??
???
??以在Win32应用程序中直接访问那块内存了。前面我们在讲0~4MB内存空间时,?
提??
???
??到High-linear?address,即DOS?VM备份的地方,就在3GB~4GB内存空间中。??
?
???
???
??对于Windows应用程序开发者来说,实现内存共享有时是非常重要的。对于VxD开?
??
???
??发者来说,还应注意,对于经常需要存取的共享内存页面,必须调用VMM的??
???
??_LinPageLock服务进行锁定,否则会出现Out-Of-Context?Memory?Reference?
。??
???
???
???
???
********************?
**??
***************************??
???
??VxD世界——硬件虚拟与虚拟设备驱动程序??
???
???
??·硬件虚拟·??
???
???
?
??回忆一下,在实模式DOS下,实模式的MS-DOS应用程序调用runtime?library中?
的??
???
??fgets或—kbhit函数,这些函数就会直接通过软中断int?21h调用MS-DOS服务(?
MS??
???
??-DOS服务会最终通过调用软中断int?16h调用BIOS服务),或者通过软中断?
int?16h??
???
???
??调用BIOS服务。BIOS通过IN?/?OUT指令来直接操纵键盘或中断控制器。??
???
???
??我们通过下面这张图来理解一下什么是硬件虚拟的实现(见图1)。??
???
???
??两个不同的MS-DOS应用程序可能要同时访问键盘,它们都感到自己在直接操纵?
着??
???
??硬件。其实从全局看,这两个MS-DOS应用程序对键盘的访问被VKD串行化了。??
?
???
??这两上MS-DOS应用程序其实是在操纵“虚拟的硬件”。??
???
???
??硬件虚拟的一个很关键的基础是:80386芯片的“port?trapping”功能,这使得?
VKD??
???
???
??可以捕获ring?3应用程序对键盘的访问。我们想起前面讲保护模式的内存管理时?
,提??
???
???
??到“虚拟内存”的概念,其时“虚拟内存”的实现也是因为80386芯片具有“??
???
??paging?trap?”功能。??
???
???
??·虚拟设备驱动程序·??
???
???
??Windows系统中,是VMM和VxDs实现了硬件虚拟(VMM本身就是一些VxD的集合??
???
??)。在Win95中,有两种VxD:static?VxD(静态加载的VxD)和dynamic?VxD(动?
??
???
??态加载的VxD)。在Windows?3.1中,只有一种VxD:static?VxD。Static?VxD的?
加载??
???
???
??需要把VxD放在SYSTEM.INI或注册表中。比如说,你写了一个文件名为Fool.VxD?
的??
???
??static?VxD,那么你可以在SYSTEM.INI中的如下位置加上一句:??
???
???
??……??
???
??[386Enh]??
???
??device=Fool.VxD??
???
??……??
???
???
??或是在注册表的如下位置加入如下一项:??
???
??\HKEY—LOCAL—MACHINE\System\CurrentControlSet\services\VxD\Fool.VxD??
?
???
???
??这样Win95在启动时就会自动加载Fool.VxD。??
???
??相对于Windows?3.1而言,Win95又增加了dynamic?VxD(动态加载的VxD)。在??
?
???
??Win95中,主要操纵dynamic?VxD的是configuration?Manager和Input/Output??
???
??Supervisor这两个功能模块(这两个模块自己却是static?VxD)。??
???
?
???
??图1?键盘的虚拟??
???
???
??VKD:Virtual?keyboard?device?VPICD:Virtual?PIC?device??
???
???
???
???
???
*****************************??
***************************??
???
??VxD世界——VxD文件格式??
???
???
?
??我们知道,MS-DOS下可执行文件.EXE是MZ格式,也就是说.EXE文件的前两个??
???
??字节是字符串“MZ”。而Windows?3.1的.EXE是NE(New?Executable)格式,??
???
??Win95和WinNT的.EXE是PE(Portable?Executable)格式,VxD则是LE(Linear??
?
???
??Executable)格式。但是有一点需要注意,在NE、PE和LE的头部,总嵌有一小段?
??
???
??DOS程序,它的作用是:当你在DOS下运行这几种.EXE文件时,它会提示你“This?
??
???
??program?cannot?be?run?in?DOS?mode”。??
???
???
??DEBUG?TEST.VXD??
???
???
??LE文件格式最早出自OS/2?2.0。这种格式的文件可以同时包含16位和32位代码,?
这??
???
??正是VxD需要的,因为VxD在加载的初始化阶段,需要进行一些实模式的操作,这?
??
???
??需要16位代码,而VxD的主要运行阶段是在32位环境中,这又需要32位代码,所?
以??
???
??LE文件格式正好适合于VxD。??
???
???
??VxD不仅有16位和32位两种代码,而且,它把数据段和代码段搅和在一起,只是?
通??
???
??过段前的标识来表明该段在运行时的特性。VxD之所以这样,是因为VxD所用的?
Flat??
???
??mode的代码和Data?selector有同样的基本地址和限制,这样当VxD想访问数据或?
代??
???
??码时,用哪个段寄存器都可以。VxD中常用的Segment?Class见表1。??
???
???
??在VxD中,LCODE、PCODE和PDATA段包含了主要的数据和代码。LCODE段所包??
???
??含的数据和代码必须总在内存中,我们在VxD中处理硬件中断的代码和相关数据?
必??
???
??须位于LCODE段中,否则,在处理硬件中断时就会出现可怕的Page?fault。??
???
???
??ICODE段包含的是VxD初始化时要完成的工作,当VxD完成初始后,ICODE段中的??
?
???
??代码和数据将被VMM抛弃。??
???
???
??RCODE中包含的是16位代码和数据,用作实模式初始化阶段。??
???
???
??SCODE中包含Static?Code和Data,一般来说,SCODE对于动态加载的VxD尤其有??
?
???
??用。试想一下,如果可动态加载的VxD包含了一个回调函数,当你卸载这个VxD后?
??
???
??,又需要这个回调函数继续发生作用,那你就得把这个回调函数放到SCODE中。?
再??
???
??者,如果你需要知道某个动态加载的VxD被加、卸载了几次,那可以在SCODE中放?
??
???
??个记数器,每次该VxD被加载时都把该记数器加一。??
???
???
??由于VxD的文件格式比较特殊,所以你必须使用可以产生LE格式的链接器(?
Linker??
???
??)。如果你要开发Windows?3.1的VxD,那得用Windows?3.1?DDK带的链接器。但?
是??
???
??如果开发Win95的VxD,那用MSVC?2.0及其以后版本的链接器就可以了。有一点需?
??
???
??要注意,MSVC?4.1的链接器由于存在一些小BUG,不能用于生成VxD。??
???
???
??表1??
???
??Segment?Class?描?述??
???
??LCODE?Page-locked?code?and?data??
???
??PCODE?Pageable?code??
???
??PDATA?Pageable?data??
???
??ICODE?Initialization-only?code?and?data??
???
??SCODE?Static?code?and?data??
???
??RCODE?Real-mode?initialization??
???
???
???
???
???
???
**********************************??
***************************??
???
??VxD世界——VxD开发的利器SoftIce/VToolsD??
???
???
??SoftIce的安装??
???
???
??对于从事Windows系统开发的人来说,SoftIce是必不可少的利器。目前SoftIce?
的最??
???
???
??新版本是3.24,有Win?95的版本,也有Win?NT的版本。SoftIce?3.24?for?Win??
95可以??
???
???
?
??从国内的一些FTP站下载。??
???
???
??在SoftIce的安装过程中,有一步是很重要的,那就是对显卡的测试。新版本的?
??
???
??SoftIce提供对更多种显卡的支持。如果说显卡不被SoftIce支持,那就意味着??
?
???
??SoftIce将无法正常工作。??
???
???
??在安装过程中,如果Test通不过,那就试着选一下Universal?Video?Driver或者?
Use??
???
???
??monochrome?card/monitor。如果还通不过的话,那就在Display?Adapter??
Selection??
???
???
??列表框中选择Stand?VGA再试。如果还不行的话,那就试着去下载一份new??
SoftIce??
???
??video?driver(在国内的FTP站上也可以找到),如果运气好的话,你的显卡会?
被??
???
??SoftIce支持。??
???
???
??安装过程结束后,重新启动计算机,按下Ctrl+D进入SoftIce,如果SoftIce能?
正常??
???
???
??工作,那我们的安装就成功了。你可以敲入help来看一下SoftIce的简要帮助,?
或者??
???
???
??,按下Ctrl+D返回Windows桌面。??
???
???
??VToolsD的安装??
???
???
??在VToolsD出世之前,VxD的开发者面对DDK浩如烟海的古怪的asm代码,能忍受下?
??
???
??来的人不多。VToolsD就是把幸福带给VxD开发者的天使。就凭这一点,VToolsD?
就??
???
??令VxD开发者趋之若鹜。VToolsD是Vireo公司的作品,目前国内的FTP站上有??
???
??VToolsD?2.03?for?Win95下载(如果运气好的话,还可以找到2.04、2.05b)。?
从国??
???
???
??内FTP站下载的VToolsD在安装时有一些需要注意的地方。??
???
???
??首先,在安装过程中会遇到对话框,询问你是否需要MASM6.11c。VToolsD并不包?
??
???
??含MASM?6.11c,但是,可以从DDK?for?Win95中找到MASM?6.11c。当你的程序中?
??
???
??需要嵌入汇编代码时,MASM?6.11c就不可缺少了(如果你只用C语言开发VxD,那?
??
???
??就用不着了)。??
???
???
??图1所示是对调试器的选择,请选择SoftIce。??
???
???
??图1?调制器的选择??
???
???
??在SoftIce的路径设置对话筐中,请选择SoftIce的Util16子目录。??
???
???
??再者,在安装过程中,图2所示的两个选项是不能选的,否则,安装过程将无法?
正常结??
束。??
???
???
??图2?不要选择的两个选项??
???
???
??在VToolsD安装完成之后,重新启动计算机,然后,进到VToolsD安装目录下的??
?
???
??examples\c\simple\下,敲如下的命令:??
???
???
??nmake-f?simple.mak??
???
???
??如果一切正常的话,将生成simple.vxd。??
???
???
??如果不行的话,那就在DOS提示符下敲set命令,检查一下环境变量的设置。下面?
是??
???
??我的系统中相应环境变量的设置,你需要根据自己系统的实际情况调整一下路径?
设置??
:??
???
???
??VTOOLSD=E:\VTD95??
???
??PATH=?%PAH%;C:\MSDEV\BIN;?E:\VTD95\BIN??
???
??INCLUDE=C:\MSDEV\INCLUDE;?E:\VTD95\INCLUDE??
???
??LIB=C:\MSDEV\LIB;E:\VTD95\LIB??
???
???
??把这些环境变量的设置加到autoexec.bat中去,以使其每次开机都自动设置。??
?
???
???
???
???
???
*********************************?
**??
*******************************??
???
??VxD世界——用VToolsD开发一个简单的VxD??
???
???
??这一次,我们讲一下如何用VToolsD开发一个最简单的VxD,以及用SoftIce进行?
源程序??
级的调试。??
???
???
??VToolsD的使用??
???
???
??在VtoolsD中,有一个最重要的VxD开发工具:QuickVxD。QuickVxD可以为我们自?
??
???
??动生成VxD源程序框架,而且QuickVxD提供了许多VxD的特性选项,例如可以选择?
??
???
??要生成的VxD是动态加载的或是静态加载的,要使用的编程语言是C还是C++等?
等。??
???
???
???
??我们要利用QuickVxD自动生成的是一个可动态加载的、基于C语言的VxD框架。之?
??
???
??所以选用动态加载的VxD,是为了调试VxD的方便。每次修改代码,重新编译连接?
??
???
??之后,要使VxD重新生效,如果采用静态加载的VxD,那就不得不重新启动电脑,?
??
???
??而若采用了动态加载的VxD,那只须使用VToolsD带的另一个开发工具VxDLoad就?
??
???
??可以卸出或重新加载内存中的VxD。之所以采用C语言而不是C++,是因为其简?
洁??
???
??易懂。请按照如图1~图4进行选择。按下Generate?Now按钮,我们就获得了动态?
加??
???
??载的、基于C语言的VxD的源程序。??
???
???
??如果您是按照上一篇文章中讲过的VToolsD的编译环境设置系统,那我们就可以?
编??
???
??译刚才生成的这个最简单的VxD了。在DOS提示符下输入指令:??
???
???
??nmake?-f?myfirst.mak??
???
???
??看一下当前目录下是否生成了myfirst.vxd,如果有,那我们下面准备对这个?
VxD进行??
???
???
??源程序级的调试。如果没有,那么很可能是您的编译环境没有正确配置,请找来?
上??
???
??一篇文章好好读读。??
???
???
??用VxDLoad加载myfirst.vxd(见图5)??
???
???
??按下Load按钮,会出现VxD?load?successfully消息框。??
???
???
??用SoftIce调试VxD??
???
???
??对于SoftIce选单作如下选择:??
???
??(1)File→Open?Module选择我们刚才生成的myfirst.vxd。??
???
??(2)Module→Translate,如果Symbol?Loader提示无法加载一些asm文件,那就?
跳过??
所有的asm文件。??
???
??(3)Module→Load。??
???
???
??按下Ctrl+D,进入SoftIce运行环境中(如果您还没有按照上一篇文章中安装??
?
???
??SoftIce的话,那就无法再进行下面的测试)。输入如下指令:??
???
??:file????
???
???
??myfirst.c??
???
??:file?myfirst.c??
???
???
??这时,在SoftIce中,您将会看到myfirst.c的源程序。??
???
???
??图1选项页面之一??
???
??图2选项页面之二??
???
??图3选项页面之三??
???
??图4可以生成VxD源程序了??
???
??图5用VxD?Load加载myfirst.vxd??
???
???
???
???
???
*********************************
**??
*******************************??
???
??VxD世界——VxD的结构??
???
???
??在上一次“走进VxD”世界中,我们用VToolsD生成了一个最简单的可以动态加载?
的??
???
??VxD——MYFIST程序。我们以这个例子为基础,不断地丰富其功能,并以此讲解?
一些V??
xD的基本技术。??
???
??下面是MYFIRST主要组成之一:MYFIRST.C。??
???
???
??//?MYFIRST.c?-?main?module?for?VxD?MYFIRST??
???
??#define?DEVICE_MAIN??
???
??#include?"myfirst.h"??
???
??#undef?DEVICE_MAIN??
???
??Declare_Virtual_Device(MYFIRST)??
???
??VOID_cdecl?V86_Api_Handler(VMHANDLE?hVM,?PCLIENT_STRUCT?pcrs)?{?}??
???
??VOID?_cdecl?PM_Api_Handler(VMHANDLE?hVM,?PCLIENT_STRUCT?pcrs)?{?}??
???
???
??DefineControlHandler(SYS_DYNAMIC_DEVICE_INIT,??
???
??OnSysDynamicDeviceInit);??
???
???
??DefineControlHandler(SYS_DYNAMIC_DEVICE_EXIT,??
???
??OnSysDynamicDeviceExit);??
???
???
??DefineControlHandler(W32_DEVICEIOCONTROL,?OnW32Deviceiocontrol);??
???
?
???
??BOOL?_cdecl?ControlDispatcher(??
???
???DWORD?dwControlMessage,??
???
???DWORD?EBX,?DWORD?EDX,??
???
???DWORD?ESI,?DWORD?EDI,?DWORD?ECX)??
???
???{?START_CONTROL_DISPATCH??
???
????ON_SYS_DYNAMIC_DEVICE_INIT(OnSysDynamicDeviceInit);??
???
????ON_SYS_DYNAMIC_DEVICE_EXIT(OnSysDynamicDeviceExit);??
???
????ON_W32_DEVICEIOCONTROL(OnW32Deviceiocontrol);??
???
???END_CONTROL_DISPATCH??
???
???return?TRUE;}??
???
???
??BOOL?OnSysDynamicDeviceInit()??
???
??{?return?TRUE;?}??
???
??BOOL?OnSysDynamicDeviceExit()??
???
??{?return?TRUE;?}??
???
???
??DWORD?OnW32Deviceiocontrol(PIOCTLPARAMS?p)??
???
??{?return?0;?}??
???
???
??记得在前面的文章中提到过,VxD可以与应用程序实现相互通信。我们在用??
???
??QuickVxD生成这个例子时,又选中了支持动态加载和Real/V86?Mode?API及??
???
??Protected?Mode?API等选项。上面程序中,函数V86_Api_Handler用来实现VxD与?
??
???
??16位的DOS应用程序通信,函数PM_Api_Handler用来实现VxD与16位的Windows??
???
??应用程序或DOS-Extended(DPMI)应用程序通信,函数OnW32Deviceiocontrol用?
来??
???
??实现VxD与32位的Windows应用程序通信。函数OnSysDynamicDeviceInit和??
???
??OnSysDynamicDeviceExit自然是用来控制VxD动态加载和卸载啦。??
???
???
??上面的代码中有两个宏DefineControlHandler和ControlDispatcher,用来把这?
些函数??
???
???
??与VxD的消息机制联系起来。好像我们搞清楚了,不,再仔细看一下,宏??
???
??DefineControlHandler和ControlDispatcher都只是定义了三个函数??
???
??OnW32Deviceiocontrol、OnSysDynamicDeviceInit和OnSysDynamicDeviceExit的?
消??
???
??息映射关系。我们很自然地想到,函数V86_Api_Handler和PM_Api_Handler呢??
???
??,为什么能肯定VxD一定用这两个函数与16位应用程序通信呢???
???
???
??让我们在VToolsD的include子目录下找一找,我们会发现VToolsC.h中有这两个?
函数??
???
???
??的定义。下面的代码摘自VToolsC.h。??
???
???
??#define?Declare_Virtual_Device_Ex(VName,?RefData)?\??
???
???
??extern?_C_?void?_cdecl?V86_Api_Handler(VMHANDLE?hVM,?PCLIENT_??
???
??STRUCT?pRegs);?\??
???
??extern?_C_?void?_cdecl?PM_Api_Handler(VMHANDLE?hVM,?PCLIENT_??
???
??STRUCT?pRegs);?\??
???
??extern?_C_?void?(?VXD_SERVICE_TABLE[])();?\??
???
??_EXC_?DDB?The_DDB?=?{?0,?DDK_VERSION,?VName##_DeviceID,?VName??
???
??##_Major,?\??
???
??VName##_Minor,?0,?{′?′,′?′,′?′,′?′,′?′,′?′,′?′,′?′},?
?\??
???
??VName##_Init_Order,?(DWORD)?LocalControlDispatcher,?\??
???
??(DWORD)?LocalV86handler,?\??
???
??(DWORD)?LocalPMhandler,?0,?0,?RefData,?(DWORD)?VXD_SERVICE_TABLE,?\??
???
??0,?\??
???
??0,?\??
???
??_SIG_}?;??
???
???
??//?This?is?the?standard?macro?for?declaring?a?DDB,?using?all?default??
value??
s.??
???
???
??#define?Declare_Virtual_Device(VName)?Declare_Virtual_Device_??
???
??Ex(VName,0)??
???
???
??从上面的代码中,我们可以看到,函数V86_Api_Handler和PM_Api_Handler被??
???
??宏Declare_Virtual_Device声明已在DDB(Device?Descriptor?Block)中,自然?
不??
???
??用再在MYFIRST.C中进行消息映射了。??
???
???
???
???
???
???
****************************************?
**??
*******************************??
???
??VxD世界__VxD的设备描述块与VxD?API??
???
???
??VxD设备描述块??
???
???
??用汇编语言描述MYFIRST.VxD的设备描述块(DDB?Device?Descriptor?Block)如下?
(??
???
??其实,如果是用DDK来开发VxD,那我们在每个VxD的源程序中都会见到这些代码?
??
???
??,只是VToolsD替我们封装了这些费解的东西):??
???
???
??Declare_Virtual_Device?MYFIRST,1,0,MYFIRST_Control,MYFIRST_??
?
???
??Device_ID,MYFIRST_Init_Order,MYFIRST_V86_API_Handler,??
???
??MYFIRST_PM_API_Handler??
???
???
??对于DDB的8个入口来说,只有前面4个是必须的,后面4个的缺省值为0,如果我?
们??
???
??的MYFIRST.VxD不输出V86?API,那么上面的代码应这样写:??
???
???
??Declare_Virtual_Device?MYFIRST,1,0,MYFIRST_Control,MYFIRST_??
???
??Device_ID,MYFIRST_Init_Order,,MYFIRST_PM_API_Handler??
???
???
??一般来说,MYFIRST_Init_Order是可以设为缺省值0的,因为我们一般不需要特?
??
???
??殊的初始化顺序。??
???
???
??你一定会奇怪MYFIRST_Control是怎么回事。读一下下面的代码,大概就明白了?
。??
???
???
??BeginProc?MYFIRST_Control??
???
???
??Begin_Control_Dispatch?MYFIRST_Control??
???
???
??Control_Dispatch?Sys_Dynamic_Device_Init,?OnSysDynamicDeviceInit??
???
???
??Control_Dispatch?Sys_Dynamic_Device_Exit,?OnSysDynamicDeviceExit??
???
???
??.........??
???
???
??End_Control_Dispatch?MYFIRST_Control??
???
???
??EndProc?MYFIRST_Control??
???
???
??对比一下VToolsD为我们生成的C程序:??
???
???
??BOOL?_cdecl?ControlDispatcher(??
???
???DWORD?dwControlMessage,??
???
???DWORD?EBX,DWORD?EDX,??
???
???DWORD?ESI,?DWORD?EDI,??
???
???DWORD?ECX)??
???
???{?START_CONTROL_DISPATCH??
???
???ON_SYS_DYNAMIC_DEVICE_INIT(OnSysDynamicDeviceInit);??
???
???ON_SYS_DYNAMIC_DEVICE_EXIT(OnSysDynamicDeviceExit);??
???
???END_CONTROL_DISPATCH??
???
???return?TRUE;}??
???
???
??Windows是基于消息机制的操作系统,这一点在VxD中也体现了出来。MYFIRST_??
?
???
??Control就是接收Windows消息的入口点。Windows发给MYFIRST_Control的消息??
?
???
??与发给Windows应用程序的消息不完全一样,前者包含了一些系统信息。MYFIRST?
??
???
??_Control在收到消息后,调用相应的控制过程。??
???
???
??VxD?API??
???
???
??在前面的文章中,我们说MYFIRST.VxD将支持Real/V86?Mode?API及Protected??
???
??Mode?API,这使得MYFIRST.VxD可以与V86应用程序或Win16应用程序通信。??
???
??MYFIRST.VxD输出的V86?API和PM?API就是??
???
???
??VOID?_cdecl?V86_Api_Handler(VMHANDLE?hVM,?PCLIENT_STRUCT?pcrs);??
???
???
??VOID?_cdecl?PM_Api_Handler(VMHANDLE?hVM,?PCLIENT_STRUCT?pcrs);??
???
???
??一个问题很快就摆在我们面前:如何在我们的应用程序中调用到这两个API???
???
???
??读一下这段代码:??
???
???
??DWORD?NEAR?PASCAL?GetAPIEntry(WORD?VxD_ID)??
???
??{DWORD?Entry_Point;??
???
???
??_asm{??
???
???mov?AX,?1684h??
???
???mov?BX,?WORD?PTR?SS:?[VxD_ID]??
???
???sub?DI,?DI??
???
???mov?ES,?DI??
???
???int?2Fh??
???
???mov?WORD?PTR?SS:?[Entry_Point][0],?DI??
???
???mov?WORD?PTR?SS:?[Entry_Point][2],?ES??
???
????????????}?return?Entry_Point;}??
???
???
??这段代码可以用在MS_DOS应用程序或是Win16应用程序中,函数GetAPIEntry将分?
??
???
??别返回V86_Api_Handler的地址或PM_Api_Handler的地址。??
???
???
??等一下,函数GetAPIEntry的入口参数VxD_ID是怎么回事?嗯,问得好。如果你?
一??
???
??直在读我的文章,那你会发现我们在前面有一个失误:在用QuickVxD生成??
???
??MYFIRST.VxD的源程序时,把MYFIRST.VxD的DeviceID置成了UNDEFINED_??
???
??DEVICE_ID。通过在VToolsD\include\Vmm.h中查找,可以看到:??
???
???
??#define?UNDEFINED_DEVICE_ID?0x00000??
???
???
??也就是说所有UNDEFINED_DEVICE_ID的VxD的DeviceID都置成了0。如果我们??
???
??向函数GetAPIEntry传递MYFIRST_DeviceID,那我们很可能无法获得??
???
??MYFIRST.VxD中的API的地址,因为我们的DeviceID不是惟一的,Windows无法在?
??
???
??众多DeviceID为0的VxD中找到我们的MYFIRST.VxD。那怎么办呢???
???
???
??解决方案有两个:??
???
???
??方案一:??
???
??再用QuickVxD重新生成MYFIRST.VxD的源程序。记着在Device?Parameters页中填?
??
???
??写Device?ID为某个值,这个值尽量大一些,因为比较小的DeviceID都让?
Microsoft或??
???
???
??是别的硬件开发商注册了(注册是需要银子的),为了保证不与系统中现存的?
VxD??
???
??的DeviceID发生冲突,我们只好把DeviceID设得大一些,比如说0xAAAA。??
???
???
??方案二:??
???
??编辑一下MYFIRST.H,把MYFIRST_DeviceID改了,改过之后的MYFIRST.h如下??
???
??:??
???
???
??#include?〈vtoolsc.h〉??
???
??#define?MYFIRST_Major?1??
???
??#define?MYFIRST_Minor?0??
???
??#define?MYFIRST_DeviceID?0xAAAA??
???
??#define?MYFIRST_Init_Order?UNDEFINED_INIT_ORDER??
???
???
??好了,我们已经准备好与我们的MYFIRST.VxD通信了。??
评论