• Delphi研究之驱动开发篇(一)


    Delphi能不能开发Windows的驱动程序(这里的驱动程序当然不是指VxD了^_^)一直是广大Delphi fans关注的问题。姑且先不说能或者不能,我们先来看看用Delphi开发驱动程序需要解决哪些技术上问题。
      Delphi的链接器是无法生成Windows内核模式程序的,因此用delphi无法直接生成驱动程序。M$的链接器是可以生成Windows内核模式程序的,那么是否可以用Delphi生成目标文件,然后用M$链接呢?要这么做必须要解决以下的问题:
      Delphi生成的目标文件是OMF格式的,而M$ link虽然声称支持OMF格式的目标文件,但基本无用。最好能将OMF格式转换成COFF格式,EliCZ大侠的OMF2D正好可以解决这个问题。解决了目标格式的问题,一切都OK了吗?远没这么简单。继续之前,让我们先来看一下著名的DDDK吧。
      DDDK(Delphi Driver Development Kit)是The Hacker Defender Project team发布的一个用Delphi开发Windows驱动程序的工具包,目前最新版是0.0.4版。DDDK是将常用的驱动API用Delphi做了层包装放在DDDK单元中,就像下面这样:

    代码:
    unit DDDK;

    interface

    const
     NtKernel
    ='ntoskrnl.exe';
    ……
    procedure IoCompleteRequest(Irp:PIrp;PriorityBoost:Integer); stdcall;
      ……
    implementation
    procedure krnlIoCompleteRequest(Irp:PIrp;PriorityBoost:Integer); stdcall; external NtKernel name 'IoCompleteRequest';

    procedure IoCompleteRequest(Irp:PIrp;PriorityBoost:Integer); stdcall; 
    begin 
    krnlIoCompleteRequest(Irp,PriorityBoost); 
    end;
    ……
    然后在每次链接驱动文件之前,用omf2d对dddk.obj中需要引入的驱动API做以下的处理:
    omf2d inc\DDDK.obj /U- /CEIoCompleteRequest=_IoCompleteRequest@8 2>nul将DDDK.obj中的IoCompleteRequest改成_IoCompleteRequest@8,为什么要这样做呢?那是因为诸如ntoskrnl.lib之类的导入库都是coff格式的,coff格式就是这样命名的。完成这步以后就可以调用m$ link将目标文件链接成驱动文件了。
      这样做虽然可以生成正确的驱动文件,但缺点也是明显的。将驱动API用delphi包装,这些用delphi包装的函数不管是否使用都会被链接到最终生成的驱动文件中,这样会增加驱动文件的尺寸,而且通过delphi的封装函数再去调用驱动API效率也会受影响,还有就是每次链接前都要用omf2d inc\DDDK.obj /U- /CEIoCompleteRequest=_IoCompleteRequest@8去转换delphi的目标文件,既麻烦又容易出错。有没有更好的办法呢?
      omf2d的工作就是将delphi的命名方法转换成coff的_xxxxxxx@xx格式,默认omf2d会去掉前导下划线和@xx后缀,可以用/U_*开关让omf2d不删除前导下划线,如果我们再有没有@xx后缀的导入库,那问题就简单多了。但m$并没有提供没有@xx后缀的导入库,那就让我们自己做一个吧^_^,其实很简单,比如我们要生成hal.dll的导入库,只需要编辑一个如下内容的hal.def文件:

    代码:
    LIBRARY     HAL.DLL

    EXPORTS
        ExAcquireFastMutex             
        ExReleaseFastMutex             
        ExTryToAcquireFastMutex        
        HalAcquireDisplayOwnership     
        HalAdjustResourceList          
        HalAllProcessorsStarted
        ……

    然后用LINK /LIB /MACHINE:IX86 /DEF:hal.def /OUT:hal.lib命令就可以生成我们需要的没有@xx后缀的导入库文件了。有了这个文件,事情就好办多了。下面就让我们开始用delphi来开发一个简单的驱动程序beeper吧。
      这个驱动程序是从Four-F的KmdKit里的beeper转换过来的,程序的目标就是通过访问端口让PC的扬声器发声,程序通过三种方法让扬声器发声,一种是直接访问端口,一种是调用hal.dll的READ_PORT_UCHAR和WRITE_PORT_UCHAR访问端口,第三种方法则是调用hal.dll的HalMakeBeep函数。

    代码:

    unit beeper;

    interface

    uses windows, DDDK, hal;

    function _DriverEntry(DriverObject:PDriverObject;RegistryPath:PUnicodeString):NTSTATUS; stdcall;

    implementation

    const
      TIMER_FREQUENCY:DWORD 
    = 1193167;  {1,193,167 Hz}
      OCTAVE:DWORD          
    = 2;        {octave multiplier}
      PITCH_C:DWORD         
    = 523;      {C        -  523,25 Hz}
      PITCH_Cs:DWORD        
    = 554;      {C#       -  554,37 Hz}
      PITCH_D:DWORD         
    = 587;      {D        -  587,33 Hz}
      PITCH_Ds:DWORD        
    = 622;      {D#       -  622,25 Hz}
      PITCH_E:DWORD         
    = 659;      {E        -  659,25 Hz}
      PITCH_F:DWORD         
    = 698;      {F        -  698,46 Hz}
      PITCH_Fs:DWORD        
    = 740;      {F#       -  739,99 Hz}
      PITCH_G:DWORD         
    = 784;      {G        -  783,99 Hz}
      PITCH_Gs:DWORD        
    = 831;      {G#       -  830,61 Hz}
      PITCH_A:DWORD         
    = 880;      {A        -  880,00 Hz}
      PITCH_As:DWORD        
    = 988;      {B        -  987,77 Hz}
      PITCH_H:DWORD         
    = 1047;     {H        - 1046,50 Hz}
      
    { We are going to play c-major chord }

      DELAY:DWORD           
    = $18000000;   {for my ~800mHz box}

      TONE_
    1:DWORD          = 1141;
      TONE_
    2:DWORD          = 905;
      TONE_
    3:DWORD          = 1568;   {for HalMakeBeep}

      STATUS_DEVICE_CONFIGURATION_ERROR:DWORD 
    =  $00C0000182;

    procedure MakeBeep1(dwPitch: DWORD); stdcall; assembler;
    asm
      cli
      mov al, 10110110b
      out 43h, al
      mov eax, dwPitch
      out 42h, al
      mov al, ah
      out 42h, al
      
    {Turn speaker ON}
      
    in al, 61h
      
    or  al, 11b
      out 61h, al
      sti
      push eax
      mov eax, DELAY
    @@
    1:
      dec eax
      jnz @@
    1
      pop eax
      cli
      
    {Turn speaker OFF}
      
    in al, 61h
      
    and al, 11111100b
      out 61h, al

      sti
    end;

    procedure MakeBeep2(dwPitch: DWORD); stdcall;
    var
      dwPort, i: DWORD;
    begin
      asm
        cli;
      
    end;
      WRITE_PORT_UCHAR(PUCHAR($
    43), $b6);
      WRITE_PORT_UCHAR(PUCHAR($
    42), dwPitch and $FF);
      WRITE_PORT_UCHAR(PUCHAR($
    42), ((dwPitch shr 8and $FF));
      dwPort :
    = READ_PORT_UCHAR(PUCHAR($61));
      dwPort :
    = dwPort or 3;
      WRITE_PORT_UCHAR(PUCHAR($
    61), dwPort);
      asm
        sti
      
    end;
      
    for i := 1 to DELAY do
      
    begin
      
    end;
      asm
        cli
      
    end;
      
    { Turn speaker OFF }
      dwPort :
    = READ_PORT_UCHAR(PUCHAR($61));
      dwPort :
    = dwPort and $FC;
      WRITE_PORT_UCHAR(PUCHAR($
    61), dwPort);
      asm
        sti
      
    end;
    end;

    function _DriverEntry(DriverObject:PDriverObject;RegistryPath:PUnicodeString):NTSTATUS; stdcall;
    var
      i: integer;
    begin
      MakeBeep1(TONE_
    1);
      MakeBeep2(TONE_
    2);
      HalMakeBeep(TONE_
    3);

      
    for i := 1 to DELAY do
      
    begin
      
    end;
      HalMakeBeep(
    0);
      Result :
    = STATUS_DEVICE_CONFIGURATION_ERROR;
    end;

    end.

    代码:
    unit hal;

    interface

    uses
      Windows;

    const
      NtHal 
    = 'hal.dll';

    function HalMakeBeep(Frequency: ULONG):BOOLEAN; stdcall;
    function READ_PORT_UCHAR(Port:PUCHAR):UCHAR; stdcall;
    procedure WRITE_PORT_UCHAR(Port: PUCHAR; Value: UCHAR); stdcall; 

    implementation

    function HalMakeBeep(Frequency: ULONG):BOOLEAN; stdcall; external NtHal name '_HalMakeBeep';
    function READ_PORT_UCHAR(Port:PUCHAR):UCHAR; stdcall; external NtHal name '_READ_PORT_UCHAR';
    procedure WRITE_PORT_UCHAR(Port: PUCHAR; Value: UCHAR); stdcall; external NtHal name '_WRITE_PORT_UCHAR';

    end.
    1.  用dcc32 –U ..\include -B -CG -JP -$A-,C-,D-,G-,H-,I-,L-,P-,V-,W+,Y- beeper.pas生成目标文件(此处的..\inc是我保存相关delphi单元文件的目录,你的可能不是这个目录哟)
    2.  用omf2d beeper.obj /U_*转换目标文件,使其能被m$ link链接
    3.  用link /NOLOGO /ALIGN:32 /BASE:0x10000 /SUBSYSTEM:NATIVE /DRIVER /FORCE:UNRESOLVED /FORCE:MULTIPLE /ENTRY:DriverEntry ..\lib\hal.lib beeper.obj /OUT:beeper.sys生成最终的驱动文件。(注意这里用/FORCE:UNRESOLVED是因为dcc32会在delphi的目标文件中加入一些单元的初始化及销毁代码,这些东东在驱动程序中是不需要的,所以强行忽略之,还会出现一堆链接警告,也不用理会)。
    执行完以上的步骤,在你的目录下就会生成一个beeper.sys文件了。把它拷贝到KmdKit的beeper目录中,用它的SCP文件加载,PC的喇叭果然发出的清脆的声音,证明我们的delphi驱动是正确的。用此种方法生成的beeper.sys只有1376字节,只比用KmdKit的汇编代码的beeper.sys大几百个字节,而用DDDK生成的beeper.sys则要超过3K。
      打完这么多字真不容易,这篇教程就到这里吧,下一篇我们再来用delphi做一个更有趣的东东。

    作 者: mickeylan   转至:http://bbs.pediy.com/showthread.php?t=57762
  • 相关阅读:
    logistics regression
    dir 以及 attrib
    python 爬取有道翻译
    spss 逐步回归
    JQuery传值给.ashx乱码
    字符串转换成json的三种方式
    启动数据库SQL Server Service Broker
    ASP.NET两种缓存方式
    VS安装控件后报错
    sql server中有不是全数字的字符串
  • 原文地址:https://www.cnblogs.com/sonicit/p/1152977.html
Copyright © 2020-2023  润新知