如何用 Delphi 编写 VxD 设备驱动程序
关键词:Delphi控件杂项
作者:Emil Biserov(dinfo@mail.primorye.ru)(Russion)
英语翻译:Vitaly Zayko(zayko@vitsoftware.com)
中文翻译改编:Riceball(teditor@mailroom.com)
前言
; 用 Delphi 3.0 编写 VxD 设备驱动程序,在Delphi 3 下编译通过,Delphi 2 下没有测试,
Delphi 4 建立的 Object 文件 M$ Linker 5.12.8181 不能识别,这里使用的汇编器是M$的Macro
Assembler ver. 6.11d ,联结器是M$ Incremental Linker ver. 5.12.8181 ,它们来自 Windows
8DDK(http://www.microsoft.com/ddk/ddk98.htm)。
介绍
; Windows 存在有两种类型的 VxD 设备驱动程序:
; ; 1、静态(Static) VxD ,装入操作系统并永久的存在于内存中;
; ; 2、动态(Dynamic) VxD,当需要时才调入内存,用完后关闭VxD即可释放内存。
; Inprise Delphi 有能力建立任何一种类型的 VxD 设备驱动程序,下面我们将介绍如何建立动
态 VxD。
; 当 Win32 应用程序打开一个 VxD “虚拟”设备时,VWIN32 使用 LoadDevice 将 VxD 装入内
存,并建立消息W32_DEVICEIOCONTROL ,发向 VxD。
; 也就是说,VxD 至少应该响应以下两个系统信息和编写以下的一个函数:
; ; SYS_DYNAMIC_DEVICE_INIT
; ; SYS_DYNAMIC_DEVICE_EXIT
; ; W32_DEVICEIOCONTROL 函数.
; 消息 SYS_DYNAMIC_DEVICE_INIT 在尝试装入 VxD 时发送到 VxD ,消息SYS_DYNAMIC_DEVICE_EXIT
在尝试动态交换时发送到 VxD ,消息的处理者在成功处理后,应该在寄存器 AX 中返回 VXD_SUCCESS
标志。
; W32_DEVICEIOCONTROL 的 dwService 参数有以下的值:
; ; DIOC_OPEN 当 VxD 通过 CreateFile() 函数尝试打开操作时发送(在 SYS_DYNAMIC_DEVICE_INIT
消息后),如果成功返回 NO_ERROR (0);
; ; DIOC_CLOSEHANDLE 当 VxD 通过 CloseHandle() 函数尝试关闭操作时发送(在 SYS_DYNAMIC_DEVICE_EXIT前)
; ; 所有其它的值 > 0 意味着不同的函数调用(由 dwIoControlCode 给出),当 VxD 被 DeviceIoControl
函数调用时。
启动模块(vxdmain.asm)
...
extrn SysDynamicDeviceInit
ROC
extrn SysDynamicDeviceExit
ROC
extrn W32DeviceIoControl ;
ROC
...
; ; ; ; ; ; PUBLIC ;DELPHIIO_DDB
; ; ; ; ; ; Public ;@@HandleFinally
; ; ; ; ; ; Public ;@initialization
...
Control_0 ; ;proc
; ; cmp ; ;eax, SYS_DYNAMIC_DEVICE_INIT
; ; jnz ; ;short chkSysDynExit
; ; call ; ;SysDynamicDeviceInit
; ; cmp ; ;eax, 1
; ; retn ; ;
;-------------
chkSysDynExit:
; ; cmp ; ;eax, SYS_DYNAMIC_DEVICE_EXIT
; ; jnz ; ;short chkDevIOCtl
; ; call ; ;SysDynamicDeviceExit
; ; cmp ; ;eax, 1
; ; retn ; ;
;-------------
chkDevIOCtl:
; ; cmp ; ;eax, W32_DEVICEIOCONTROL
; ; jnz ; ;short loc_ret
; ; push ; ;esi
; ; push ; ;edx
; ; push ; ;ebx
; ; push ; ;ecx
; ; call ; ;W32DeviceIoControl
; ; cmp ; ;eax, 1
; ; retn ; ;
;-------------
loc_ret:
; ; clc ; ;
; ; retn ; ;
Control_0 ; ;endp
@@HandleFinally:
@initialization:
; ; ; ; ; ; ret
_LTEXT ; ends
; ; ; ; ; ; ; ; ; ; ; ; END
; Delphi 会为单元的 initialization/finalization 建立代码调用外部过程 HandleFinaly
和 initialization ,即使 initialization/finalization 在单元中不存在。因此我们在汇
编的启动文件中建立空的外部过程入口。
主 Delphi 程序单元(vxdProcs.pas) ;
...
procedure ShellMessage(Handle, Flags : integer; const Message, Caption : PChar; ;
; ;Callback, ReferenceData : pointer); stdcall; assembler;
asm
; mov ; ;ebx, Handle ; ; ; ;// virtual machine handle
; mov ; ;eax, Flags ; ; ; ;// message box flags
; mov ; ;ecx, Message ; ; ; ;// address of message text
; mov ; ;edi, Caption ; ; ; ;// address of caption text
; mov ; ;esi, Callback ; ; ; ;// address of callback
; mov ; ;edx, ReferenceData ; ; ; ;// reference data for callback
; int ; ;20H ; ; ; ; ; ;// VxDCall
; dd ; ; 170004h ; ; ; ; ; ;// Shell_Message
end;
function SysDynamicDeviceInit : INTEGER;
begin
; ShellMessage(0, $10, Copyright, 'SysDynInit: Hello from Delphi VxD !!!', nil, nil);
; Result := VXD_SUCCESS;
end;
function SysDynamicDeviceExit : INTEGER;
begin
; ShellMessage(0, $10, Copyright, 'SysDynDevExit: Bye from Delphi VxD !!!', nil, nil);
; Result := VXD_SUCCESS;
end;
function W32DeviceIoControl(dwService : INTEGER;
; ; ; ; ; ; ; ; ; ; ; ; ; ; dwDDB : INTEGER;
; ; ; ; ; ; ; ; ; ; ; ; ; ; hDevice : INTEGER;
; ; ; ; ; ; ; ; ; ; ; ; ; ; lpDIOCParms : pointer) : INTEGER;
begin
; ShellMessage(0, $10, Copyright, 'W32DevIOCtl', nil, nil);
; if (dwService = DIOC_OPEN) then
; begin
; ; ; Result := NO_ERROR;
; end
; else if (dwService = DIOC_CLOSEHANDLE) then
; begin
; ; ; Result := VXD_SUCCESS;
; end
; else if (dwService > MAX_PASVXD_W32_API) then
; begin
; ; ; Result := ERROR_NOT_SUPPORTED;
; end
; ; else
; begin
; ; ; Result := VXD_SUCCESS; ;
; end;
end;
...
[译者:好了,简单的 VxD 设备驱动程序编写完毕了。你可以将它当作一个写 VxD 设备驱动程序的模板。]
附一:Make.bat
D:/VISUAL~1/98DDK/BIN/Win98/ml -coff -DBLD_COFF -DIS_32 -W2 -c -Cx -Zm -DMASM6 vxdmain.asm
call dcc3.bat -J vxdprocs.pas
D:/VISUAL~1/98DDK/BIN/link /DEF:vxddef.def /VXD vxdmain.obj vxdprocs /OUT:delphiio.vxd
附二:
现在让我们来编写对该 VxD 的测试程序,两个按钮:一个打开 VxD;一个关闭 VxD。
const
VxDName = '//./DELPHIIO.VXD';
...
function TVxDTestForm.OpenVxDDriver: boolean;
begin
HVxDHandle := CreateFile(VxDName,0,0,nil,0,FILE_FLAG_DELETE_ON_CLOSE,0);
Result := HVxDHandle <> INVALID_HANDLE_VALUE;
end;
procedure TVxDTestForm.CloseVxDDriver;
begin
if HVxDHandle <> INVALID_HANDLE_VALUE then begin
; ;CloseHandle(HVxDHandle);
; ;HVxDHandle := INVALID_HANDLE_VALUE;
end;
end
...
==================
K!俄国鬼子就是厉害!据说这个东东99年就发表了,但我们现在才看到!
懂不懂呀!你!
procedure ShellMessage()是在vxdProcs.pas中主要用来屏幕显示的过程,中间调用了
汇编,是因为在VxD所处于的Ring0级,绝大多数Winapi不能使用,所有基于Windows消
息的事件服务程序和调用都不起作用,这时,不用汇编写,那要用什么写呀?
vxdProcs.pas主要用于产生vxdProcs.obj文件,与vxdmain.obj一起链接成vxd。vxdmain.asm
是一个标准的VxD调用的头,代码量倒是不大,而且基本上也不需要大改动就可用了。
用这种方法写VxD,你的精力可以将大的花在vxdProcs.pas这个单元的编写上。它是纯
功能部分,纯IDE环境的、纯Pascal语法的、可使用熟悉的BASM的代码!看起来已经是
很不错了。黑黑。
仔细想了想,用VC来写这段代码(我的意思是vxdProcs.pas这样的一段代码),也不过
如此嘛!哈哈哈哈。
用了一晚上的时间测试。结果如下:
1. 一定要用Delphi3来编译vxdProcs.pas!D4/D5都不成。
2. 一定要用生成C obj方式,即-J开关。生成C++ Obj不行。即不能用-JP开关。
3. 汇编编译器用MASM/TASM均可。但建议找个高版本。
4. 链接器一定要用WinDDK中所带的Link.exe,有且仅有这个版本的Link.exe支持生成VxD!
5. 测试程序能在Win98上使用,但在WinNT/2K上均无效。
6. 在Win98上测试结果:进入蓝屏方式,显示一条信息!!!系统Ring0权限已经获得!
7. Win98 DDK下载地址:http://www.microsoft.com/ddk/install98ddk.htm