Windows Driver Model >> Pascal >> File

IRP


參考資訊:
1. Source Code
2. delphidriverdevelopmentkit

基本上,驅動程式(韌體)的基本功能就是當作User Application(軟體)和硬體的溝通橋樑,軟體可以透過很多種方式和驅動程式溝通,而最常用的溝通方式就是把驅動程式當做是一個檔案在操作,軟體可以透過檔案讀寫的方式和驅動程式做資料傳遞的動作,驅動程式最後再把資料輸出到硬體裝置上,而在Windows系統上,可以使用CreateFile(檔案開啟)、ReadFile(讀取檔案)、WriteFile(寫入檔案)、CloseHandle(關閉檔案)等檔案操作的Win32 API,而驅動程式如何跟軟體串接起來呢?驅動程式必須在DriverEntry()設定IRP_MJ_CREATE(CreateFile)、IRP_MJ_READ(ReadFile)、IRP_MJ_WRITE(WriteFile)、IRP_MJ_CLOSE(CloseHandle)的Callback副程式,這樣I/O Manager才會知道如何串起這條溝通管道。

基本的步驟如下:
1. 安裝驅動程式時,驅動程式註冊自己的名字、註冊檔案的Callback副程式
2. 軟體開啟檔案(使用驅動程式的名字(GUID或者Symbolic)),I/O Manager呼叫驅動程式的IRP_MJ_CREATE
3. 軟體讀寫檔案,I/O Manager呼叫驅動程式的IRP_MJ_READ、IRP_MJ_WRITE
4. 軟體關閉檔案,I/O Manager呼叫驅動程式的IRP_MJ_CLOSE

當然,使用者可能也很好奇,為何軟體不直接跟硬體溝通就好,還要透過一個這麼複雜的驅動程式當作溝通橋樑?當軟體可以直接溝通硬體時,會造成太多衝突,以及優先順序問題,而因為可以直接控制硬體,因此,寫出一個非惡意但具有破壞性的軟體時,是間接可以把一個系統搞壞掉,這也是為何要在Win98之後,架構出一個驅動程式的原因,至少有一個溝通和協調的機制,當然隨著系統的發展,規定就會變成越來嚴格,這也是Microsoft致力於保護系統的最好方式!

對應的溝通管道

Win32 API Kernel IRP
CreateFile() IRP_MJ_CREATE
ReadFile() IRP_MJ_READ
WriteFile() IRP_MJ_WRITE
CloseHandle() IRP_MJ_CLOSE

接著司徒將介紹如何在驅動程式做檔案的基本操作

main.pas

unit main;

interface
  uses
    DDDK;
    
  const
    DEV_NAME = '\Device\MyDriver';
    SYM_NAME = '\DosDevices\MyDriver';

  function _DriverEntry(pOurDriver:PDRIVER_OBJECT; pOurRegistry:PUNICODE_STRING):NTSTATUS; stdcall;

implementation
var
  pNextDevice: PDEVICE_OBJECT;
  
procedure Unload(pOurDriver:PDRIVER_OBJECT); stdcall;
begin
end;

function IrpFile(pOurDevice:PDEVICE_OBJECT; pIrp:PIRP):NTSTATUS; stdcall;
var
  psk: PIO_STACK_LOCATION;
  
begin
  psk:= IoGetCurrentIrpStackLocation(pIrp);
  case psk^.MajorFunction of
  IRP_MJ_CREATE:
    DbgPrint('IRP_MJ_CREATE', []);
  IRP_MJ_READ:
    DbgPrint('IRP_MJ_READ', []);
  IRP_MJ_WRITE:
    DbgPrint('IRP_MJ_WRITE', []);
  IRP_MJ_CLOSE:
    DbgPrint('IRP_MJ_CLOSE', []);
  end;
  
  IoCompleteRequest(pIrp, IO_NO_INCREMENT);
  Result:= STATUS_SUCCESS;
end;

function IrpPnp(pOurDevice:PDEVICE_OBJECT; pIrp:PIRP):NTSTATUS; stdcall;
var
  psk: PIO_STACK_LOCATION;
  suSymName: UNICODE_STRING;
  
begin
  psk:= IoGetCurrentIrpStackLocation(pIrp);
  if psk^.MinorFunction = IRP_MN_REMOVE_DEVICE then
  begin
    RtlInitUnicodeString(@suSymName, SYM_NAME);
    IoDetachDevice(pNextDevice);
    IoDeleteDevice(pOurDevice);
    IoDeleteSymbolicLink(@suSymName);
  end;
  IoSkipCurrentIrpStackLocation(pIrp);
  Result:= IoCallDriver(pNextDevice, pIrp);
end;

function AddDevice(pOurDriver:PDRIVER_OBJECT; pPhyDevice:PDEVICE_OBJECT):NTSTATUS; stdcall;
var
  suDevName: UNICODE_STRING;
  suSymName: UNICODE_STRING;
  pOurDevice: PDEVICE_OBJECT;
  
begin
  RtlInitUnicodeString(@suDevName, DEV_NAME);
  RtlInitUnicodeString(@suSymName, SYM_NAME);
  IoCreateDevice(pOurDriver, 0, @suDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, pOurDevice);
  pNextDevice:= IoAttachDeviceToDeviceStack(pOurDevice, pPhyDevice);
  pOurDevice^.Flags:= pOurDevice^.Flags or DO_BUFFERED_IO;
  pOurDevice^.Flags:= pOurDevice^.Flags and not DO_DEVICE_INITIALIZING;
  Result:= IoCreateSymbolicLink(@suSymName, @suDevName);
end;

function _DriverEntry(pOurDriver:PDRIVER_OBJECT; pOurRegistry:PUNICODE_STRING):NTSTATUS; stdcall;
begin
  pOurDriver^.MajorFunction[IRP_MJ_PNP]:= @IrpPnp;
  pOurDriver^.MajorFunction[IRP_MJ_CREATE]:= @IrpFile;
  pOurDriver^.MajorFunction[IRP_MJ_READ]:= @IrpFile;
  pOurDriver^.MajorFunction[IRP_MJ_WRITE]:= @IrpFile;
  pOurDriver^.MajorFunction[IRP_MJ_CLOSE]:= @IrpFile;
  pOurDriver^.DriverExtension^.AddDevice:=@AddDevice;
  pOurDriver^.DriverUnload:=@Unload;
  Result:=STATUS_SUCCESS;
end;
end.

IrpFile()處理檔案相關的IRP,目前只列印IRP資訊,其餘不處理。

為了測試驅動程式的檔案讀寫IRP,我們需要一個User Application做測試(app.pas)

program main;

{$APPTYPE CONSOLE}

uses
  Windows,
  Messages,
  SysUtils,
  Variants,
  Classes,
  Graphics,
  Controls,
  Forms,
  DIALOGS;

var
  fd: DWORD;
  ret: DWORD;
  len: DWORD;
  szBuf: array[0..255] of char;

begin
  fd:= CreateFile('\\.\MyDriver', GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, Nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  if (fd <> INVALID_HANDLE_VALUE) then
  begin
    StrCopy(szBuf, 'I am error');
    len:= strlen(szBuf)+1;
    WriteFile(fd, szBuf, len, ret, Nil);
    ReadFile(fd, szBuf, len, ret, Nil);
    CloseHandle(fd);
  end else
  begin
    WriteLn(Output, 'failed to open mydriver');
  end;
end.

編譯App:

del /s /q app.exe
c:\dddk\bin\dcc32.exe -Uc:\dddk\inc /b /dReleaseCompilation app.pas

接著執行該User Application就可以看到結果

返回上一頁