VxD编程入门教程
一、Windows 95 DDK的安装
安装Windows 95 DDK一般需要先安装Win32 SDK,原因是Windows 95 DDK
需要Win32 SDK的16位资源编译器,但是Win32 SDK很大(一整张光盘
的容量),而且国内难以买到,ftp站上也较少见,即使有,下载也很困难。
笔者经过一段时间的摸索,找到了安装Windows 95 DDK的几种简单方法,
现将方法介绍如下:
方法之一:使用第三方资源编译器
1、修改注册表,仿真Win32 SDK已经安装的情况。
建立一个名为WIN32SDK.REG的注册表文件,内容为:
REGEDIT4
[HKEY_USERS.DefaultSoftwareMicrosoftWin32SDK]
[HKEY_USERS.DefaultSoftwareMicrosoftWin32SDKDirectories]
"Install Dir"="C:\MSTOOLS"
在资源管理器中双击此文件,将此文件中的内容添加到注册表。就可以安
装Windows 95 DDK了。
2、运行Windows 95 DDK中的SETUP.EXE文件,安装Windows 95 DDK到
C:DDK。
3、安装MASM 6.11到C:MASM611,安装完成后将未安装的Windows 95 DDK
中的MASM611C目录下的文件覆盖到C:MASM611BIN。
4、安装Visual C++ 5.0(4.0也可以,但不宜使用6.0)到C:Program
FilesDevStudioVC。
5、建立C:MSTOOLSBINW16目录,拷贝资源编译器。
Windows 95 DDK需要一个能将Win32资源源文件编译成16位资源的资源
编译器。如果有Win32 SDK,可以将BINW16目录下的文件直接拷贝到
C:MSTOOLSBINW16,如果没有Win32 SDK,则可以使用第三方的资源编译
器,这里以使用Borland的资源编译器为例:
准备Turbo MASM 5.0一套,用UNPAK工具解压缩CMDLINE.PAK文件,找
到下面三个文件:
BRC.EXE
BRCC32.EXE
RW32CORE.DLL
将这三个文件拷贝到C:MSTOOLSBINW16,并将BRC.EXE改名为RC.EXE即
可。
6、修改CONFIG.SYS增大环境变量空间。
在CONFIG.SYS文件最后加入一行:
SHELL=C:WINDOWSCOMMAND.COM /E:8192 /P
7、进入Windows 95 MS-DOS方式,初始化编译环境(最好建立一个批处
理文件):
C:MASM611BINRNEW-VARS.BAT
C:DDKDDKINIT.BAT 32 BASE
(编译的设备驱动程序不同,参数也不同)
C:Program FilesDevStudioVCinVCVARS32.BAT
就可以使用Windows 95 DDK了,连接时出现的警告可以不理会。
方法之二:使用Windows 98 DDK
完整版的Windows 98 DDK(约30M)包括Windows 95 DDK、全套SDK编译
器和MASM 6.11C汇编器,安装方法非常简单:安装Windows 98 DDK和Visual
C++ 5.0,然后直接运行“Check Build Environment”(编译带调试信息
的设备驱动程序)或者“Free Build Environment”(编译正式发布版本
的设备驱动程序)程序项即可。
二、一个拦截Windows 95/98文件操作的VxD
VxD――虚拟设备驱动程序,顾名思义,VxD是用来控制硬件设备的,那么
这里为什么要讲一个拦截Windows 95/98文件操作的VxD呢?其实,VxD
不仅仅可以用来控制硬件设备,因为VxD工作在80386保护模式Ring 0
特权级(最高特权级)上,而一般的应用程序工作在Ring 3特权级(最
低特权级)上,所以VxD可以完成许多API不能完成的功能,例如端口读
写、物理内存读写、中断调用、API拦截等。正因为如此,VxD在Windows
系统编程中有广泛用途,其实大家一般都是碰到Windows API不能解决或
者难以解决的问题才考虑编写VxD解决问题。
这里介绍的拦截Windows 95/98文件操作的VxD可以用来拦截Windows 95
/98(Windows NT不支持VxD)的所有文件操作,那么这个VxD有什么用
途呢?最大的用途可能是――病毒防火墙,用来对文件操作进行过滤,可
以进行动态病毒检测和动态杀毒。这个VxD使用的原理和目前流行的CIH
病毒传染的原理是基本相同的。
(其实大家如果要问我为什么要写这么一个VxD,那是因为――我是Virus
版的版主啦)
该VxD的文件名是FILEHOOK.VXD,源程序(FILEHOOK.ASM)如下:
;FILEHOOK.VXD--拦截Windows 95/98文件操作的VxD
.386p
.XLIST
INCLUDE VMM.Inc
INCLUDE VWin32.Inc
INCLUDE Shell.Inc
MASM=1
INCLUDE IFS.Inc
INCLUDE IFSMgr.Inc
.LIST
;VxD声明
Declare_Virtual_Device
FILEHOOK,1,0,VxD_Control,Undefined_Device_ID,,,
;保护模式数据段
VxD_DATA_SEG
Prev_File_System_Api_Hook dd 0
In_File_System_Api_Hook db 0
Message1 db 'Open file !',0
Caption1 db 'FILEHOOK',0
VxD_DATA_ENDS
;保护模式代码段
VxD_CODE_SEG
;系统控制过程
BeginProc VxD_Control
Control_Dispatch SYS_DYNAMIC_DEVICE_INIT,VxD_Device_Init
Control_Dispatch SYS_DYNAMIC_DEVICE_EXIT,VxD_Device_Exit
Control_Dispatch W32_DEVICEIOCONTROL,VxD_IOCTL
clc
ret
EndProc VxD_Control
;IOCTL 控制(设备I/O控制)过程
BeginProc VxD_IOCTL
;获取DeviceIoControl控制代码
mov ecx,[esi.dwIoControlCode]
cmp ecx,1
jz Install_File_System_Api_Hook
cmp ecx,2
jz Uninstall_File_System_Api_Hook
jmp VxD_IOCTL_Exit
;安装文件系统API 钩子
Install_File_System_Api_Hook:
mov eax,OFFSET32 File_System_Api_Hook
VxDCall IFSMgr_InstallFileSystemApiHook
or eax,eax
jz Error_Handler
;保存上一个文件系统API 钩子地址
mov Prev_File_System_Api_Hook,eax
jmp VxD_IOCTL_Exit
;移去文件系统API 钩子
Uninstall_File_System_Api_Hook:
mov eax,OFFSET32 File_System_Api_Hook
VxDCall IFSMgr_RemoveFileSystemApiHook
cmp eax,0FFFFFFFFH
jz Error_Handler
jmp VxD_IOCTL_Exit
;IOCTL 控制过程结束
VxD_IOCTL_Exit:
xor eax,eax
clc
ret
;错误处理
Error_Handler:
mov eax,0FFFFFFFFH
stc
ret
EndProc VxD_IOCTL
;VxD_Device_Exit过程
BeginProc VxD_Device_Exit
clc
ret
EndProc VxD_Device_Exit
;文件系统API 钩子过程(C语言调用方式)
BeginProc File_System_Api_Hook,CCALL
ArgVar FSDFnAddr,DWORD
ArgVar FunctionNum,DWORD
ArgVar Drive,DWORD
ArgVar ResourceFlags,DWORD
ArgVar CodePage,DWORD
ArgVar pir,DWORD
EnterProc
pushad
;防止重入
cmp byte ptr In_File_System_Api_Hook,00h
jnz Prev_Hook
;比较是打开文件操作吗?
cmp dword ptr FunctionNum,IFSFN_OPEN
jnz Prev_Hook
;设置重入标志
inc byte ptr In_File_System_Api_Hook
;取当前VM句柄
VMMCall Get_Cur_VM_Handle
;显示消息框
mov eax,MB_ICONASTERISK+MB_OK
mov ecx,OFFSET32 Message1
mov edi,OFFSET32 Caption1
mov esi,0
mov edx,0
VxDCall Shell_Message
;取消重入标志
dec byte ptr In_File_System_Api_Hook
;转到上一个文件系统API 钩子地址
Prev_Hook:
popad
LeaveProc
mov eax,Prev_File_System_Api_Hook
jmp [eax]
Return
EndProc File_System_Api_Hook
VxD_CODE_ENDS
;保护模式初始化代码段
VxD_ICODE_SEG
;VxD_Device_Init过程
BeginProc VxD_Device_Init
clc
ret
EndProc VxD_Device_Init
VxD_ICODE_ENDS
end
该VxD在设备控制过程(VxD_Control过程)中处理了3个系统控制消息,
分别是SYS_DYNAMIC_DEVICE_INIT(动态VxD初始化)、
SYS_DYNAMIC_DEVICE_EXIT(动态VxD退出)和W32_DEVICEIOCONTROL(设
备I/O控制),对应的消息处理过程分别是VxD_Device_Init、
VxD_Device_Exit和VxD_IOCTL。其中VxD_Device_Init过程和
VxD_Device_Exit过程只清除进位标志返回(表示成功),VxD_IOCTL过程
是Windows 95/98应用程序与VxD通信的接口,完成文件系统API钩子
的安装和移去工作,[esi.dwIOControlCode]中是设备I/O控制代码,该
控制代码为1时安装文件系统API钩子,为2时移去文件系统API钩子。
File_System_Api_Hook是文件系统API钩子过程,这里作为一个简单的实
例,钩子过程判断是否是打开文件操作,如果是则显示一个简单的消息框,
然后跳转到上一个文件钩子(相当于旧的文件系统API入口)。如果需要
扩充功能,可以在该过程中增加代码。
汇编连接VxD需要一个模块定义文件和一个NMAKE文件(手工汇编连接当
然也可以)。这两个文件都可以直接用DDK中的GENERIC实例中的模块定
义文件和NMAKE文件修改而成,模块定义文件(FILEHOOK.DEF)如下:
VXD FILEHOOK DYNAMIC
DESCRIPTION 'File System API Hook Program'
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
_LMSGTABLE 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
FILEHOOK_DDB @1
NMAKE文件(MAKEFILE)如下:
!ifdef MASTER_MAKE
BUILD_BITS=32
BUILD_TYPE=base
!INCLUDE $(DDKROOT)master.mk
!endif
NAME = filehook
# supply the location of a 16-bit linker
LINK =
# Definitions for the debug level
!ifdef DEBUG
DDEBUG =-DDEBLEVEL=1 -DDEBUG
!else
DDEBUG =-DDEBLEVEL=0
!endif
# Definitions for MASM 6 Assembler
ASM = ml
AFLAGS = -coff -DBLD_COFF -DIS_32 -W2 -c -Cx -Zm -DMASM6 $(DDEBUG)
ASMENV = ML
LFLAGS = /VXD /NOD
# MASM 6 only inference rules
.asm.obj:
set $(ASMENV)=$(AFLAGS)
$(ASM) -Fo$*.obj $<
all : $(NAME).VXD
OBJS = filehook.obj
filehook.obj: filehook.asm
$(NAME).vxd: $(NAME).def $(OBJS)
link @<<$(NAME).lnk
$(LFLAGS)
/OUT:$(NAME).vxd
/MAP:$(NAME).map
/DEF:$(NAME).def
$(OBJS)
<<
mapsym -s -o $(NAME).sym $(NAME).map
clean:
-@del *.obj
-@del *.vxd
-@del *.exp
-@del *.lib
-@del *.map
-@del *.sym
有了这两个文件,运行NMAKE即可汇编连接VxD。
三、Windows 95/98应用程序与VxD的通信
Windows 95/98应用程序与VxD的通信一般使用DeviceIoControl函数,
这里给出一个实例源程序(FHTEST.C)如下:
file://拦截Windows 95/98文件操作测试程序
#include
#include "tchar.h"
#define INSTALL_FILE_SYSTEM_API_HOOK 1
#define UNINSTALL_FILE_SYSTEM_API_HOOK 2
static TCHAR szAppName[]=_T("FHTEST");
static TCHAR szAppTitle[]=_T("拦截Windows 95/98文件操作测试程
序");
static HANDLE hDevice;
LRESULT CALLBACK WndProc(HWND hWnd,UINT Message,WPARAM
wParam,LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE
hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
HWND hWnd;
WNDCLASSEX wcex;
MSG Msg;
file://本程序不能在Windows NT中运行
if(GetVersion()<0x80000000)
{
MessageBox(NULL,_T("本程序不能在Windows NT中运行!
"),szAppTitle,MB_ICONINFORMATIONMB_OK);
return FALSE;
}
if(!hPrevInstance)
{
wcex.cbSize=sizeof(WNDCLASSEX);
wcex.style=CS_HREDRAWCS_VREDRAW;
wcex.lpfnWndProc=WndProc;
wcex.cbClsExtra=0;
wcex.cbWndExtra=0;
wcex.hInstance=hInstance;
wcex.hIcon=LoadIcon(hInstance,IDI_APPLICATION);
wcex.hCursor=LoadCursor(NULL,IDC_ARROW);
wcex.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName=NULL;
wcex.lpszClassName=szAppName;
wcex.hIconSm=LoadIcon(hInstance,IDI_APPLICATION);
if(!RegisterClassEx(&wcex)) return FALSE;
}
hWnd=CreateWindow(szAppName,szAppTitle,WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
0,0,hInstance,NULL);
if(!hWnd) return FALSE;
ShowWindow(hWnd,nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&Msg,0,0,0))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT Message,WPARAM
wParam,LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps;
DWORD cb;
BOOL bResult;
switch(Message)
{
case WM_CREATE:
hDevice=CreateFile("\\.\FILEHOOK.VXD",0,0,NULL,0,FILE_FLA
G_DELETE_ON_CLOSE,NULL);
if(hDevice!=INVALID_HANDLE_VALUE)
{
bResult=DeviceIoControl(hDevice,INSTALL_FILE_SYSTEM_API_HOOK,
NULL,0,NULL,0,&cb,0);
if(bResult) MessageBox(hWnd,_T("文件系统API 钩子安装
成功!"),szAppTitle,MB_ICONINFORMATIONMB_OK);
else MessageBox(hWnd,_T("不能安装文件系统API 钩子!
"),szAppTitle,MB_ICONINFORMATIONMB_OK);
}
else
{
MessageBox(hWnd,_T("不能打开FILEHOOK.VXD!
"),szAppTitle,MB_ICONINFORMATIONMB_OK);
}
break;
case WM_PAINT:
hDC=BeginPaint(hWnd,&ps);
EndPaint(hWnd,&ps);
break;
case WM_DESTROY:
if(hDevice!=INVALID_HANDLE_VALUE)
{
bResult=DeviceIoControl(hDevice,UNINSTALL_FILE_SYSTEM_API_HO
OK,NULL,0,NULL,0,&cb,0);
if(bResult) MessageBox(hWnd,_T("文件系统API 钩子移去
成功!"),szAppTitle,MB_ICONINFORMATIONMB_OK);
else MessageBox(hWnd,_T("不能移去文件系统API 钩子!
"),szAppTitle,MB_ICONINFORMATIONMB_OK);
CloseHandle(hDevice);
}
else
{
MessageBox(hWnd,_T("不能打开FILEHOOK.VXD!
"),szAppTitle,MB_ICONINFORMATIONMB_OK);
}
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd,Message,wParam,lParam);
}
return 0;
}
本程序使用CreateFile函数动态装载VxD,然后用DeviceIoControl函数
向VxD发送设备I/O控制代码,安装和移去文件系统API钩子,最后用
CloseHandle函数动态移去VxD。
运行该程序时,如果文件系统API钩子安装成功,会显示出“文件系统API
钩子安装成功!”的消息框,然后只要进行打开文件操作,就会显示出“Open
File !”的消息框。退出该程序时,如果文件API钩子移去成功,会显示
出“文件API钩子移去成功!”的消息框,以后进行打开文件操作时不再
显示消息框。
四、小结
上述实例演示了一个完整的拦截Windows 95/98文件系统API的VxD和
Windows 95/98应用程序与该VxD通信的方法。该VxD可以作为一个基本
框架,稍作修改即可用于病毒防火墙等软件。其实VxD的功能远不止这些,
充分利用VxD的功能可以编写出许多用API不能编写出的高级应用程序。
本教程只是一个简单的VxD编程例子,因为笔者水平所限,加之时间较少,
写得较为简单,还请大家见谅,同时也欢迎大家讨论。本教程中使用的VMM
功能、VxD功能和API函数请查阅相关手册或者帮助。