首页
社区
课程
招聘
[原创]Delphi研究之驱动开发篇(三)--一个完整的驱动程序示例
发表于: 2008-1-14 20:58 26140

[原创]Delphi研究之驱动开发篇(三)--一个完整的驱动程序示例

2008-1-14 20:58
26140

Delphi研究之驱动开发篇(三)

    (注:本篇的原理部分均摘自罗云彬大侠翻译的驱动开发教程)
     在前面的两篇教程中我们写了三个玩具驱动程序,为什么说是玩具驱动呢?因为它们确确实实是驱动程序,而且也能完成一些有趣的功能,但是它们都不完整,没有同用户交流的功能,这一篇就让我们来完成一个简单的全功能驱动程序。
    在写程序之前,我们有必要了解一些基础知识。
在用户模式下,我们可以通过访问某个地址来直接调用dll中的函数,但是在内核模式下,从系统的稳定性考虑,这样做是非常危险的。所以,系统提供了和内核模式通讯的媒介--I/O管理器,它是I/O子系统的部件之一。I/O管理器将应用程序、系统部件和设备连接起来,并定义了一个架构来支持设备驱动程序。
    一般来说,用户模式的操作都被转换成了对具体硬件设备的I/O操作,仅对于某些设备,设备由驱动程序来创建和控制,这些设备就是虚拟设备。当然,创建这些设备并不意味着你创造了什么硬件,而仅仅是在内存中创建了一个新的对象而已。每个对象和一个物理设备或者逻辑设备对应,用于描述它们的特征。
    创建设备后,驱动程序告诉I/O管理器:“这里有个我控制的设备,如果你收到了操作这个设备的I/O请求的话,直接发给我好了,剩下的由我来搞定!”。驱动程序知道如何对自己管理的设备进行I/O操作,I/O管理器唯一的职责在于创建I/O请求并把它发送给适当的设备驱动程序。用户模式的代码不知道(也不必知道)其中的细节,也不用知道究竟是哪个驱动程序在管理哪个设备。
下面先让我们来看一下用户模式下的控制程序:

program VirToPhys;

{$APPTYPE CONSOLE}

uses
  SysUtils, Windows, WinSvc, Dialogs, nt_status;

const
  NUM_DATA_ENTRY =4;
  DATA_SIZE = sizeof(DWORD) * NUM_DATA_ENTRY;
  _DELETE = $10000;

var
  hSCManager:THANDLE;
  hService:THANDLE;
  acModulePath: array [0..MAX_PATH] of char;
  _ss:SERVICE_STATUS;
  hDevice:THANDLE;

  adwInBuffer: array [0..NUM_DATA_ENTRY] of DWORD;
  adwOutBuffer: array [0..NUM_DATA_ENTRY] of DWORD;
  dwBytesReturned:DWORD;
  IOCTL_GET_PHYS_ADDRESS: DWORD;
  lpTemp: PChar;
  iRetValue: boolean;

{生成控制码}  
function CTL_CODE(DeviceType, Func, Method, Access: DWORD): DWORD;
begin
  result := (((DeviceType) SHL 16) OR ((Access) SHL 14) OR ((Func) SHL 2) OR (Method));
end;

begin
  IOCTL_GET_PHYS_ADDRESS := CTL_CODE(FILE_DEVICE_UNKNOWN,
                            $800, METHOD_BUFFERED,
                            FILE_READ_ACCESS + FILE_WRITE_ACCESS);
  hSCManager := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
  if hSCManager <> 0 then
  begin
    GetFullPathName(PChar('VirtToPhys.sys'), sizeof(acModulePath), acModulePath, lpTemp);
    hService := CreateService(hSCManager, 'VirtToPhys', 'Virtual To Physical Address Converter',
                              SERVICE_START + SERVICE_STOP + _DELETE, SERVICE_KERNEL_DRIVER,
                              SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, acModulePath,
                              nil, nil, nil, nil, nil);
    if hService <> 0 then
    begin
      {驱动程序的DriverEntry过程将被调用}
      if StartService(hService, 0, lpTemp) = true then
      begin
        {驱动程序将接收IRP_MJ_CREATE I/O请求包(IRP)}
        hDevice := CreateFile('\\.\slVirtToPhys', GENERIC_READ+GENERIC_WRITE,
                              0, nil, OPEN_EXISTING, 0, 0);
        if hDevice <> INVALID_HANDLE_VALUE  then
        begin
          {准备送往驱动程序的数据包}
          adwInBuffer[0] := GetModuleHandle(nil);
          adwInBuffer[1] := GetModuleHandle('kernel32.dll');
          adwInBuffer[2] := GetModuleHandle('user32.dll');
          adwInBuffer[3] := GetModuleHandle('comctl32.dll');
          {驱动程序将接收IRP_MJ_DEVICE_CONTROL I/O请求包}
          iRetValue := DeviceIoControl(hDevice, IOCTL_GET_PHYS_ADDRESS,
                                       @adwInBuffer, sizeof(adwInBuffer),
                                       @adwOutBuffer, sizeof(adwOutBuffer),
                                       dwBytesReturned, nil);
          if (iRetValue = true) and (dwBytesReturned <> 0) then
          begin
            {取程序名}
            GetModuleFileName(adwInBuffer[0], acModulePath, sizeof(acModulePath));
            ShowMessage(Format('Modules          BASE     Physical'#13#10 +
                               '----------------------------------'#13#10 +
                               '%s    %8.8X %8.8X'#13#10 +
                               'kernel32.dll     %8.8X %8.8X'#13#10 +
                               'user32.dll       %8.8X %8.8X'#13#10 +
                               'comctl32.dll     %8.8X %8.8x',
                               [ExtractFileName(acModulePath), adwInBuffer[0], adwOutBuffer[0],
                                adwInBuffer[1], adwOutBuffer[1],
                                adwInBuffer[2], adwOutBuffer[2],
                                adwInBuffer[2], adwOutBuffer[2]]));
          end else
          begin
            ShowMessage('Can''t send control code to device.');
          end;
          {Driver will receive IRP of type IRP_MJ_CLOSE}
          CloseHandle(hDevice);
        end else
        begin
          ShowMessage('Device is not present.');
        end;
        {DriverUnload proc in our driver will be called}
        ControlService(hService, SERVICE_CONTROL_STOP, _ss);
      end else
      begin
        ShowMessage('Can''t start driver.');
      end;
      DeleteService(hService);
      CloseServiceHandle(hService);
    end else
    begin
      ShowMessage('Can''t register driver.');
    end;
  CloseServiceHandle(hSCManager);
  end else
  begin
    ShowMessage('Can''t connect to Service Control Manager.');
  end;
end.
function CreateFile(lpFileName: PChar; 
                    dwDesiredAccess, dwShareMode: DWORD;
                    lpSecurityAttributes: PSecurityAttributes;dwCreationDisposition, dwFlagsAndAttributes: DWORD;
                    hTemplateFile: THandle): THandle; stdcall;
hDevice := CreateFile('\\.\slVirtToPhys', GENERIC_READ+GENERIC_WRITE,0, nil, OPEN_EXISTING, 0, 0);
function DeviceIoControl(hDevice: THandle; 
    dwIoControlCode: DWORD; 
    lpInBuffer: Pointer;          
    nInBufferSize: DWORD; 
    lpOutBuffer: Pointer;  
    nOutBufferSize: DWORD;
    var lpBytesReturned: DWORD; 
    lpOverlapped: POverlapped): BOOL; stdcall;
adwInBuffer[0] := GetModuleHandle(nil);
      adwInBuffer[1] := GetModuleHandle('kernel32.dll');
      adwInBuffer[2] := GetModuleHandle('user32.dll');
      adwInBuffer[3] := GetModuleHandle('comctl32.dll');
unit VirtToPhys;

interface

uses
  nt_status, ntoskrnl, ntutils;

function _DriverEntry(pDriverObject:PDRIVER_OBJECT; pusRegistryPath:PUNICODE_STRING): NTSTATUS; stdcall;

implementation

const
  NUM_DATA_ENTRY = 4;
  DATA_SIZE = sizeof(DWORD) * NUM_DATA_ENTRY;

var
  g_usDeviceName, g_usSymbolicLinkName: UNICODE_STRING;

function DispatchCreateClose(p_DeviceObject:PDEVICE_OBJECT; p_Irp:PIRP): NTSTATUS; stdcall;
begin
  p_Irp^.IoStatus.Status := STATUS_SUCCESS;
  p_Irp^.IoStatus.Information := 0;

  IofCompleteRequest(p_Irp, IO_NO_INCREMENT);
  result := STATUS_SUCCESS;
end;

function DispatchControl(p_DeviceObject: PDEVICE_OBJECT; p_Irp:PIRP): NTSTATUS; stdcall;
var
  status:NTSTATUS;
  dwBytesReturned:DWORD;
  psl:PIO_STACK_LOCATION;
  IOCTL_GET_PHYS_ADDRESS: DWORD;
  pSystemBuffer: PULONG;
  iCnt: DWORD;
  liPhysicalAddress: PHYSICAL_ADDRESS;
begin
  dwBytesReturned := 0;

  IOCTL_GET_PHYS_ADDRESS := CTL_CODE(FILE_DEVICE_UNKNOWN,
    $800, METHOD_BUFFERED, FILE_READ_ACCESS + FILE_WRITE_ACCESS);


  psl := IoGetCurrentIrpStackLocation(p_Irp);

  if psl^.Parameters.DeviceIoControl.IoControlCode = IOCTL_GET_PHYS_ADDRESS then
  begin
    if (psl^.Parameters.DeviceIoControl.OutputBufferLength >= DATA_SIZE)
       and (psl^.Parameters.DeviceIoControl.InputBufferLength >= DATA_SIZE) then
    begin
      pSystemBuffer := p_Irp^.AssociatedIrp.SystemBuffer;
      iCnt := 0;
      for iCnt := 1 to NUM_DATA_ENTRY do
      begin
        liPhysicalAddress := MmGetPhysicalAddress(PVOID(pSystemBuffer^));
        pSystemBuffer^ := liPhysicalAddress.LowPart;
        inc(pSystemBuffer);
      end;
      dwBytesReturned := DATA_SIZE;
      status := STATUS_SUCCESS;
    end else
    begin
      status := STATUS_BUFFER_TOO_SMALL;
    end;
  end else
  begin
    status := STATUS_INVALID_DEVICE_REQUEST;
  end;
  
  p_Irp^.IoStatus.Status := status;
  p_Irp^.IoStatus.Information := dwBytesReturned;

  IofCompleteRequest(p_Irp, IO_NO_INCREMENT);
  result := status;
end;

procedure DriverUnload(p_DriverObject:PDRIVER_OBJECT); stdcall;
begin
  IoDeleteSymbolicLink(@g_usSymbolicLinkName);
  IoDeleteDevice(p_DriverObject^.DeviceObject);
end;


function _DriverEntry(pDriverObject:PDRIVER_OBJECT; pusRegistryPath:PUNICODE_STRING): NTSTATUS;
var
  status:NTSTATUS;
  pDeviceObject:TDeviceObject;
begin
  status := STATUS_DEVICE_CONFIGURATION_ERROR;
  RtlInitUnicodeString(g_usDeviceName, '\Device\devVirtToPhys');
  RtlInitUnicodeString(g_usSymbolicLinkName, '\??\slVirtToPhys');

  if (IoCreateDevice(pDriverObject, 0, @g_usDeviceName,
                     FILE_DEVICE_UNKNOWN, 0, FALSE,
                     pDeviceObject) = STATUS_SUCCESS) then
  begin
    if (IoCreateSymbolicLink(@g_usSymbolicLinkName,
                             @g_usDeviceName) = STATUS_SUCCESS) then
    begin
      pDriverObject^.MajorFunction[IRP_MJ_CREATE] := @DispatchCreateClose;
      pDriverObject^.MajorFunction[IRP_MJ_CLOSE] := @DispatchCreateClose;
      pDriverObject^.MajorFunction[IRP_MJ_DEVICE_CONTROL] := @DispatchControl;
      pDriverObject^.DriverUnload := @DriverUnload;

      status := STATUS_SUCCESS;
    end else
    begin
      IoDeleteDevice(@pDeviceObject);
    end;
  end;
  result := status;
end;

end.
function IoCreateDevice(DriverObject: PDRIVER_OBJECT;
                        DeviceExtensionSize: ULONG;
                        DeviceName: PUNICODE_STRING;
                        DeviceType: DEVICE_TYPE;
                        DeviceCharacteristics: ULONG;
                        Exclusive: BOOLEAN;
                        var DeviceObject:TDeviceObject):NTSTATUS; stdcall;
pDriverObject^.MajorFunction[IRP_MJ_CREATE] := @DispatchCreateClose;
pDriverObject^.MajorFunction[IRP_MJ_CLOSE] := @DispatchCreateClose;
pDriverObject^.MajorFunction[IRP_MJ_DEVICE_CONTROL] := @DispatchControl;

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (27)
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
2
好文!到时侯收录到专题
2008-1-14 21:22
0
雪    币: 97697
活跃值: (200839)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
3
Thanks.
2008-1-14 22:45
0
雪    币: 9793
活跃值: (2191)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
又见精彩好文!期望大大大作不断。
2008-1-14 22:50
0
雪    币: 1004
活跃值: (75)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
5
能写出这样的东东,首先要感谢罗云彬大侠,没有他辛苦翻译的KmdTur系列教程,也没有我的KmdKit4D了,再次感谢一下罗云彬大侠。
2008-1-14 23:08
0
雪    币: 902
活跃值: (2146)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
希望看雪可以开个DELPHI的专版,真的很喜欢DELPHI
2008-1-15 05:55
0
雪    币: 217
活跃值: (91)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
好文章,一直在跟着学习。
2008-1-15 08:24
0
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
8
好,引进消化,创新。同时推荐mickeylan加入编程技术小组。
2008-1-15 08:37
0
雪    币: 271
活跃值: (18)
能力值: ( LV12,RANK:370 )
在线值:
发帖
回帖
粉丝
9
写得相当不错...我会及时更新专题的。
2008-1-15 08:53
0
雪    币: 1004
活跃值: (75)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
10
谢谢大家鼓励,我一定会加倍努力的
2008-1-15 09:26
0
雪    币: 1919
活跃值: (901)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
11
好文,学习了~~~
2008-1-15 12:43
0
雪    币: 22
活跃值: (868)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
12
好东西,希望能坚持下去。
2008-1-15 13:41
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
上面的资料都不错.
请问,你能够做个有实践价值的驱动吗?
比如,做鼠标的delphi驱动版本,
这个应该有学习价值.
2008-1-15 14:49
0
雪    币: 1004
活跃值: (75)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
14
谢谢您的关注,最终我会写一个TDI过滤驱动的例程,这个东东应该是很实用的。
2008-1-15 15:38
0
雪    币: 198
活跃值: (1585)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
非常期待这个.
2008-1-15 21:27
0
雪    币: 217
活跃值: (91)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
16
link.exe,ome2d.exe,mspdb70.dll,msvcr70.dll,rlink32.exe这些文件是不是要放到delphi的bin目录中。
VirtToPhys.pas用make编译出现下面的错误,不过还是生成了VirtToPhys.sys,加载后蓝屏了
上传的附件:
2008-1-16 10:17
0
雪    币: 1004
活跃值: (75)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
17
不好意思,在教程里没说清楚patch包的用法 你仔细看下出错提示,你的IoGetCurrentIrpStackLocation、IofCompleteRequest、MmGetPhysicalAddress、CTL_CODE这几个函数都没有链到文件里,当然会蓝屏,请用我上传的那个patch包更新一下,然后在source目录里执行make clean、make、make install,然后再编译。另外编译的时候,LINK 4006警告不用管,link 2001错误要区别对待,如果提示aaHandleFinallysqqrv找不到可以不管,如果是其他的就要分析一下了。
2008-1-16 11:15
0
雪    币: 217
活跃值: (91)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
18
确是这样,问题已解决,谢谢!
试了一下,换成link.exe(VC8),mspdb80.dll,msvcr80.dll,就不好用了!
2008-1-16 13:15
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
已经收藏了,等待下一节课开堂,呵
2008-1-16 14:57
0
雪    币: 266
活跃值: (60)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
20
支持delphi,支持mickeylan

mickeylan开个delphi驱动的专题。
2008-1-16 15:07
0
雪    币: 1004
活跃值: (75)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
21
我也热爱Delphi,我学的第一个编程语言就是pascal,用的第一个编译器就是Turbo Pascal 4.0,然后turbo c 1.5这样一路过来的,Delphi就像我的初恋情人一样
2008-1-16 15:41
0
雪    币: 1324
活跃值: (5179)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
22
2008-1-16 22:43
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
楼主无私奉献的精神实在太伟大了。。
楼主的几篇文章我都看了N次,但都未能消化。
希望楼主先多出一些基本知识的文选,详细介绍一下系统内核结构,驱动原理,与及所需的相关知识等等。。。让我们这些处于内核门外的小鸟有个扎实的入门基础。
期待下一大作...
2008-1-22 15:37
0
雪    币: 200
活跃值: (230)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
[QUOTE=;]...[/QUOTE]
高手啊,崇拜ing ...
2008-1-24 21:52
0
雪    币: 409
活跃值: (982)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
25
太太太太太太 牛牛牛牛牛牛牛了了了了了了了了了了了啊啊啊啊啊啊啊啊啊啊啊
2009-2-5 17:57
0
游客
登录 | 注册 方可回帖
返回
//