首页
社区
课程
招聘
[注意]关于Delphi驱动开发跨单元变量引用的说明
发表于: 2010-1-2 20:18 8322

[注意]关于Delphi驱动开发跨单元变量引用的说明

2010-1-2 20:18
8322
由于易博龙接手Delphi后,对编译器做了些修改,导致Delphi7以后的版本跨单元变量引用发生了改变,现举例说明如下:
unit Unit1;

interface

uses 
  nt_status, ntoskrnl, unit2;

function _DriverEntry(pDriverObject: PDriverObject; RegistryPath: PUnicodeString) : NTSTATUS; stdcall;

implementation

procedure DriverUnload(pDriverObject: PDriverObject); stdcall;
begin

end;

function _DriverEntry(pDriverObject: PDriverObject; RegistryPath: PUnicodeString) : NTSTATUS; stdcall;
begin
  asm
    int	3;
  end;
  pDriverObject^.DriverUnload := @DriverUnload;
  test1 := 100;
  ChangeOffset;
  test3 := $3A3A;               

  DbgPrint('offset %d, test1=%d, test2=%d, test3=%d'#13#10, ShareNameOffset, test1, test2, test3);
  recTest.b := $7F7F;
  DbgPrint('myRec.a = %d, myRec.b = %d'#13#10, recTest.a, recTest.b); 
  Result:= STATUS_SUCCESS;
end;

end.

unit Unit2;

interface

uses 
  nt_status, ntoskrnl;
  
type
  myRec = packed record
    a: integer;
    b: integer;
  end;

var
  ShareNameOffset: ULONG = 0;    //全局变量
  test1: DWORD;
  test2: DWORD = 1000;
  test3: DWORD;
  recTest: myRec = (a:10; b:100);

procedure ChangeOffset;

implementation

procedure ChangeOffset;
begin
  ShareNameOffset:= $ABC;     //修改
 test2 := $2000;
  recTest.a := $1A1A;
  recTest.b := $2A2A;
end;

end.

以上程序在单元unit2中定义了公共变量ShareNameOffset、test1、test2、test3及recTest记录,其中ShareNameOffset和recTest在定义时就进行了初始化(编译后这几个变量会被放入DATA段),test1、test2和test3没有进行初始化(编译后这几个变量会被放入BSS段),同时在unit2里定义过程ChangeOffset对变量进行赋值操作,在unit1单元里对在unit2里定义的一些变量进行赋值操作并用DbgPrint对unit2定义的变量值进行输出。我们先看下用Delphi7编译这个驱动生成的相关代码:
                public start
start           proc near

arg_0           = dword ptr  8

                push    ebp
                mov     ebp, esp
                int     3               ; Trap to Debugger
                mov     eax, [ebp+arg_0]
                mov     dword ptr [eax+34h], offset sub_10280
                mov     dword_103B4, 64h ; test1 := 100
                call    sub_10344
                mov     dword_103B8, 3A3Ah ; test3 := 3A3A
                mov     eax, dword_103B8
                push    eax
                mov     eax, dword_103A4
                push    eax
                mov     eax, dword_103B4
                push    eax
                mov     eax, dword_103A0
                push    eax
                push    offset Format   ; "offset %d, test1=%d, test2=%d, test3=%d"...
                call    DbgPrint
                add     esp, 14h
                mov     eax, 7F7Fh
                mov     dword_103AC, eax ; recTest.b := $7F7F
                push    eax
                mov     eax, dword_103A8
                push    eax
                push    offset aMyrec_aDMyrec_ ; "myRec.a = %d, myRec.b = %d\r\n"
                call    DbgPrint
                add     esp, 0Ch
                xor     eax, eax
                pop     ebp
                retn    8
start           endp

dword_103A0     dd 0   
                       
dword_103A4     dd 3E8h
                       
dword_103A8     dd 0Ah 
                       
dword_103AC     dd 64h 
                       
                db    0
                db    0
                db    0
                db    0
dword_103B4     dd 0   
                       
dword_103B8     dd 0

下面是用Delphi2010生成的驱动代码:
                public start
start           proc near

arg_0           = dword ptr  8

                push    ebp
                mov     ebp, esp
                int     3               ; Trap to Debugger
                mov     eax, [ebp+arg_0]
                mov     dword ptr [eax+34h], offset sub_10280
                mov     eax, off_103E0
                mov     dword ptr [eax], 64h ; test1 := 64
                call    sub_10390
                mov     eax, off_103E4
                mov     dword ptr [eax], 3A3Ah ; test3 := $3A3A
                mov     eax, off_103E4
                mov     eax, [eax]
                push    eax
                mov     eax, off_103FC
                mov     eax, [eax]
                push    eax
                mov     eax, off_103E0
                mov     eax, [eax]
                push    eax
                mov     eax, off_103F8
                mov     eax, [eax]
                push    eax
                push    offset Format   ; "offset %d, test1=%d, test2=%d, test3=%d"...
                call    DbgPrint
                add     esp, 14h
                mov     edx, off_10400
                mov     eax, 7F7Fh      ; recTest.b := $7F7F
                mov     [edx+4], eax
                mov     edx, off_10400
                push    eax
                mov     eax, off_10400
                mov     eax, [eax]
                push    eax
                push    offset aMyrec_aDMyrec_ ; "myRec.a = %d, myRec.b = %d\r\n"
                call    DbgPrint
                add     esp, 0Ch
                xor     eax, eax
                pop     ebp
                retn    8
start           endp

off_103E0       dd offset unk_10408 ;Delphi2010里这变成指针了  
                                     
off_103E4       dd offset unk_1040C  
                                     
dword_103E8     dd 0                 
                                     
dword_103EC     dd 3E8h              
                                     
dword_103F0     dd 0Ah               
                                     
dword_103F4     dd 64h               
off_103F8       dd offset dword_103E8
off_103FC       dd offset dword_103EC
off_10400       dd offset dword_103F0
                                     
                align 8
unk_10408       db    0              
                db    0
                db    0
                db    0
unk_1040C       db    0              
                db    0
                db    0
                db    0
                db    0
                db    0
                db    0
                db    0
                db    0
                db    0
                db    0
                db    0
                db    0
                db    0
                db    0
                db    0
                db    0
                db    0
                db    0
                db    0

大家对比一下两个代码就能发现区别:D7里是直接引用相关变量,而在D2010里变成了指针引用,具体的指针值是由链接器在链接程序时填充的,而我们写驱动时是用Delphi编译,用M$ Link链接,M$ Link是不会帮我们填充这个指针值的,这就导致我们的程序访问了非法的内存空间,从而出现BSOD。目前最新版的rmcoff2(2.0.0.87及后续版本)会对每个单元导出的公共变量进行修正,为我们生成这个指针,处理后就会得到我们想要的结果。但是大家在嵌入汇编里访问其它单元里定义的变量时就要根据Delphi的不同版本写不同的代码了,因为对嵌入式汇编,Delphi编译器基本是尊重原著的^_^。举个例子:
unit seh;

interface

uses
  nt_status;

//: SEH
{异常处理}
const
  SEH_SafePlaceCounter	= 0;
  SEH_INSTALLED			    = 0;

type
  _SEH = record
    SafeEip: DWORD; {The offset where it's safe to continue execution}
	  PrevEsp: DWORD; {The previous value of esp}
	  PrevEbp: DWORD; {The previous value of ebp}
end;

var
  sseh: _SEH; ///:~  

function DefaultExceptionHandler(pExcept:PEXCEPTION_RECORD; pFrame:DWORD; p_Context:PCONTEXT; pDispatch:DWORD): DWORD; cdecl;

implementation

uses
  ntoskrnl;

//: DefaultExceptionHandler
function DefaultExceptionHandler(pExcept:PEXCEPTION_RECORD;
                                 pFrame:DWORD;
                                 p_Context:PCONTEXT;
                                 pDispatch:DWORD): DWORD; cdecl;
begin
  DbgPrint('Exception: %08X at address: %08X'#13#10,
						pExcept^.ExceptionCode,
						pExcept^.ExceptionAddress);
  asm
    lea eax, sseh
    push (_SEH PTR [eax]).SafeEip
    push (_SEH PTR [eax]).PrevEsp
    push (_SEH PTR [eax]).PrevEbp

    mov eax, pContext
    pop (CONTEXT PTR [eax]).regEbp
    pop (CONTEXT PTR [eax]).regEsp
    pop (CONTEXT PTR [eax]).regEip
  end;
  result := 0;
end; ///:~

end.

我们在seh单元里定义了一个结构_SEH,并定义了一个公共变量sseh。现在我们在别的单元里通过嵌入汇编填充该结构,如果是Delphi7,你可以写成
{安装SEH}
  asm
              push offset DefaultExceptionHandler
              push fs:[0]
              mov fs:[0], esp

              mov sseh.SafeEip, offset SafePlace
              mov sseh.PrevEbp, ebp
              mov sseh.PrevEsp, esp
  end;

但如果是D7以后的版本,你就要写成:
{安装SEH}
          asm
            push offset DefaultExceptionHandler
            push fs:[0]
            mov fs:[0], esp

            mov eax, dword ptr sseh ;在D2010里,sseh是个指针
            mov (_SEH PTR [eax]).SafeEip, offset SafePlace
            mov (_SEH PTR [eax]).PrevEbp, ebp
            mov (_SEH PTR [eax]).PrevEsp, esp
          end;

对于嵌入汇编的这个差别大家就一定要注意了,这个rmcoff也无能为力,只有大家写程序的时候自己注意了。不过我还是推荐大家用Delphi2010写驱动,因为2010提供的一些新特性可以大大降低写驱动的复杂性。当然Kmdkit4D会尽力兼容Delphi7的。

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (10)
雪    币: 296
活跃值: (89)
能力值: ( LV15,RANK:340 )
在线值:
发帖
回帖
粉丝
2
非结构体变量也成了指针?!这个改动还真不知道,找时间试试...
不过指针像这样操作就很有点麻烦
lea eax, sseh
mov (_SEH PTR [eax]).xxx, yyy

以前结构体操作 mov sseh.xxx, yyy 这样用还是很爽的。

{$ifdef VER150}
2010-1-3 00:08
0
雪    币: 1004
活跃值: (75)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
3
是的,你看我上面贴的代码,普通变量也成了指针,我也没弄明白易博龙为什么要这样做,对比d2010和d7生成的代码,就感觉d2010的明显要拖沓很多。不过也只能慢慢适应了,不可能永远抱着delphi7不放,毕竟D2010里的很多新特性是非常诱人的。
2010-1-3 00:18
0
雪    币: 296
活跃值: (89)
能力值: ( LV15,RANK:340 )
在线值:
发帖
回帖
粉丝
4
这个可能是D2010新改成这样的,D2009中都还能这样用:
Unit1.pas.38: stTest.bA := 1;
0040A7A1 C605841D410001 mov byte ptr [$00411d84],$01 ; bA类型为BYTE,默认2字节对齐
Unit1.pas.44: mov ax, $4321
0040A7BC 66B82143 mov ax,$4321
Unit1.pas.45: mov stTest.wB, ax
0040A7C0 66A3861D4100 mov [$00411d86],ax


D2010这边机器还没装,主要是不习惯启动前还要加载那个破解用bpl(目前Update3好像只能用这个方法进去)。感觉D2010中这样修改可能是为了后期扩展,难道以后所有变量都可以运行期改变类型?(比如原先定义一个DWORD,后面不够用了想扩展成array[0..1] of DWORD的话,只用把指针移向新的地方就够了。而原来那种方式显然做不到这样的操作)
纯属胡猜 不过相信Delphi的编译器只会越改越好,我们拭目以待吧。
2010-1-3 09:12
0
雪    币: 1004
活跃值: (75)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
5
是这样的,比如你在unit1里定义一个公共变量test1,unit1里的程序引用test1的时候是直接对test1操作,而如果你在其他单元里引用test1的话,则会通过一个指向test1的指针对其进行操作,我试过Delphi2007、Delphi2009都是这样做的^_^
2010-1-3 13:35
0
雪    币: 296
活跃值: (89)
能力值: ( LV15,RANK:340 )
在线值:
发帖
回帖
粉丝
6
哦,跨单元是这个意思,重新试了一下D2009,确实如此:
test.dpr.65: [B]stTest.wB := 2;[/B]
0040B196 A1FCCA4000       mov eax,[$0040cafc]         ; $0040cafc 是存放 stTest 实际位置$00411d84的指针
0040B19B 66C740020200     mov word ptr [eax+$02],$0002

跨单元后,Delphi 2009直接编译能识别 mov stTest.wB, ax 这样的操作,是因为他完全按照内嵌汇编写的内容转换成非指针的形式:
test.dpr.68: [B]mov stTest.wB, ax[/B]
0040B19B 66A3861D4100     mov [[COLOR="Red"]$00411d86[/COLOR]],ax        ; 这里直接由编译器将stTest.wB转换成地址$00411d84+2了,确实很终于“原著”

而直接写的 stTest.wB := xxx; 这个跨单元操作,则可能按照新编译器的规则转成了指针方式。

跨单元全局变量,好隐蔽的问题...
2010-1-3 19:45
0
雪    币: 1004
活跃值: (75)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
7
哈哈,这个隐蔽的问题害的我的rmcoff2修改了无数次,不过现在终于搞定了^_^
2010-1-3 20:44
0
雪    币: 296
活跃值: (89)
能力值: ( LV15,RANK:340 )
在线值:
发帖
回帖
粉丝
8
[QUOTE=mickeylan;738748]哈哈,这个隐蔽的问题害的我的rmcoff2修改了无数次,不过现在终于搞定了^_^

2010.01.02 -- 彻底解决Delphi7以后版本跨单元变量引用问题,并于跨单元变量引用的详细说明请参阅:http://www.kmdkit4d.net/dispbbs.do?boardId=8&ID=123&star=1
[/QUOTE]

这么快就搞定了?恭喜
2010-1-4 22:58
0
雪    币: 1004
活跃值: (75)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
9
对嵌入式汇编我就无能为力了,这个就要靠写程序的人了,别的都搞定了^_^
2010-1-5 00:22
0
雪    币: 12406
活跃值: (4007)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
10
学习一下~~写驱动蓝到怕了~~
2010-1-5 05:23
0
雪    币: 109
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
delphi牛人啊,,,羡慕ing.....
2010-1-5 10:28
0
游客
登录 | 注册 方可回帖
返回
//