var
x,y,z:char;
N,k:integer;
procedure sub(n:integer;a,c,b:char);
begin
if n=0 then Exit;
sub(n-1,a,b,c);
Inc(k);
Writeln(k,':from',a,'->',c);
//sub(n-1,b,c,a);
end;
begin
{ TODO -oUser -cConsole Main : Insert code here }
write('n=');
readln(n);
k:=0;
x:='A';y:='B';Z:='C';
sub(n,x,y,z);
readln
end.
简单分析如下代码
目的:了解DELPHI递归反编译单步运行分析
sub(n,x,y,z);
4个参数
n存于ESI
X 分别用第一参数表示
Y 第二参数
Z 第三参数
反现个规律:
第三参数压堆栈 第二参数存ECX 第一参数存EDX 递归的计数N-1存于EAX中
假调N=2情况分析
SUB(N,x,y,z) PA:
SUB(N,A,C,B)
SUB(N-1,A,B,C)---》
=============
========= SUB(N-1,A,C,B)
SUB(N-1-1,A,B,C)
SUB(N-1-1,A,C,B)
SUB(N-1-1-1,A,B,C)
Project1.dpr.48: k:=0;
004084A9 33C0 xor eax,eax
004084AB A398A74000 mov [k],eax
Project1.dpr.49: x:='A';y:='B';Z:='C';
004084B0 B041 mov al,$41 //A
084B2 B242 mov dl,$42 //B //存第一
084B4 B143 mov cl,$43 //40A143=C //存第二 起始ECX EDX相反参数初始 eaz存记数
N X Y Z
-- ESI eax eDX ECX 初始时 ESI=N EAX=A EDX=B ECX=C
运行时:
第三参数压堆栈 第二参数存ECX 第一参数存EDX 递归的计数N-1存于EAX中
Z压堆栈 Y压ECX X压 EDX N 压EAX
2 n x y z
esi eaX edx ecx 堆栈 堆栈保存不动 参数下划一级 为CALL准备参数
Project1.dpr.50: sub(n,x,y,z); //PA
///下面开始为CALL准备参数sub(n,x,y,z);
004084B6 51 push ecx //保存第三参数压堆栈 C
004084B7 8BCE mov ecx,esi //esi=N=2假设N=2
004084B9 91 xchg eax,ecx //EAX=2 ecx=A
004084BA 87CA xchg edx,ecx //ecx=B 值 EDX=A
n X=A Y=B
ESI EAX EdX EcX 堆栈 存放第三参数ECX做周转实现
调用CALL前的准备:第三参数压堆栈 第二参数存ECX 第一参数存EDX 递归的计数N存于EAX中
004084BC E8AFFEFFFF call sub
[
Project1.dpr.36: begin
00408370 55 push ebp
00408371 8BEC mov ebp,esp //保存当前EBP值到
00408373 51 push ecx //第三参数压推栈ECX值是B 是SUB(A,B,C)之前调用的SUB(A,C,B)C中值是B第三
00408374 53 push ebx //保存EBX原值
00408375 884DFF mov [ebp-$01],cl [EBP-$01]=B c1值是=$B //这里奇怪多了个B作用
00408378 8BDA mov ebx,edx EBX='a'
Project1.dpr.37: if n=0 then Exit;
0040837A 85C0 test eax,eax //比较N值是否为0 是0结束函数
0040837C 7452 jz +$52
Project1.dpr.38: sub(n-1,a,b,c); //
0040837E 8A55FF mov dl,[ebp-$01]
00408381 52 push edx
00408382 48 dec eax
00408383 8A4D08 mov cl,[ebp+$08]
00408386 8BD3 mov edx,ebx
00408388 E8E3FFFFFF call sub
Project1.dpr.39: Inc(k);
0040838D FF0598A74000 inc dword ptr [k]
Project1.dpr.40: Writeln(k,':from',a,'->',c);
00408393 A104934000 mov eax,[$00409304]
00408398 8B1598A74000 mov edx,[k]
0040839E E8C9AAFFFF call @Write0Long
004083A3 BAE0834000 mov edx,$004083e0
004083A8 E82BBDFFFF call @Write0LString
004083AD 8BD3 mov edx,ebx
004083AF E83CAAFFFF call @Write0Char
004083B4 BAF0834000 mov edx,$004083f0
004083B9 E81ABDFFFF call @Write0LString
004083BE 8A55FF mov dl,[ebp-$01]
004083C1 E82AAAFFFF call @Write0Char
004083C6 E8D1AAFFFF call @WriteLn
004083CB E85CA2FFFF call @_IOTest
Project1.dpr.42: end;
004083D0 5B pop ebx
004083D1 59 pop ecx
]
Project1.dpr.52: end.
004084C1 A170934000 mov eax,[$00409370]
004084C6 E88DA6FFFF call @ReadLn
004084CB E85CA1FFFF call @_IOTest
Project1.dpr.52: end.
004084D0 5E pop esi
004084D1 E816B7FFFF call @Halt0
004084D6 0000 add [eax],al
004084D8 FF db $ff
004084D9 FF db $ff
004084DA FF db $ff
004084DB FF02 inc dword ptr [edx]
004084DD 0000 add [eax],al
004084DF 006E3D add [esi+$3d],ch
004084E2 0000 add [eax],al
004084E4 0000 add [eax],al
004084E6 0000 add [eax],al
004084E8 0000 add [eax],al
004084EA 0000 add [eax],al
004084EC 0000 add [eax],al
004084EE 0000 add [eax],al
004084F0 0000 add [eax],al
////////////////////////////////////////////////
Project1.dpr.48: k:=0;
004084A9 33C0 xor eax,eax
004084AB A398A74000 mov [k],eax
Project1.dpr.49: x:='A';y:='B';Z:='C';
004084B0 B041 mov al,$41 //EAX 65 A
004084B2 B242 mov dl,$42 //eDx 66 B
004084B4 B143 mov cl,$43 //ecx 67 C 这里不同前两个寄存器存的是值 C1存的是地址(存放C的地址0040A143)
Project1.dpr.50: sub(n,x,y,z); 此时ESP=13FFAC=K
004084B6 51 push ecx C压入堆 (ESP)K-4 =13FFA8=0040A143=C //当前栈顶
004084B7 8BCE mov ecx,esi //ESI存放的是N的值 假设是3 放入ECX
004084B9 91 xchg eax,ecx 交换 EAX=3 ECX=$41
004084BA 87CA xchg edx,ecx ECX=$42 EDX=$41 N放入EAX
004084BC E8AFFEFFFF call sub //调用SUB (esp)K-8=13FFA4 值是:004084C1是SUB CALL的下一条指令地址:堆栈存返回地址
Project1.dpr.52: end.
004084C1 A170934000 mov eax,[$00409370]
004084C6 E88DA6FFFF call @ReadLn
004084CB E85CA1FFFF call @_IOTest
Project1.dpr.52: end.
004084D0 5E pop esi
004084D1 E816B7FFFF call @Halt0
004084D6 0000 add [eax],al
004084D8 FF db $ff
004084D9 FF db $ff
004084DA FF db $ff
004084DB FF02 inc dword ptr [edx]
004084DD 0000 add [eax],al
004084DF 006E3D add [esi+$3d],ch
004084E2 0000 add [eax],al
004084E4 0000 add [eax],al
004084E6 0000 add [eax],al
004084E8 0000 add [eax],al
004084EA 0000 add [eax],al
004084EC 0000 add [eax],al
004084EE 0000 add [eax],al
004084F0 0000 add [eax],al
调用SUB
Project1.dpr.36: begin
00408370 55 push ebp
PUSH执行前 K=ESP=0013FFA4= 004084C1 ESP+4=0013FFA8=C EAX=N=3 ECX42 EDX41 ESI=N
执行后 (esp)K-4=0013ffa0=EBP=0013FFC0 //保存EBP
00408373 51 push ecx
执行后:ESP=K-8=0013FF9C=$42 EBP=0013FFA0=K-4
00408374 53 push ebx //
执行后:esp=k-c=0013FF98 ==$41 EBP=0013FFA0=K-4
00408375 884DFF mov [ebp-$01],cl [EBP-$01]=0013FFA0-$01=0013ff9c=$42
ebp-$0100408378 8BDA mov ebx,edx==41 //
Project1.dpr.37: if n=0 then Exit;
0040837A 85C0 test eax,eax
0040837C 7452 jz +$52
Project1.dpr.38: sub(n-1,a,b,c);
0040837E 8A55FF mov dl,[ebp-$01]
00408381 52 push edx
00408382 48 dec eax
00408383 8A4D08 mov cl,[ebp+$08]
00408386 8BD3 mov edx,ebx
00408388 E8E3FFFFFF call sub
Project1.dpr.39: Inc(k);
0040838D FF0598A74000 inc dword ptr [k]
Project1.dpr.40: Writeln(k,':from',a,'->',c);
00408393 A104934000 mov eax,[$00409304]
00408398 8B1598A74000 mov edx,[k]
0040839E E8C9AAFFFF call @Write0Long
004083A3 BAE0834000 mov edx,$004083e0
004083A8 E82BBDFFFF call @Write0LString
004083AD 8BD3 mov edx,ebx
004083AF E83CAAFFFF call @Write0Char
004083B4 BAF0834000 mov edx,$004083f0
004083B9 E81ABDFFFF call @Write0LString
004083BE 8A55FF mov dl,[ebp-$01]
004083C1 E82AAAFFFF call @Write0Char
004083C6 E8D1AAFFFF call @WriteLn
004083CB E85CA2FFFF call @_IOTest
Project1.dpr.42: end;
004083D0 5B pop ebx
004083D1 59 pop ecx
本文详细解析了Delphi环境下递归函数的反编译过程及单步运行机制,通过具体实例展示了参数传递、堆栈操作及递归调用的内部细节。
647

被折叠的 条评论
为什么被折叠?



