由于易博龙接手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直播授课