我在对特定的Delphi Pascal .exe进行逆向工程时遇到了麻烦(旧版vsn,1995年以前,大概是v.3)。从系统调用中,我了解到这可能是一个try..except..finally块,但我无所适从,发现了代码中的“正常”路线以及except和(可能)finally块是什么。

汇编看起来像这样:

782CFC  33 C0                   xor    eax, eax
782CFE  55                      push   ebp
782CFF  68 (782E37)             push   _FINALLY_A_0_782E37
782D04  64 FF 30                push   dword ptr fs:[eax]
782D07  64 89 20                mov    dword ptr fs:[eax], esp

            _try_0_782D0A:
782D0A  8B D3                   mov    edx, ebx
782D0C  8B C6                   mov    eax, esi
782D0E  E8 D1 F3 FF FF          call   ...unrelated...
782D13  8D 56 1C                lea    edx, [esi+1Ch]
.. lots of regular code here ..
.. ending with ..
782E17  8B 18                   mov    ebx, dword ptr [eax]
782E19  FF 53 20                call   dword ptr [ebx+20h]

        finally_1_782E1C:
782E1C  33 C0                   xor    eax, eax
782E1E  5A                      pop    edx
782E1F  59                      pop    ecx
782E20  59                      pop    ecx
782E21  64 89 10                mov    dword ptr fs:[eax], edx
782E24  68 (782E3E)             push   _end_1_782E3E

                @block_L:
782E29  8D 45 F4                lea    eax, [ebp + local_0C]
782E2C  BA 02 00 00 00          mov    edx, 2
782E31  E8 12 E3 F7 FF          call   System.@LStrArrayClr
782E36  C3                      retn

            _FINALLY_A_0_782E37:
782E37  E9 B4 E2 F7 FF          jmp    System.@HandleFinally

            _FINALLY_B_0_782E3C:
782E3C  EB EB                   jmp    @block_L
                ; -------

            _end_1_782E3E:
782E3E  5F                      pop    edi
782E3F  5E                      pop    esi
782E40  5B                      pop    ebx
782E41  8B E5                   mov    esp, ebp
782E43  5D                      pop    ebp
782E44  C3                      retn


-这是我自己的反汇编程序的输出,但是我认为其中没有错误。标签已经自动命名,但是我仍然无法从一个块到下一个块遵循“逻辑”(如果有)。尤其是函数结尾之前的下半部分使我感到困惑。看完伊戈尔的回答:是的。请考虑以下流程图:左图,是对try / finally块进行特殊处理之前的原始图,右图是之后的。

到另一个作为链接,并且代码流在每个try停止。可以清楚地区分finally(E-(F)-K)和retn(G-H / I-J)结构。但是,推动返回地址和异常处理的其他“技巧”,可以克服这一点,如悬空的块N和O所示(它们从无处进入),还有一个单独的块“ M”,

在右侧,我将异常块的初始化与主代码分离(添加了一个新块B),并将finalize结构串联为一个新块(M),最终跳到AFTER_TRY(恰好是最后一个Exit块)。现在很明显,在序言之后,立即启动了if
所有代码都结束于if-else块M,该块总是存在该代码在单个固定点上。


#1 楼

Delphi通过使用Win32结构化异常处理程序(SEH)实现try / except / finally。 SEH的基础知识在Matt Pietrek的经典文章中进行了解释,因此我将跳过仅与Delphi相关的详细信息。

1。 try入口

try块或保护退出时需要销毁的自动变量的块(例如字符串)的入口看起来像以下内容:

xor     eax, eax
push    ebp
push    offset SEH_HANDLER
push    dword ptr fs:[eax]
mov     fs:[eax], esp


这是设置SEH框架的典型方法。运行后,堆栈顶部将如下所示:

       +-----------+
ESP+00 |    next   | <- fs:[0] points here
       +-----------+
ESP+04 |  handler  |
       +-----------+
ESP+08 | saved_ebp |
       +-----------+


指向该结构的指针将传递给SEH处理程序。

2。 try出口

try块的末尾,SEH框架被拆下:

    xor     eax, eax
    pop     edx               ; pop 'next' into edx
    pop     ecx               ; pop handler
    pop     ecx               ; pop saved_ebp
    mov     fs:[eax], edx     ; move 'next' into fs:[0]


如果有finally处理程序或自动析构函数,然后像这样继续:

    push    offset AFTER_TRY  ; make it so the 'ret' will jump to AFTER_TRY
FINALLY_HANDLER:
    <destruct automatic variables created in the try block>
    <finally handler body>
    ret                       ; jumps to AFTER_TRY


否则有一个简单的跳转:

    jmp AFTER_TRY


3 。 finally处理程序

如果程序使用finally语句,或者如果编译器添加了try..finally以保护自动变量,则SEH处理程序如下所示:

SEH_HANDLER:
    jmp     _HandleFinally
    jmp     FINALLY_HANDLER


4。 except处理程序

如果程序使用except处理程序捕获所有异常,则代码看起来有些不同:

SEH_HANDLER:
    jmp     _HandleAnyException
    <handler code>
    call    _DoneExcept


5。 except on处理程序

如果程序使用except on...来匹配所捕获的异常,则编译器将生成一个表,其中包含一个或多个可能的异常类以及相应的处理程序:

SEH_HANDLER:
    jmp     _HandleOnException
    dd <numExceptions>
    dd offset ExceptionClass1
    dd offset OnException1_handler
    dd offset ExceptionClass2
    dd offset OnException2_handler
    <...>

OnException1_handler:
    <handler code>
    call    _DoneExcept

OnException2_handler:
    <handler code>
    call    _DoneExcept


可能会有一些变化,但是我想我涵盖了大部分。

_HandleFinally_HandleAnyException_HandleOnException_DoneExcept的源代码以及其他一些与异常相关的功能可以在VCL源中的system.pas中找到。