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后綴的導入庫文件了。有了這個文件,再加上本人開發的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.



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.   用rmcoff beeper.obj 處理delphi目標文件的相關符號並將目標文件從OMF格式轉換成COFF格式,使其能被m$ link鏈接
3.   用link /NOLOGO /ALIGN:32 /BASE:0x10000 /SUBSYSTEM:NATIVE /DRIVER /ENTRY:DriverEntry ..\lib\hal.lib beeper.obj /OUT:beeper.sys生成最終的驅動文件。執行完以上的步驟,在你的目錄下就會生成一個beeper.sys文件了。把它拷貝到KmdKit的beeper目錄中,用它的SCP文件加載,PC的喇叭果然發出的清脆的聲音,證明我們的delphi驅動是正確的。用此種方法生成的beeper.sys只有1376字節,只比用KmdKit的匯編代碼的beeper.sys大幾百個字節,而用DDDK生成的beeper.sys則要超過3K。
   打完這么多字真不容易,這篇教程就到這里吧,下一篇我們再來用delphi做一個更有趣的東東。

http://blog.csdn.net/sustzw/article/details/6722250


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM