分享
 
 
 

浅谈 VxD 回调 Win32 应用程序 -- 赵世平

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

浅谈 VxD 回调 Win32 应用程序

作者:赵世平

Windows 95 / 98 的虚拟外壳设备( Virtual Shell Device )提供了 VxD 直接回调 Win16 应用程序函数的 VxD 服务,但是没有提供 VxD 直接回调 Win32 应用程序的 VxD 服务。不过 Windows 95 / 98 还是提供了两种 VxD 回调 Win32 应用程序的方法。方法之一是使用 VWIN32.VXD 提供的“异步过程调用( APC )”功能。 Win32 应用程序首先动态加载 VxD ,并使用 DeviceIoControl 函数将回调函数的地址传递给 VxD ,然后 Win32 应用程序调用 SleepEx / WaitForMultipleObjectsEx / WaitForSingleObjectEx 函数,将 Win32 应用程序自身置为“挂起”状态,这时 VxD 可以通过 VWIN32.VXD 提供的 _VWIN32_QueueUserApc 服务调用 Win32 应用程序的回调函数。该方法较简单,目前大多数回调 Win32 应用程序的 VxD 均使用该方法[例如瑞星杀毒软件( RAV )的实时监控 VxD ( RAV_IO.VXD )即使用该方法回调 Win32 查毒/杀毒程序],《 Windows 95 System Programming 4 》一书的配套光盘中有一个名为 IFSMONITOR 的实例程序详细讲述了该方法,由于该书的配套光盘在国内很多 FTP 服务器上都可以找到,此处就不再详述了。

方法之二是一种比较灵活的方法,这种方法充分利用了 Win32 应用程序的多线程特点和线程间通信的事件机制。 Win32 应用程序设置两个线程并定义一个事件,主线程负责动态加载/卸载 VxD 和通过 DeviceIoControl 函数与 VxD 通信,辅助线程通过 ResetEvent 函数和 WaitForSingleObject / WaitForSingleObjectEx 函数暂时挂起, VxD 可以通过 VWIN32.VXD 提供的 Win32 事件服务中的 _VWIN32_SetWin32Event 服务唤醒辅助线程,从而间接实现 VxD 回调 Win32 应用程序。由于 VWIN32.VXD 提供了与 Win32 API 几乎完全对应的 Win32 事件服务,所以该方法极其灵活,甚至可以通过定义两个事件实现 VxD 在回调 Win32 应用程序时与 Win32 应用程序完全同步。

笔者为了验证上述方法,编写了一个动态加载/卸载 VxD 的 Win32 应用程序和一个回调 Win32 应用程序的 VxD 。其中 Win32 应用程序用 Delphi 5.0 编写,选用 Delphi 5.0 的原因是 Delphi 5.0 实现多线程非常容易,而且不必将大量代码用在 Win32 应用程序界面上; VxD 使用 VToolsD 2.03 编写,是一个挂接实时钟中断( IRQ 8 )的 VxD ,该 VxD 参照 VToolsD 2.03 中的 CHIME 实例程序编写。程序代码如下:

Win32 应用程序工程文件( TMR_TEST.DPR ):

program TMR_TEST;

uses

Forms,

TT_MAIN in 'TT_MAIN.pas' {TimerTestMain},

TMR_CLBK in 'TMR_CLBK.pas';

{$R *.RES}

begin

Application.Initialize;

Application.CreateForm(TTimerTestMain, TimerTestMain);

Application.Run;

end.

Win32 应用程序主窗体/主线程( TT_MAIN.PAS ):

unit TT_MAIN;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

Menus, SyncObjs, TMR_CLBK, StdCtrls;

type

TOpenVxDHandle=function(hSource:THandle):THandle;stdcall;

TTimerTestMain = class(TForm)

MainMenu1: TMainMenu;

File1: TMenuItem;

Exit1: TMenuItem;

Callback1: TMenuItem;

Start1: TMenuItem;

Label1: TLabel;

procedure FormShow(Sender: TObject);

procedure FormClose(Sender: TObject; var Action: TCloseAction);

procedure Exit1Click(Sender: TObject);

procedure Start1Click(Sender: TObject);

private

{ Private declarations }

Handle1:THandle;

TimerCallback1:TTimerCallback;

public

{ Public declarations }

end;

const

TIMER_DIOC_SET_VXD_EVENT=101;

var

TimerTestMain: TTimerTestMain;

implementation

{$R *.DFM}

procedure TTimerTestMain.FormShow(Sender: TObject);

begin

Event1:=TEvent.Create(nil,True,False,'');

Handle1:=CreateFile('\\.\TIMER.VXD',0,0,nil,0,FILE_FLAG_DELETE_ON_CLOSE,0);

if Handle1=INVALID_HANDLE_VALUE then MessageBox(Handle,' 不能打开 TIMER.VXD ! ',' 错误 ',MB_ICONSTOP or MB_OK)

end;

procedure TTimerTestMain.FormClose(Sender: TObject;

var Action: TCloseAction);

begin

if Handle1<>INVALID_HANDLE_VALUE then CloseHandle(Handle1)

end;

procedure TTimerTestMain.Exit1Click(Sender: TObject);

begin

Release;

Application.Terminate

end;

procedure TTimerTestMain.Start1Click(Sender: TObject);

var

Kernel32:THandle;

OpenVxDHandle:TOpenVxDHandle;

VxDEvent:THandle;

cb:Longword;

begin

if Handle1<>INVALID_HANDLE_VALUE then

begin

TimerCallback1:=TTimerCallback.Create(True);

TimerCallback1.Resume;

Kernel32:=LoadLibrary('KERNEL32.DLL');

if Kernel32<>0 then

begin

OpenVxDHandle:=GetProcAddress(Kernel32,'OpenVxDHandle');

VxDEvent:=OpenVxDHandle(Event1.Handle);

DeviceIoControl(Handle1,TIMER_DIOC_SET_VXD_EVENT,@VxDEvent,SizeOf(VxDEvent),nil,0,cb,nil);

FreeLibrary(Kernel32)

end

end

end;

end.

Win32 应用程序辅助线程( TMR_CLBK.PAS ):

unit TMR_CLBK;

interface

uses

Classes, SysUtils, Windows, SyncObjs;

type

TTimerCallback = class(TThread)

private

{ Private declarations }

procedure UpdateLabelCaption;

protected

procedure Execute; override;

end;

var

Event1:TEvent;

implementation

uses TT_MAIN;

{ Important: Methods and properties of objects in VCL can only be used in a

method called using Synchronize, for example,

Synchronize(UpdateCaption);

and UpdateCaption could look like,

procedure TTimerCallback.UpdateCaption;

begin

Form1.Caption := 'Updated in a thread';

end; }

{ TTimerCallback }

procedure TTimerCallback.UpdateLabelCaption;

begin

TimerTestMain.Label1.Caption:=Trim(IntToStr(StrToInt(TimerTestMain.Label1.Caption)+1))

end;

procedure TTimerCallback.Execute;

begin

{ Place thread code here }

while not Terminated do

begin

Event1.ResetEvent;

Event1.WaitFor(INFINITE);

Synchronize(UpdateLabelCaption)

end

end;

end.

VxD 头文件( TIMER.H ):

// TIMER.h - include file for VxD TIMER

#include <vtoolscp.h>

#define DEVICE_CLASS TimerDevice

#define TIMER_DeviceID UNDEFINED_DEVICE_ID

#define TIMER_Init_Order UNDEFINED_INIT_ORDER

#define TIMER_Major 1

#define TIMER_Minor 0

class TimerInterrupt:public VHardwareInt

{

public:

TimerInterrupt(VOID);

~TimerInterrupt();

virtual VOID OnHardwareInt(VMHANDLE hVM);

private:

VOID (*m_callback)();

BYTE m_originalA;

BYTE m_originalB;

};

class TimerEvent:public VGlobalEvent

{

public:

TimerEvent(VOID);

virtual VOID handler(VMHANDLE hVM,CLIENT_STRUCT* pRegs,PVOID refData);

};

BYTE ReadRegister(BYTE reg);

VOID WriteRegister(BYTE reg, BYTE value);

VOID TimerHandler(VOID);

class TimerDevice : public VDevice

{

public:

virtual BOOL OnSysDynamicDeviceInit();

virtual BOOL OnSysDynamicDeviceExit();

virtual DWORD OnW32DeviceIoControl(PIOCTLPARAMS pDIOCParams);

private:

TimerInterrupt *pTimerInterrupt;

};

class TimerVM : public VVirtualMachine

{

public:

TimerVM(VMHANDLE hVM);

};

class TimerThread : public VThread

{

public:

TimerThread(THREADHANDLE hThread);

};

VxD 源程序( TIMER.CPP ):

// TIMER.cpp - main module for VxD TIMER

#define DEVICE_MAIN

#include "timer.h"

Declare_Virtual_Device(TIMER)

#undef DEVICE_MAIN

#include <vsd.h>

#include <vdebug.h>

#define STATREG_A 0xA

#define STATREG_B 0xB

#define STATREG_C 0xC

#define ENABLE_INTERRUPT 0x40

#define TIMER_DIOC_SET_VXD_EVENT 101

static int Count=0;

static HANDLE VxDEvent=NULL;

TimerInterrupt::TimerInterrupt():VHardwareInt(8,0,0,0)

{

m_callback=TimerHandler;

m_originalA=ReadRegister(STATREG_A);

m_originalB=ReadRegister(STATREG_B);

}

TimerInterrupt::~TimerInterrupt()

{

WriteRegister(STATREG_A,m_originalA);

WriteRegister(STATREG_B,m_originalB);

forceDefaultOwner(8,0);

}

VOID TimerInterrupt::OnHardwareInt(VMHANDLE hVM)

{

if(m_callback!=NULL) m_callback();

ReadRegister(STATREG_C);

sendPhysicalEOI();

}

TimerEvent::TimerEvent():VGlobalEvent(NULL)

{

}

VOID TimerEvent::handler(VMHANDLE hVM,CLIENT_STRUCT* pRegs,PVOID refData)

{

dout<<"handler !"<<endl;

if(VxDEvent!=NULL)

{

VWIN32_SetWin32Event(VxDEvent);

}

else

{

VSD_Bell();

}

}

VOID TimerHandler(VOID)

{

Count++;

if(Count==2000)

{

(new TimerEvent)->call();

Count=0;

}

}

BYTE ReadRegister(BYTE reg)

{

BYTE r;

_asm {

pushfd

cli

mov al, reg

or al, 80h

out 70h, al

jmp _1

}

_1:

_asm jmp _2

_2:

_asm {

in al, 71h

mov r, al

jmp _3

}

_3:

_asm jmp _4

_4:

_asm {

xor al, al

out 70h, al

popfd

}

return r;

}

VOID WriteRegister(BYTE reg, BYTE value)

{

_asm {

pushfd

cli

mov al, reg

or al, 80h

out 70h, al

jmp _1

}

_1:

_asm jmp _2

_2:

_asm {

mov al, value

out 71h, al

jmp _3

}

_3:

_asm jmp _4

_4:

_asm {

xor al, al

out 70h, al

popfd

}

}

TimerVM::TimerVM(VMHANDLE hVM) : VVirtualMachine(hVM) {}

TimerThread::TimerThread(THREADHANDLE hThread) : VThread(hThread) {}

BOOL TimerDevice::OnSysDynamicDeviceInit()

{

BYTE statreg;

pTimerInterrupt=new TimerInterrupt();

if(pTimerInterrupt!=NULL)

{

if(!pTimerInterrupt->hook())

{

pTimerInterrupt=NULL;

return FALSE;

}

else

{

statreg=ReadRegister(STATREG_B);

statreg|=ENABLE_INTERRUPT;

WriteRegister(STATREG_B,statreg);

ReadRegister(STATREG_C);

pTimerInterrupt->physicalUnmask();

VEvent::initEvents();

}

}

else

{

return FALSE;

}

return TRUE;

}

BOOL TimerDevice::OnSysDynamicDeviceExit()

{

BYTE statreg;

if(VxDEvent!=NULL)

{

VWIN32_CloseVxDHandle(VxDEvent);

VxDEvent=NULL;

}

statreg=ReadRegister(STATREG_B);

statreg&=~ENABLE_INTERRUPT;

WriteRegister(STATREG_B,statreg);

pTimerInterrupt->physicalMask();

if(pTimerInterrupt!=NULL) delete pTimerInterrupt;

return TRUE;

}

DWORD TimerDevice::OnW32DeviceIoControl(PIOCTLPARAMS pDIOCParams)

{

switch(pDIOCParams->dioc_IOCtlCode)

{

case TIMER_DIOC_SET_VXD_EVENT:

VxDEvent=*((HANDLE *)pDIOCParams->dioc_InBuf);

break;

default:

break;

}

return 0;

}

VxD 的基本原理与 VToolsD 2.03 中的 CHIME 实例程序相同,这里不再详述,只讲述一下 VxD 回调 Win32 应用程序的实现:

Win32 应用程序有一个主线程和一个辅助线程,并定义了一个事件(这里通过 Delphi 5.0 的 TEvent 类定义),主线程负责动态加载/卸载 VxD ,辅助线程通过 ResetEvent 函数和 WaitForSingleObject / WaitForSingleObjectEx 函数暂时挂起(这里通过 Delphi 5.0 的 TEvent 类的方法实现),但是 Win32 应用程序的事件句柄不能直接被 VWIN32.VXD 提供的 Win32 事件服务使用,必须先通过 KERNEL32.DLL 中的未公开 API —— OpenVxDHandle 函数转换成 VxD 事件句柄(该函数在 SDK 文档中未公开,但是在 DDK 文档中公开了),然后通过 DeviceIoControl 函数传递给 VxD 。 Win32 SDK 没有为未公开 API 提供头文件和引入库,但是可以通过 LoadLibrary 函数、 GetProcAddress 函数和 FreeLibrary 函数动态获取 OpenVxDHandle 函数的入口地址,从而进行间接调用(注意! GetProcAddress 函数对于 KERNEL32.DLL 只能通过函数名获取 API 入口地址,不能通过函数序号获取 API 入口地址,原因是 Microsoft 公司在 KERNEL32.DLL 中加入了 Anti-Hacking 代码,而且大部分未公开 API 的引出函数名被去掉了,也就是说通过函数名也不能获取这些未公开 API 的入口地址, OpenVxDHandle 函数是个例外)。 VxD 通过 VWIN32.VXD 提供的 Win32 事件服务中的 _VWIN32_SetWin32Event 服务(在 VToolsD 中是调用 VWIN32_SetWin32Event 函数)唤醒辅助线程,从而间接实现 VxD 回调 Win32 应用程序。

该程序当中断每发生 2000 次(大约 1 — 2 秒)时唤醒辅助线程, Win32 应用程序窗口中的计数器的计数值会不断增大,该程序稍加修改,即可实现精度高达 1ms 的高精度定时。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有