分享
 
 
 

浅谈设备驱动程序回调Win32应用程序

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

浅谈设备驱动程序回调Win32应用程序

(第1篇——VxD)

(2005修订版,本文档适用于Windows 9x VxD,包括Windows 95/98/Me VxD,但是方法的思想适用于Windows NT/2000/XP及其以后版本NT Driver/WDM驱动程序)

作者:TBsoft Software Studio(2000)

修订:TBsoft Software Studio(2005)

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均使用该方法(例如某些反病毒软件的实时监控VxD即使用该方法回调Win32查毒/杀毒程序),此处就不再详述了。

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

注意:VxD回调Win32应用程序是不可能实现同步的,也就是说,不可能在VxD回调Win32应用程序时中止VxD的执行,等到回调完成再继续执行VxD,因为VxD回调的Win32应用程序必然又要访问VMM,而VMM是不可重入的。

笔者为了验证上述方法,编写了一个动态加载/卸载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函数将Win32应用程序的事件句柄转换成VxD事件句柄,然后通过DeviceIoControl函数传递给VxD。

OpenVxDHandle函数在SDK文档中未公开,但是在DDK文档中公开了,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应用程序窗口中的计数器的计数值会不断增大。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有