我正在尝试设置SEH
而不使用try except
(这是我个人的知识,可以更好地了解SEH的工作原理)
以下代码不起作用
type
TSeh = packed record
OldSeh:DWORD;
NewSeh:DWORD;
end;
procedure test;
begin
WriteLn('Hello from seh');
end;
var
eu:TSeh;
old_seh:DWORD;
begin
asm
mov eax,fs:[0]
mov old_seh,eax
end;
eu.OldSeh := old_seh;
eu.NewSeh := DWORD(@test);
asm
mov eax,offset eu
mov fs:[0],eax
ret //This will cause an exception because jumps on an invalid memory address
end;
end.
但这确实
procedure test;
begin
WriteLn('Hello from seh');
end;
begin
asm
push offset test
push fs:[0]
mov fs:[0],esp
ret //This will cause an exception because jumps on an invalid memory address
end;
end.
我做错了什么?第一个代码和第二个代码有什么区别?
答案 0 :(得分:6)
Windows要求所有堆栈帧都在系统分配的堆栈内。它还要求堆栈帧在堆栈上按顺序排列。此外,对于异常处理,它要求所有“异常记录”都在堆栈中,并且要求它们链接到 通过堆栈内存顺序排序。
几年前我在编写一个微线程库(http://www.eternallines.com/microthreads)时想到了这个/读过这个。
答案 1 :(得分:4)
您不能将test
过程用作异常回调函数,因为异常回调函数具有不同的原型。阅读Matt Pietrek article,IMO是有关Win32 SEH的最佳信息来源。
<强>更新强>
对于进一步的调查,我建议在代码中进行以下更改,以使问题更加清晰:
function test: Integer;
begin
WriteLn('Hello from seh');
Result:= 0;
end;
(因为异常回调应该在EAX中返回整数值)
并为第一个代码段
begin
asm
mov eax,fs:[0]
mov old_seh,eax
end;
eu.OldSeh := old_seh;
eu.NewSeh := Cardinal(@test);
asm
lea eax, eu
mov fs:[0],eax
mov ds:[0],eax //This will cause an AV exception
end;
end.
现在您看到异常正确处理为:
---------------------------
Debugger Fault Notification
---------------------------
Project C:\Users\Serg\Documents\RAD Studio\Projects\Project13.exe faulted with
message: 'access violation at 0x004050f5: write of address 0x00000000'. Process
Stopped. Use Step or Run to continue.
---------------------------
但不是您的异常处理程序。操作系统可能会忽略非基于堆栈的异常注册记录(操作系统可以很容易地执行此操作,因为它知道最小和最大堆栈值)
答案 2 :(得分:2)
对于第一个代码,TSeh
位于可执行文件的DATA全局部分,而第二个代码将其存储在堆栈中。
这是恕我直言,差异在于。 _EXCEPTION_REGISTRATION_RECORD结构可能应该在堆栈上。不知道为什么,老实说(一些低级SS注册技巧?)。
要引发异常,您最好尝试类似于零除法或访问零绝对地址的内容:
PInteger(nil)^ := 0; // will always raise an exception
asm
xor eax,eax
mov [eax],eax // will always raise an exception
end;
关于如何拦截Delphi中的异常,请查看at this article。实际上,Delphi通过Windows在SEH上添加了一些自定义层。
还要注意exception handling changes in Win64 mode。值得一读的是Delphi XE2。