Changjiang的专栏

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

赵世平ID:Changjiang
[修改头像]
17668次访问,排名5663(1)好友0人,关注者0
Changjiang的文章
原创 8 篇
翻译 2 篇
转载 0 篇
评论 6 篇
最近评论
wangqinghua1:很有收获,文风简洁
fantasyzzz:老师好,我是今天和您联系请教学习汇编方法的您的学生,我以前一直学习的是8086的汇编语言,不过也可以理解您上面的Win32ASM程序示例的大意,我已经对8086的汇编程序编写有一定的掌握,熟悉汇编的编写方法了,而且我现在还在看8086的教材巩固,我想问的是这个过程是否可以跳过,放下8086的学习,直接学习Win32的汇编。不知道这里请教合适么?谢谢老师。。
royelee:当年也是玩crack的啊?
还是原来用trw2k的时代爽啊。
Changjiang:呵呵,这个BLOG是去年开的,一直没有用,这几天正好有空,放上了一点文章。最近还好么,支持你。
flier:正是应了微软 3.0 那个说法,CLI 一直出到第三版,才算是基本上完成了预先的设想,把从一开始就规划进去的那些坑都填上,也腾出了精力去折腾相对来说更实际一些的 WinFX。等 Enterprise Library 那套东东再成熟一点,针对 Java 阵营的进攻号角才算真正吹向。
软件项目交易
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes
文章分类
收藏
    相册
    好友的BLOG
    孟岩的BLOG
    自己的其他BLOG
    Changjiang的另一个BLOG:TBsoft工作室的BLOG
    存档

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

    新一篇: 从.NET CLI(第3版)看.NET Framework 2.0

    浅谈设备驱动程序回调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...)|编辑

    旧一篇: Windows 95虚拟设备驱动程序(VxD)

    评论:没有评论。

    发表评论  


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