Delphi驱动开发研究第一篇

时间:2022-01-02 04:17:43

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后缀的导入库文件了。有了这个文件,再加上本人开发的rmcoff工具,事情就好办多了。下面就让我们开始用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 8) and $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.