I'm having trouble reverse-engineering a specific Delphi Pascal .exe (old vsn., pre-1995 so probably v.3). From the system calls I understand this is probably a
try..except..finally block, but I am at a loss finding the 'normal' route through the code, and what the
except and (possibly)
finally blocks are.
The assembly looks like this:
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
-- this is output from my own disassembler, but I don't think there are errors in it. The labels have been auto-named, but I still cannot follow the 'logic' (if any) from one block to the next. In particular, the bottom half, right before the function epilogue, confuses me.
Are these fragments enough to reconstruct the original
After reading Igor's answer: yes they are. Consider these flowcharts: left, original before special handling of try/finally blocks, right, afterwards.
In the original flowchart, I considered every jump from one basic block to another as a link, and the code flow stops at every
if (E-(F)-K) and
if-else (G-H/I-J) structures can clearly be discerned. However, pushing return addresses and the other 'tricks' of exception handling, defeat this, as can be seen by the dangling blocks N and O -- they 'enter' from nowhere --, and a separate block 'M' which comes and goes from nowhere.
At the right, I separated the initialization of the exception block from the main code (adding a new block B), and concatenated the finalize structure into one single new block (M), which ultimately jumps to an AFTER_TRY (which happened to be the last Exit block). Now it's clear that
- right after the prologue, a
- all code ends up at the
finallyblock M, which
- then always exists the code at a single fixed point.