Changjiang的专栏

孤帆远影碧空尽,唯见长江天际流。

原创 浅谈设备驱动程序回调Win32应用程序收藏

新一篇: 从.NET CLI(第3版)看.NET Framework 2.0 | 旧一篇: Windows 95虚拟设备驱动程序(VxD)

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

 

 

(第1篇——VxD

 

 

2005修订版,本文档适用于Windows 9x VxD,包括Windows 9598Me VxD,但是方法的思想适用于Windows NT2000XP及其以后版本NT DriverWDM驱动程序)

 

 

作者:TBsoft Software Studio2000

 

修订:TBsoft Software Studio2005

 

 

Windows 9598的虚拟外壳设备(Virtual Shell Device)提供了VxD直接回调Win16应用程序函数的VxD服务,但是没有提供VxD直接回调Win32应用程序的VxD服务。不过Windows 9598还是提供了两种VxD回调Win32应用程序的方法。方法之一是使用VWIN32.VXD提供的“异步过程调用(APC)”功能。Win32应用程序首先动态加载VxD,并使用DeviceIoControl函数将回调函数的地址传递给VxD,然后Win32应用程序调用SleepExWaitForMultipleObjectsExWaitForSingleObjectEx函数,将Win32应用程序自身置为“挂起”状态,这时VxD可以通过VWIN32.VXD提供的_VWIN32_QueueUserApc服务调用Win32应用程序的回调函数。该方法较简单,目前大多数回调Win32应用程序的VxD均使用该方法例如某些反病毒软件的实时监控VxD即使用该方法回调Win32查毒/杀毒程序,此处就不再详述了。

 

 

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

 

 

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

 

 

笔者为了验证上述方法,编写了一个动态加载/卸载VxDWin32应用程序和一个回调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.0TEvent类定义),主线程负责动态加载/卸载VxD,辅助线程通过ResetEvent函数和WaitForSingleObjectWaitForSingleObjectEx函数暂时挂起(这里通过Delphi 5.0TEvent类的方法实现)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次(大约12秒)时唤醒辅助线程,Win32应用程序窗口中的计数器的计数值会不断增大。

 

发表于 @ 2005年11月17日 00:49:00|评论(loading...)|编辑

新一篇: 从.NET CLI(第3版)看.NET Framework 2.0 | 旧一篇: Windows 95虚拟设备驱动程序(VxD)

评论:没有评论。

发表评论  


当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
Csdn Blog version 3.1a
Copyright © Changjiang