【问题标题】:Structured Exception Handler and Delphi结构化异常处理程序和 Delphi
【发布时间】:2011-08-09 09:46:43
【问题描述】:

我正在尝试在不使用 try except 的情况下设置 SEH
(这是我个人的知识,以便更好地了解 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.

我做错了什么?第一个代码和第二个代码有什么区别?

【问题讨论】:

  • +1 表示尝试了一些奇怪的事情。
  • +1 也用于在 Delphi 和 asm 中尝试一些奇怪的东西

标签: delphi exception-handling assembly seh


【解决方案1】:

Windows 要求所有堆栈帧都在系统分配的堆栈内。它还要求堆栈帧在堆栈上按顺序排列。此外,对于异常处理,它要求所有“异常记录”都在堆栈上,并且它们要链接在一个 通过堆栈内存的顺序。

几年前,我在编写微线程库 (http://www.eternallines.com/microthreads) 时发现/阅读了这个。

【讨论】:

  • 感谢您的回答让我明白了困扰我的问题。非常感谢
【解决方案2】:

您不能使用test 过程作为异常回调函数,因为异常回调函数有不同的原型。阅读Matt Pietrek article,IMO 是有关 Win32 SEH 的最佳信息来源。


更新

为了进一步调查,我建议对代码进行以下更改,以使问题更清晰:

function test: Integer;
begin
  WriteLn('Hello from seh');
  Result:= 0;
end;

(因为异常回调应该在 EAX 中返回整数值)

对于第一个代码sn-p

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.
---------------------------

但不是您的异常处理程序。可能 OS 会忽略不基于堆栈的异常注册记录(OS 可以轻松做到,因为它知道最小和最大堆栈值)

【讨论】:

  • 我知道我已经读过,但让我困惑的是为什么第二个代码 sn-p 有效?
  • 异常函数是stdcall定义的,所以它可以在没有任何参数的过程中工作,比如test
  • @opc0de 有趣的是第一个代码 sn-p 不处理异常。看起来操作系统不喜欢不是基于堆栈的异常注册记录。
【解决方案3】:

对于第一个代码,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 在 SEH over Windows 上添加了一些自定义层。

还要注意exception handling changes in Win64 mode。升级到 Delphi XE2 时值得一读。

【讨论】:

  • ret 导致异常,因为我在调试器中看不到它;)我尝试按照您说的方式触发异常,但结果仍然相同...所以这不是问题,谢谢不过为了您的回复
  • 您是否在子程序中尝试过您的代码? TSeh 实例将在堆栈上,就像第二个代码一样。 AFAIK 唯一的区别是 _EXCEPTION_REGISTRATION_RECORD 对于第一个代码在程序的 DATA 块中,而对于第二个代码它在堆栈中......
猜你喜欢
  • 2010-12-28
  • 1970-01-01
  • 2012-01-14
  • 2010-09-14
  • 2020-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多