首页
论坛
课程
招聘
[原创]Delphi研究之驱动开发篇(一)--实现原理
2008-1-5 21:48 53631

[原创]Delphi研究之驱动开发篇(一)--实现原理

2008-1-5 21:48
53631
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 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.        用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做一个更有趣的东东。

看雪招聘平台创建简历并且简历完整度达到90%及以上可获得500看雪币~

收藏
点赞0
打赏
分享
最新回复 (73)
雪    币: 109
活跃值: 活跃值 (197)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
combojiang 活跃值 26 2008-1-5 23:43
2
0
good,顶
雪    币: 4127
活跃值: 活跃值 (873)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
kagayaki 活跃值 2008-1-6 04:01
3
0
顶!!!
雪    币: 207
活跃值: 活跃值 (37)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
sislcb 活跃值 7 2008-1-6 10:27
4
0
强人,顶起!
雪    币: 892
活跃值: 活跃值 (74)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
mickeylan 活跃值 14 2008-1-6 12:44
5
0
谢谢捧场,我目前正在做一个基于Delphi的驱动开发工具包,我给它起了个名字叫KmdKit4D(Kernel Mode Driver Kit for Delphi),目前已经完成了没有@xx后缀的ntoskrnl.lib、hal.lib、win32k.lib、ntdll.lib导入库的创建以及一些常用C头文件转换成pascal声明的工作,适当的时候我会把它发布在论坛里(Delphi能开发驱动,BCB当然也能做^_^)
雪    币: 207
活跃值: 活跃值 (37)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
sislcb 活跃值 7 2008-1-6 19:14
6
0
C头文件转换成pascal声明

这个已经有一个工具了,在jvcl的网站有的下载,叫什么headerconv
雪    币: 892
活跃值: 活跃值 (74)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
mickeylan 活跃值 14 2008-1-6 21:23
7
0
谢谢sislcb兄提醒,HeadConv我试过,基本无法转换DDK的头文件,我用的是4.01版的^_^
雪    币: 212
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wping 活跃值 2008-1-7 10:16
8
0
麻烦楼主一下,我现在手头只有DELPHI7
那么想写一个驱动,还需要哪些准备工作呢(是下载还是什么的)
实在是不想用C来写
雪    币: 892
活跃值: 活跃值 (74)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
mickeylan 活跃值 14 2008-1-7 13:58
9
0
正准备写个贴子详细说呢,如果你用D7的话,需要D7的DCC32、system.dcu、sysinit.dcu
m$的link(我用的是8.00.50727.42版)、omf2d,DDK相关的Delphi声明文件以及我生成的那些没有@xx后缀的导入库。需要的话可以留下邮件地址我发给你。
雪    币: 210
活跃值: 活跃值 (56)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
ww990 活跃值 1 2008-1-7 14:35
10
0
不知到直接用delphi2007的版本可不可以编辑。
好像总有问题!
雪    币: 212
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wping 活跃值 2008-1-7 15:31
11
0
当然要了,麻烦发到wp231957@163.com谢谢
雪    币: 892
活跃值: 活跃值 (74)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
mickeylan 活跃值 14 2008-1-7 16:01
12
0
我用的就是delphi 2007编辑的,很好用的,不过要把相关的dcu文件拷贝到delphi2007的lib目录下,这样可以CodeInsign^_^
雪    币: 200
活跃值: 活跃值 (41)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lsuper 活跃值 2008-1-8 00:19
13
0
非常支持啊!
雪    币: 263
活跃值: 活跃值 (26)
能力值: ( LV12,RANK:410 )
在线值:
发帖
回帖
粉丝
vxin 活跃值 10 2008-1-12 11:30
14
0
学习。。。
雪    币: 112
活跃值: 活跃值 (111)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
wr960204 活跃值 2008-1-17 10:12
15
0
楼主写的不错.
前两年我做驱动的时候也尝试过用Delphi做驱动.
实际上我没用到MS的任何连接器和工具.
我是直接建立EXE/DLL工程,RTL的单元也是用的DDDK简化过的剥离Win32API的单元.
直接用DCC32编译成EXE/DLL,扩展名改成.SYS.
然后写了一个工具把修改PE文件中导入表OriginalFirstThunk和PE头的CheckSum.Delphi编译器总是把这些地方填0.而驱动加载的时候恰恰是要校验这些地方的.
雪    币: 892
活跃值: 活跃值 (74)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
mickeylan 活跃值 14 2008-1-17 12:13
16
0
此法的确可行,我也用过,优点是方便,不过缺点也很明显,生成的代码太大,随便一个driver的尺寸都会超过20K,而且没有init节区,不是一个标准的驱动文件,所以我才下决心做了KmdKit4d.
雪    币: 213
活跃值: 活跃值 (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
badapi 活跃值 2008-1-25 14:56
17
0
支持,LZ
能不能给个link的 下载连接?
雪    币: 892
活跃值: 活跃值 (74)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
mickeylan 活跃值 14 2008-1-26 12:31
18
0
请稍等一到二天,我目前正在对KmdKit4D做重大的改动,使其可以使用7.1以上版本的link进行链接,快好了
http://bbs.pediy.com/showthread.php?t=58070里有link 7.0,在11楼^_^
雪    币: 213
活跃值: 活跃值 (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
badapi 活跃值 2008-1-26 22:30
19
0
恩,支持....
偶昨天直接从官方下了SDK
里面附带的有link.exe
- -

M 大牛  多写点文章哈~~~~

加油!
雪    币: 236
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
bbzhu 活跃值 2008-1-28 19:08
20
0
MICKEYLAN 我需要你所说的这些文件,能否发我!
我的邮件 jing5700@sina.com
雪    币: 892
活跃值: 活跃值 (74)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
mickeylan 活跃值 14 2008-1-29 01:13
21
0
请到http://bbs.pediy.com/showthread.php?t=58986下最新版的吧。
雪    币: 205
活跃值: 活跃值 (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
NETSYSKEY 活跃值 2008-4-1 22:22
22
0
我用的是delphi2007,照教程说的做,为什么总提示
fatal:f1026 file not found: h:\a.dpr
是找不到这个文件,是什么意思,需要工程文件吗
雪    币: 892
活跃值: 活跃值 (74)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
mickeylan 活跃值 14 2008-4-3 09:43
23
0
编译时要在DOS命令行的相应目录下直接执行make,而不是用Delphi的集成开发环境。make前请先检查你电脑的环境变量是否设置好了,至少PATH里应该有make.exe文件所在目录的信息。
雪    币: 205
活跃值: 活跃值 (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
NETSYSKEY 活跃值 2008-4-4 22:50
24
0
首先十分感谢你的回复,可能我太苯了,没能理解你的意思!
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生成最终的驱动文件。
这是你讲的三步
第一步,我想问的是 beeper.pas 在哪编辑生成?我理解应该可以在 delphi下吧?或者记事本?这一步应该生成的是obj文件
第二步好理解
第三补应该就是make.exe吧?
不知道理解的是否?
另在用delphi7或delphi2007各需要怎么做,希望能写出详细的每一步骤!再次感谢!
雪    币: 892
活跃值: 活跃值 (74)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
mickeylan 活跃值 14 2008-4-8 14:17
25
0
对于.pas文件只要是个文本编辑器就可以了,不过我也是用delphi2007的编辑器编辑.pas文件的,用着亲切些。至于编译,就必须在命令行下做了。我的做法是先写个makefile,以下是一个makefile的例子:

NAME=beeper
DCC=dcc32
INCLUDE=\mickeylan\KmdKit4D\include
LIB_PATH=\mickeylan\KmdKit4D\lib
DCCFLAGS=-U$(INCLUDE) -B -CG -JP -$A-,C-,D-,G-,H-,I-,L-,P-,V-,W+,Y- -O+
LIBS=ntoskrnl.lib hal.lib win32k.lib ntdll.lib ntutils.lib
LINKFLAGS=/NOLOGO /ALIGN:32 /BASE:0x10000 /SUBSYSTEM:NATIVE /DRIVER /LIBPATH:$(LIB_PATH) /ENTRY:DriverEntry

all : $(NAME).sys

$(NAME).sys : $(NAME).obj		
	rmcoff $(NAME).obj 
	link $(LINKFLAGS) $(LIBS) /out:$(NAME).sys $(NAME).obj
	 
$(NAME).obj : $(NAME).pas
	$(DCC) $(DCCFLAGS) $(NAME).pas

clean :	
	del *.obj
	del *.dcu
	del *.sys


NAME=beeper
DCC=dcc32
INCLUDE=\mickeylan\KmdKit4D\include
LIB_PATH=\mickeylan\KmdKit4D\lib
DCCFLAGS=-U$(INCLUDE) -B -CG -JP -$A-,C-,D-,G-,H-,I-,L-,P-,V-,W+,Y- -O+
LIBS=ntoskrnl.lib hal.lib win32k.lib ntdll.lib ntutils.lib
LINKFLAGS=/NOLOGO /ALIGN:32 /BASE:0x10000 /SUBSYSTEM:NATIVE /DRIVER /LIBPATH:$(LIB_PATH) /ENTRY:DriverEntry

这一段用来定义编译器的别名、include目录和lib目录的宏,用于make时进行宏替换,仔细看一下应该能理解

all : $(NAME).sys

$(NAME).sys : $(NAME).obj		
	rmcoff $(NAME).obj 
	link $(LINKFLAGS) $(LIBS) /out:$(NAME).sys $(NAME).obj
	 
$(NAME).obj : $(NAME).pas
	$(DCC) $(DCCFLAGS) $(NAME).pas

这一段用来定义编译规则,要生成beeper.sys(这里$(NAME)=beeper,在定义部分定义的)依赖于beeper.obj,首先用rmcoff对beeper.obj进行格式转换等工作,然后调用link链接生成beeper.sys。而beeper.obj则依赖于beeper.pas,用DCC编译beeper.pas生成beeper.obj

clean :	
	del *.obj
	del *.dcu
	del *.sys

这部分是用于清理的代码。

在命令行下执行make all就会编译生成驱动文件,执行make clean则会进行清工作。要注意的就是系统环境变量的path部分必须有包含make.exe这个文件的路径信息。如果你用D7的话,唯一要做的就是用D7的system.dcu和sysutils.dcu替换我提供的这两个文件。如果有问题可以到MSN上和我交流。我一天基本都在的^_^
游客
登录 | 注册 方可回帖
返回