[标题]随便反汇编Delphi程序段整理
[作者]神杀中龙
用OllyDBG反Delphi写的程序, 一些Delphi的函数反不出来。
引用看学 foxabu说的 Sig ... LIB函数。 
或者可以说,Delphi内有不少SysUtils的函数。

我就把这些识别不了的叫做Sig...Lib函数吧。我整理下,
并确定下各种Delphi特殊的指令序列是怎么出来的。

分下类,


1. 函数调用函数参数  
2. 顺序语句
3. 分支语句
4. 循环语句
5. Delphi自动添加语句, 一般是添加在函数头和函数尾, 已经语句指令中间。



默认Delphi入口

0040338C > $  55            push    ebp
0040338D   .  8BEC          mov     ebp, esp
0040338F   .  83C4 F0       add     esp, -10
00403392   .  B8 6C334000   mov     eax, 0040336C
00403397   .  E8 2CFFFFFF   call    004032C8
0040339C   .  E8 5BFAFFFF   call    00402DFC

[源]
begin
end;
这是什么也没加的情形。

[源]
Writeln('Hello World');
[反]这五行一般是Writeln写出来的。
00403890   .  A1 A4404000   mov     eax, dword ptr [4040A4]
00403895   .  BA BC384000   mov     edx, 004038BC                    ;  ASCII "Hello World"
0040389A   .  E8 B1FBFFFF   call    00403450
0040389F   .  E8 38F2FFFF   call    00402ADC
004038A4   .  E8 73ECFFFF   call    0040251C

当从主函数调用一个无参数函数时程序头就变成这样了。
00403394 > $  55            push    ebp
00403395   .  8BEC          mov     ebp, esp
00403397   .  83C4 F0       add     esp, -10
0040339A   .  B8 74334000   mov     eax, 00403374
0040339F   .  E8 24FFFFFF   call    004032C8
004033A4   .  E8 9BFFFFFF   call    00403344
004033A9   .  E8 4EFAFFFF   call    00402DFC

其中, 第二个call就是你中转的函数。要步入才行。
00403344  /$  89C0          mov     eax, eax
00403346  |.  89C0          mov     eax, eax
00403348  \.  C3            retn
那个函数是个空函数, 我加了两条 汇编指令以区分结尾,
另外也可以再加两条指令以区分头。等会儿我们就那样做。

目前的任务是将Delphi一些调用整理以下。关键是整理call的部分。
这样当我们写了某个形式的代码,遇见那样的序列就不需要步进了。

为了逆向我们先练习 反高级语言写的代码, 然后再先有代码后
逆。

00403838  /$  89C0          mov     eax, eax     =>这就是我用
                                              asm mov eax,ax; mov eax,eax; end;加的,以识别
0040383A  |.  89C0          mov     eax, eax

#Writeln开始
0040383C  |.  A1 A4404000   mov     eax, dword ptr [4040A4]
00403841  |.  BA 64384000   mov     edx, 00403864                    ;  ASCII "Hello World"
00403846  |.  E8 05FCFFFF   call    00403450
0040384B  |.  E8 8CF2FFFF   call    00402ADC
00403850  |.  E8 C7ECFFFF   call    0040251C
#结束
00403855  |.  89C0          mov     eax, eax
00403857  |.  89C0          mov     eax, eax

00403859  |.  33C0          xor     eax, eax                         ;这条指令就是多出的指令
                                                                     ;是Delphi自动加的。其实

这句是由于调用的是function的原因, 返回值, 而如果调用procedure是没有这句的。
好既然Delphi会自动来加指令,我们就来看看procedure 和 各种不同返回值的情形。
0040385B  \.  C3            retn

00403838  /$  89C0          mov     eax, eax
0040383A  |.  89C0          mov     eax, eax
0040383C  |.  A1 A4404000   mov     eax, dword ptr [4040A4]
00403841  |.  BA 64384000   mov     edx, 00403864                    ;  ASCII "Hello World"
00403846  |.  E8 05FCFFFF   call    00403450
0040384B  |.  E8 8CF2FFFF   call    00402ADC
00403850  |.  E8 C7ECFFFF   call    0040251C
00403855  |.  89C0          mov     eax, eax
00403857  |.  89C0          mov     eax, eax
00403859  \.  C3            retn

这就是一个procedure的情形, 这里就没有 xor eax, eax;了。 procedure就相当与void类型的函数。


00403838  /$  53            push    ebx
00403839  |.  89C0          mov     eax, eax
0040383B  |.  89C0          mov     eax, eax
0040383D  |.  A1 A4404000   mov     eax, dword ptr [4040A4]
00403842  |.  BA 68384000   mov     edx, 00403868                    ;  ASCII "Hello World"
00403847  |.  E8 04FCFFFF   call    00403450
0040384C  |.  E8 8BF2FFFF   call    00402ADC
00403851  |.  E8 C6ECFFFF   call    0040251C
00403856  |.  89C0          mov     eax, eax
00403858  |.  89C0          mov     eax, eax
0040385A  |.  8BC3          mov     eax, ebx
0040385C  |.  5B            pop     ebx
0040385D  \.  C3            retn

这种情形是 我没有加 result := 0或别的。 
function PWinMain():Integer;
begin
       asm
         mov eax,eax;
         mov eax,eax;
     end;
     Writeln('Hello World');
     asm
         mov eax,eax;
         mov eax,eax;
     end;  
       
end;
我加上result后又会如何呢?
00403838  /$  89C0          mov     eax, eax
0040383A  |.  89C0          mov     eax, eax
0040383C  |.  A1 A4404000   mov     eax, dword ptr [4040A4]
00403841  |.  BA 64384000   mov     edx, 00403864                    ;  ASCII "Hello World"
00403846  |.  E8 05FCFFFF   call    00403450
0040384B  |.  E8 8CF2FFFF   call    00402ADC
00403850  |.  E8 C7ECFFFF   call    0040251C
00403855  |.  89C0          mov     eax, eax
00403857  |.  89C0          mov     eax, eax
00403859  |.  33C0          xor     eax, eax
0040385B  \.  C3            retn

加上后就没有mov     eax, ebx
0040385C  |.  5B            pop     ebx
这样的结尾指令了, 而换成了 xor eax, eax; xor是在做逻辑与,就是清零。

00403838  /$  89C0          mov     eax, eax
0040383A  |.  89C0          mov     eax, eax
0040383C  |.  A1 A4404000   mov     eax, dword ptr [4040A4]
00403841  |.  BA 68384000   mov     edx, 00403868                    ;  ASCII "Hello World"
00403846  |.  E8 05FCFFFF   call    00403450
0040384B  |.  E8 8CF2FFFF   call    00402ADC
00403850  |.  E8 C7ECFFFF   call    0040251C
00403855  |.  89C0          mov     eax, eax
00403857  |.  89C0          mov     eax, eax
00403859  |.  B8 01000000   mov     eax, 1        ; result := 1
0040385E  \.  C3            retn
函数的返回值默认用eax返回。

00403838  /$  89C0          mov     eax, eax
0040383A  |.  89C0          mov     eax, eax
0040383C  |.  A1 A4404000   mov     eax, dword ptr [4040A4]
00403841  |.  BA 68384000   mov     edx, 00403868                    ;  ASCII "Hello World"
00403846  |.  E8 05FCFFFF   call    00403450
0040384B  |.  E8 8CF2FFFF   call    00402ADC
00403850  |.  E8 C7ECFFFF   call    0040251C
00403855  |.  89C0          mov     eax, eax
00403857  |.  89C0          mov     eax, eax
00403859  |.  B8 0A000000   mov     eax, 0A       ; result := $A;
0040385E  \.  C3            retn

还是像上面说的, 我们先关注高级语言, 然后看反汇编, 而不是 看反汇编像高级语言。
[源]
var MyMessage :string;

function PWinMain():Integer;
begin
       asm
         mov eax,eax;
         mov eax,eax;
     end;
     MyMessage := 'Hello world!';
     Writeln(MyMessage);
     asm
         mov eax,eax;
         mov eax,eax;
     end;  
     result := $A;
end;

[反]
0040387C  /$  89C0          mov     eax, eax
0040387E  |.  89C0          mov     eax, eax

00403880  |.  B8 5C564000   mov     eax, 0040565C
00403885  |.  BA BC384000   mov     edx, 004038BC                    ;  ASCII "Hello world!"
0040388A  |.  E8 4DFBFFFF   call    004033DC                         ;  给一个全局string赋值

0040388F  |.  A1 A4404000   mov     eax, dword ptr [4040A4]
00403894  |.  8B15 5C564000 mov     edx, dword ptr [40565C]          ;这里一个立即数地址,被

替换成了一个存储器操作数。  
0040389A  |.  E8 F5FBFFFF   call    00403494
0040389F  |.  E8 38F2FFFF   call    00402ADC
004038A4  |.  E8 73ECFFFF   call    0040251C
004038A9  |.  89C0          mov     eax, eax
004038AB  |.  89C0          mov     eax, eax
004038AD  |.  B8 0A000000   mov     eax, 0A
004038B2  \.  C3            retn
我们虽然中心放在高级语言, 次而反汇编, 但是心里也要记住一下平时反不出的汇编指令。
比如就是像不到某种形式是通过什么高级语言指令得到的, 这时一但出来马上就比较一下。

这个call 004033DC在Delphi里还很多, 它不像Win32 API可以被识别出名字来。
004033DC  /$  85D2          test    edx, edx                         ;  heloMain.004038BC
004033DE  |.  74 24         je      short 00403404
004033E0  |.  8B4A F8       mov     ecx, dword ptr [edx-8]
004033E3  |.  41            inc     ecx
004033E4  |.  7F 1A         jg      short 00403400
004033E6  |.  50            push    eax
004033E7  |.  52            push    edx
004033E8  |.  8B42 FC       mov     eax, dword ptr [edx-4]
004033EB  |.  E8 30000000   call    00403420
004033F0  |.  89C2          mov     edx, eax
004033F2  |.  58            pop     eax
004033F3  |.  52            push    edx
004033F4  |.  8B48 FC       mov     ecx, dword ptr [eax-4]
004033F7  |.  E8 50F1FFFF   call    0040254C
004033FC  |.  5A            pop     edx
004033FD  |.  58            pop     eax
004033FE  |.  EB 04         jmp     short 00403404
00403400  |>  F0:FF42 F8    lock inc dword ptr [edx-8]

00403404  |>  8710          xchg    dword ptr [eax], edx
00403406  |.  85D2          test    edx, edx
00403408  |.  74 14         je      short 0040341E
0040340A  |.  8B4A F8       mov     ecx, dword ptr [edx-8]
0040340D  |.  49            dec     ecx
0040340E  |.  7C 0E         jl      short 0040341E
00403410  |.  F0:FF4A F8    lock dec dword ptr [edx-8]
00403414  |.  75 08         jnz     short 0040341E
00403416  |.  8D42 F8       lea     eax, dword ptr [edx-8]
00403419  |.  E8 7AF0FFFF   call    00402498
0040341E  \>  C3            retn
上面这段是 0040388A  |.  E8 4DFBFFFF   call    004033DC     的反汇编。大概记住它的形态。
这样再看到就直接返回就可以了, Ctrl + F9

上面拿个是全局变量赋值,我们看局变量的情形。
[反]当给一个局部string赋值时 push ebp; mov ebp esp; 自动就出来了。   而且又自动加了不少代码


00403864  /$  55            push    ebp
00403865  |.  8BEC          mov     ebp, esp
00403867  |.  6A 00         push    0
00403869  |.  53            push    ebx
0040386A  |.  33C0          xor     eax, eax
0040386C  |.  55            push    ebp
0040386D  |.  68 BF384000   push    004038BF
00403872  |.  64:FF30       push    dword ptr fs:[eax]             ;标志性的是这两句
00403875  |.  64:8920       mov     dword ptr fs:[eax], esp


00403878  |.  89C0          mov     eax, eax
0040387A  |.  89C0          mov     eax, eax

0040387C  |.  8D45 FC       lea     eax, dword ptr [ebp-4]           ;局部变量用lea取
0040387F  |.  BA D4384000   mov     edx, 004038D4                    ;  ASCII "Hello world!"
00403884  |.  E8 53FBFFFF   call    004033DC

00403889  |.  A1 A4404000   mov     eax, dword ptr [4040A4]
0040388E  |.  8B55 FC       mov     edx, dword ptr [ebp-4]
00403891  |.  E8 E6FBFFFF   call    0040347C
00403896  |.  E8 41F2FFFF   call    00402ADC
0040389B  |.  E8 7CECFFFF   call    0040251C
004038A0  |.  89C0          mov     eax, eax
004038A2  |.  89C0          mov     eax, eax

004038A4  |.  BB 0A000000   mov     ebx, 0A            result:= $A; 
004038A9  |.  33C0          xor     eax, eax
004038AB  |.  5A            pop     edx
004038AC  |.  59            pop     ecx
004038AD  |.  59            pop     ecx
004038AE  |.  64:8910       mov     dword ptr fs:[eax], edx          ;尾部也发生了变化
004038B1  |.  68 C6384000   push    004038C6
004038B6  |>  8D45 FC       lea     eax, dword ptr [ebp-4]
004038B9  |.  E8 FAFAFFFF   call    004033B8
004038BE  \.  C3            retn
004038BF   .^ E9 90F5FFFF   jmp     00402E54                         ;会发现有两个retn
004038C4   .^ EB F0         jmp     short 004038B6
004038C6   .  8BC3          mov     eax, ebx
004038C8   .  5B            pop     ebx
004038C9   .  59            pop     ecx
004038CA   .  5D            pop     ebp
004038CB   .  C3            retn                                     
[源]
function PWinMain():Integer;
var MyMessage :string;
begin
       asm
         mov eax,eax;
         mov eax,eax;
     end;
     MyMessage := 'Hello world!';
     Writeln(MyMessage);
     asm
         mov eax,eax;
         mov eax,eax;
     end;  
     result := $A;
end;
这些变化还仅仅是对一个局部string进行赋值并引用, 如果不引用是什么情形呢?

[反]
00403370  /$  55            push    ebp
00403371  |.  8BEC          mov     ebp, esp
00403373  |.  6A 00         push    0
00403375  |.  53            push    ebx
00403376  |.  33C0          xor     eax, eax

00403378  |.  55            push    ebp
00403379  |.  68 B4334000   push    004033B4
0040337E  |.  64:FF30       push    dword ptr fs:[eax]
00403381  |.  64:8920       mov     dword ptr fs:[eax], esp
00403384  |.  89C0          mov     eax, eax
00403386  |.  89C0          mov     eax, eax

00403388  |.  8D45 FC       lea     eax, dword ptr [ebp-4]
0040338B  |.  BA CC334000   mov     edx, 004033CC                    ;  ASCII "Hello world!"
00403390  |.  E8 7BFBFFFF   call    00402F10

00403395  |.  89C0          mov     eax, eax
00403397  |.  89C0          mov     eax, eax
00403399  |.  BB 0A000000   mov     ebx, 0A
0040339E  |.  33C0          xor     eax, eax
004033A0  |.  5A            pop     edx
004033A1  |.  59            pop     ecx
004033A2  |.  59            pop     ecx
004033A3  |.  64:8910       mov     dword ptr fs:[eax], edx
004033A6  |.  68 BB334000   push    004033BB
004033AB  |>  8D45 FC       lea     eax, dword ptr [ebp-4]
004033AE  |.  E8 39FBFFFF   call    00402EEC
004033B3  \.  C3            retn
004033B4   .^ E9 CFF5FFFF   jmp     00402988
004033B9   .^ EB F0         jmp     short 004033AB
004033BB   .  8BC3          mov     eax, ebx
004033BD   .  5B            pop     ebx
004033BE   .  59            pop     ecx
004033BF   .  5D            pop     ebp
004033C0   .  C3            retn

这是不引用, 就是没调用Writeln(MyMessage);
我们观察下  00403390  |.  E8 7BFBFFFF   call    00402F10 的形态和全局的有区别没?
00402F10  /$  85D2          test    edx, edx
00402F12  |.  74 0A         je      short 00402F1E
00402F14  |.  8B4A F8       mov     ecx, dword ptr [edx-8]
00402F17  |.  41            inc     ecx
00402F18  |.  7E 04         jle     short 00402F1E
00402F1A  |.  F0:FF42 F8    lock inc dword ptr [edx-8]
00402F1E  |>  8710          xchg    dword ptr [eax], edx

00402F20  |.  85D2          test    edx, edx
00402F22  |.  74 14         je      short 00402F38
00402F24  |.  8B4A F8       mov     ecx, dword ptr [edx-8]
00402F27  |.  49            dec     ecx
00402F28  |.  7C 0E         jl      short 00402F38
00402F2A  |.  F0:FF4A F8    lock dec dword ptr [edx-8]

00402F2E  |.  75 08         jnz     short 00402F38
00402F30  |.  8D42 F8       lea     eax, dword ptr [edx-8]
00402F33  |.  E8 20F5FFFF   call    00402458
00402F38  \>  C3            retn
对一个局部变量赋值少了些动作。看到此情形也可以返回了。
00403388  |.  8D45 FC       lea     eax, dword ptr [ebp-4]
0040338B  |.  BA CC334000   mov     edx, 004033CC                    ;  ASCII "Hello world!"
00403390  |.  E8 7BFBFFFF   call    00402F10                         ;一般局部填充很容易识别

再看参数是string时填充的情形, 函数为void
004033B0  |.  8D45 FC       lea     eax, dword ptr [ebp-4]
004033B3  |.  BA FC334000   mov     edx, 004033FC                    ;  ASCII "Hello world!"
004033B8  |.  E8 53FBFFFF   call    00402F10
004033BD  |.  8B45 FC       mov     eax, dword ptr [ebp-4]           ; 参数
004033C0  |.  E8 ABFFFFFF   call    00403370                         ; 我自定义的函数

PrintMessage

当, PrintMessage('Hello World');时则变为
004033BD  |.  B8 FC334000   mov     eax, 004033FC                    ;  ASCII "Hello world!"
004033C2  |.  E8 A9FFFFFF   call    00403370

00403885  |.  BA BC384000   mov     edx, 004038BC                    ;  ASCII "Hello world!"
0040388A  |.  E8 4DFBFFFF   call    004033DC                         ;  给一个全局string赋值
看这四句是不是有些相似, 知道的call 00403370是我自定义的, 不知道的就以为是在为
一个全局string赋值呢。 根据eax,  edx也可以判断。 给一个全局变量赋值会用edx而不是eax;
而当函数参数只有一个时32位的参数, 首选eax

[反]
00403370   $  55            push    ebp
00403371   .  8BEC          mov     ebp, esp
00403373   .  33C0          xor     eax, eax
00403375   .  55            push    ebp
00403376   .  68 8F334000   push    0040338F
0040337B   .  64:FF30       push    dword ptr fs:[eax]
0040337E   .  64:8920       mov     dword ptr fs:[eax], esp

00403381   .  33C0          xor     eax, eax
00403383   .  5A            pop     edx
00403384   .  59            pop     ecx
00403385   .  59            pop     ecx
00403386   .  64:8910       mov     dword ptr fs:[eax], edx
00403389   .  68 96334000   push    00403396
0040338E   >  C3            retn                                     ;  RET 用作跳转到 

00403396
0040338F   .^ E9 F4F5FFFF   jmp     00402988
00403394   .^ EB F8         jmp     short 0040338E
00403396   >  5D            pop     ebp
00403397   .  C3            retn

[源]
procedure PrintMessage(msg: string);
begin
          
end;
这是一个空函数, 但是由于参数中有 string类型的, 所以会多出不少代码。

我们暂时写的不引入各种调用类的情形, 因为一旦这部分单纯的函数形式掌握好就很容易区分
含类和不含类的情形。 这部分基础很重要。

[源]
const a = 45;
Writeln(a);
[反]
0040394D  |.  A1 A4404000   mov     eax, dword ptr [4040A4]
00403952  |.  BA 2D000000   mov     edx, 2D  ;2D = 45;
00403957  |.  E8 3CF2FFFF   call    00402B98
0040395C  |.  E8 67F2FFFF   call    00402BC8
00403961  |.  E8 B6EBFFFF   call    0040251C
这是输出一个常量, 看看跟输出个变量有什么区别
常量更像个立即数, 注意这里起作用的是 mov edx, 2D; 以为是第一句就找不到了。


这篇跟前面的多少有重复, 但是在最后部分,将引入更复杂的表达式 操作数和逻辑操作
数的情形, 由于我习惯用C/C++写, 所以只好把C/C++写的直接转为Delphi再反汇编。
[源]
I := 0;
[反]
0040335C   .  A3 5C564000   mov     dword ptr [40565C], eax
给一个整数 赋0   I:= 0;

[源]
I := 0;
     S := 'Hello world!';
[反]
004033A0   .  A3 5C564000   mov     dword ptr [40565C], eax          ; I:= 0
004033A5   .  B8 60564000   mov     eax, 00405660                    ; 根据前面说可以判断是

在给全局string赋值
004033AA   .  BA E0334000   mov     edx, 004033E0                    ;  ASCII "Hello world!"
004033AF   .  E8 5CFBFFFF   call    00402F10

下面这段复杂写, 可以自己试着分解。
[源]
I := 0;
     S := 'Hello world!';

     while(I<=Length(S)) and (S[i]<>',') do
     begin
           Writeln('Hello World');
     end;
[反]
004038A7   .  8903          mov     dword ptr [ebx], eax
004038A9   .  8BC6          mov     eax, esi
004038AB   .  BA 14394000   mov     edx, 00403914                    ;  ASCII "Hello world!"
004038B0   .  E8 27FBFFFF   call    004033DC

#循环开始  我这么写是个死循环,但是暂且不管, 因为我们是看, 不是执行。
004038B5   .  EB 19         jmp     short 004038D0

#Writeln 5句。
004038B7   >  A1 A4404000   mov     eax, dword ptr [4040A4]
004038BC   .  BA 2C394000   mov     edx, 0040392C                    ;  ASCII "Hello World"
004038C1   .  E8 D6FBFFFF   call    0040349C
004038C6   .  E8 11F2FFFF   call    00402ADC
004038CB   .  E8 4CECFFFF   call    0040251C


004038D0   >  8B06          mov     eax, dword ptr [esi]
004038D2   .  E8 BDFBFFFF   call    00403494

004038D7   .  3B03          cmp     eax, dword ptr [ebx]
004038D9   .  7C 0B         jl      short 004038E6

004038DB   .  8B06          mov     eax, dword ptr [esi]
004038DD   .  8B13          mov     edx, dword ptr [ebx]
004038DF   .  807C10 FF 2C  cmp     byte ptr [eax+edx-1], 2C
004038E4   .^ 75 D1         jnz     short 004038B7

为了分解这个while, 我们首先要确定while形式, Length形式, 单条件形式加逻辑与后形式。
我们先看个一般while形式。


下面只是个死循环while
[源]
I := 0;
     S := 'Hello world!';

     while(True) do
     begin
           Writeln('Hello World');
     end;

00403892   .  33C0          xor     eax, eax
00403894   .  A3 5C564000   mov     dword ptr [40565C], eax          ;I:= 0;

00403899   .  B8 60564000   mov     eax, 00405660
0040389E   .  BA F0384000   mov     edx, 004038F0                    ;  ASCII "Hello world!"
004038A3   .  E8 34FBFFFF   call    004033DC

004038A8   >  A1 A4404000   mov     eax, dword ptr [4040A4]
004038AD   .  BA 08394000   mov     edx, 00403908                    ;  ASCII "Hello World"
004038B2   .  E8 DDFBFFFF   call    00403494
004038B7   .  E8 20F2FFFF   call    00402ADC
004038BC   .  E8 5BECFFFF   call    0040251C
004038C1   .^ EB E5         jmp     short 004038A8                   ;  死循环下让jmp始终跳

会就可以了,真简洁。 我们再看单独一个Length传给一个整数是什么情形,然后再复合。

[源]
N := Length(S);   //N是Integer型, Length取得S串长度
[反]
004038B0   .  A1 60564000   mov     eax, dword ptr [405660]
004038B5   .  E8 DAFBFFFF   call    00403494                    ;这个call又是Delphi的一个库

函数, 而且识别不出来,我们看看它的情形。
004038BA   .  A3 64564000   mov     dword ptr [405664], eax     ; 传递给N

Length(S);
00403494  /$  85C0          test    eax, eax
00403496  |.  74 03         je      short 0040349B
00403498  |.  8B40 FC       mov     eax, dword ptr [eax-4]
0040349B  \>  C3            retn
很短,  当再看到这种情形可能就是Length了。Delphi还有少类似的库函数如 IntToStr  PChar()强制转

换等。  应该搜集一下。

下面再单独 让I 和一个常数作比较, 然后再让I 和 Length返回后的比较。符号还用 <= 转为跳转指令

就是 jg 大于。 这只是推测。
[源]
while(I <= 10) do
     begin
           Writeln('Hello World');
     end;
[反]
004038A8   .  833D 5C564000>cmp     dword ptr [40565C], 0A
004038AF   .  7F 22         jg      short 004038D3                   ;首次指令和我推测的一样
004038B1   >  A1 A4404000   mov     eax, dword ptr [4040A4]
004038B6   .  BA 18394000   mov     edx, 00403918                    ;  ASCII "Hello World"
004038BB   .  E8 D4FBFFFF   call    00403494
004038C0   .  E8 17F2FFFF   call    00402ADC
004038C5   .  E8 52ECFFFF   call    0040251C
004038CA   .  833D 5C564000>cmp     dword ptr [40565C], 0A           ; A = 10
004038D1   .^ 7E DE         jle     short 004038B1

反汇编最好实现学过些汇编如 80x86  Win32汇编等,对其指令系统的学习有助于识别各种反

汇编指令。


然后我们把立即数 10 换为Length(S)
[源]
I := 0;
     S := 'Hello world!';
     
     
     while(I <= Length(S)) do
     begin
           Writeln('Hello World');
     end;
[反]
0040389A   .  33C0          xor     eax, eax
0040389C   .  A3 5C564000   mov     dword ptr [40565C], eax          ;dword ptr [40565C]= I

004038A1   .  B8 60564000   mov     eax, 00405660
004038A6   .  BA 08394000   mov     edx, 00403908                    ;  ASCII "Hello world!"
004038AB   .  E8 2CFBFFFF   call    004033DC

#循环
004038B0   .  EB 19         jmp     short 004038CB
004038B2   >  A1 A4404000   mov     eax, dword ptr [4040A4]
004038B7   .  BA 20394000   mov     edx, 00403920                    ;  ASCII "Hello World"
004038BC   .  E8 DBFBFFFF   call    0040349C
004038C1   .  E8 16F2FFFF   call    00402ADC
004038C6   .  E8 51ECFFFF   call    0040251C

004038CB   >  A1 60564000   mov     eax, dword ptr [405660]     ;复合后几乎没啥新变化
004038D0   .  E8 BFFBFFFF   call    00403494
004038D5   .  3B05 5C564000 cmp     eax(call返回的长度), dword ptr [40565C](I)     ;但是比较

的发现变化了。
004038DB   .^ 7D D5         jge     short 004038B2

下面两行是单独的Length(S)经过复合正好。
004038B0   .  A1 60564000   mov     eax, dword ptr [405660]
004038B5   .  E8 DAFBFFFF   call    00403494 


好,下面我们加个and , 但是 仅仅用个一般的条件,比如 True或False
[源]
while (I <= Length(S)) and True do
     begin
           Writeln('Hello World');
     end;
[反]
0040389A   .  33C0          xor     eax, eax
0040389C   .  A3 5C564000   mov     dword ptr [40565C], eax          ;I:=0

004038A1   .  B8 60564000   mov     eax, 00405660          
004038A6   .  BA 08394000   mov     edx, 00403908                    ;  ASCII "Hello world!"
004038AB   .  E8 2CFBFFFF   call    004033DC                         ;  S:= 'Hello world!';

004038B0   .  EB 19         jmp     short 004038CB
004038B2   >  A1 A4404000   mov     eax, dword ptr [4040A4]
004038B7   .  BA 20394000   mov     edx, 00403920                    ;  ASCII "Hello World"
004038BC   .  E8 DBFBFFFF   call    0040349C
004038C1   .  E8 16F2FFFF   call    00402ADC
004038C6   .  E8 51ECFFFF   call    0040251C

004038CB   >  A1 60564000   mov     eax, dword ptr [405660]
004038D0   .  E8 BFFBFFFF   call    00403494

004038D5   .  3B05 5C564000 cmp     eax, dword ptr [40565C]
004038DB   .^ 7D D5         jge     short 004038B2

看来加个 True没什么变化。还是全加上条件吧, 但是先要把单独的条件测试下形态。

我们看下
[源]
 c := S[1];
[反]
0040339E   .  33C0          xor     eax, eax
004033A0   .  A3 5C564000   mov     dword ptr [40565C], eax

004033A5   .  B8 60564000   mov     eax, 00405660
004033AA   .  BA EC334000   mov     edx, 004033EC                    ;  ASCII "Hello world!"
004033AF   .  E8 5CFBFFFF   call    00402F10

004033B4   .  A1 60564000   mov     eax, dword ptr [405660]
004033B9   .  8A00          mov     al, byte ptr [eax]
004033BB   .  A2 64564000   mov     byte ptr [405664], al

这个更容易理解些
对于这句mov     eax, 00405660, 我理解是先将存储串的基址或叫首地址取到了eax内,然后又取了
临时串Hello world!的基址, 然后调用call 做了填充。也就是复制的作用。
mov     eax, dword ptr [405660] ;  得到 S[1],  由于Char是 8bit的,所以 要 bype
知道了这种形态, S[i]就好办了。
[源]
I := 0;
     S := 'Hello world!';
     

     S[i] := S[1];

[反]
004033EA   .  33C0          xor     eax, eax
004033EC   .  A3 5C564000   mov     dword ptr [40565C], eax

004033F1   .  B8 60564000   mov     eax, 00405660
004033F6   .  BA 48344000   mov     edx, 00403448                    ;  ASCII "Hello world!"
004033FB   .  E8 10FBFFFF   call    00402F10

00403400   .  B8 60564000   mov     eax, 00405660           ;取串首地址
00403405   .  E8 02FCFFFF   call    0040300C                ; 
0040340A   .  8B15 5C564000 mov     edx, dword ptr [40565C] ; 取出 I的值放在edx内

00403410   .  8B0D 60564000 mov     ecx, dword ptr [405660] ; S[1]
00403416   .  8A09          mov     cl, byte ptr [ecx]      ; 将S[1]值临时放在cl内
00403418   .  884C10 FF     mov     byte ptr [eax+edx-1], cl;串首地址+I-1 就是偏移量

这样我们再把','加上。
[源]
I := 0;
S := 'Hello world!';
S[i] := ',';
[反]
004033EA   .  33C0          xor     eax, eax
004033EC   .  A3 5C564000   mov     dword ptr [40565C], eax

004033F1   .  B8 60564000   mov     eax, 00405660
004033F6   .  BA 40344000   mov     edx, 00403440                    ;  ASCII "Hello world!"
004033FB   .  E8 10FBFFFF   call    00402F10

00403400   .  B8 60564000   mov     eax, 00405660
00403405   .  E8 02FCFFFF   call    0040300C

0040340A   .  8B15 5C564000 mov     edx, dword ptr [40565C]
00403410   .  C64410 FF 2C  mov     byte ptr [eax+edx-1], 2C   ; 2C = ','了。没太大的变化。

这种分解的思想的很重要, 叫分而治之,  把复杂的问题, 复杂的情形减小,缩小规模进而逐个
击破,  但是也有很多问题本身就是一系列的, 不过不管是什么问题都是需要一个入口,或者
特别是数学问题, 总是有连续的分析入口的, 如果突然中断了, 只能是我们搜集的背景信息还不够而

已。

这样我们再把  S[i] <> ','作为条件加上。

[源]
I := 0;
     S := 'Hello world!';
     
     while (I <= Length(S)) and (S[i] <> ',') do
     begin
           Writeln('Hello World');
     end;
[反]
004038A5   .  33C0          xor     eax, eax
004038A7   .  8903          mov     dword ptr [ebx], eax

004038A9   .  8BC6          mov     eax, esi
004038AB   .  BA 14394000   mov     edx, 00403914                    ;  ASCII "Hello world!"
004038B0   .  E8 27FBFFFF   call    004033DC

#循环开始
004038B5   .  EB 19         jmp     short 004038D0
#Writeln
004038B7   >  A1 A4404000   mov     eax, dword ptr [4040A4]
004038BC   .  BA 2C394000   mov     edx, 0040392C                    ;  ASCII "Hello World"
004038C1   .  E8 D6FBFFFF   call    0040349C
004038C6   .  E8 11F2FFFF   call    00402ADC
004038CB   .  E8 4CECFFFF   call    0040251C

004038D0   >  8B06          mov     eax, dword ptr [esi]             ;S被取到了esi内
004038D2   .  E8 BDFBFFFF   call    00403494                         ;Length(S)

004038D7   .  3B03          cmp     eax, dword ptr [ebx]             I 就是 I <= Length(S)
004038D9   .  7C 0B         jl      short 004038E6                   ;SF!=OF时跳

004038DB   .  8B06          mov     eax, dword ptr [esi]             ;串
004038DD   .  8B13          mov     edx, dword ptr [ebx]             ;得到I的值
004038DF   .  807C10 FF 2C  cmp     byte ptr [eax+edx-1](S[i]), 2C

004038E4   .^ 75 D1         jnz     short 004038B7                   ;ZF = 0时跳
004038E6   > \89C0          mov     eax, eax



当写了这样的代码时, 在函数头部Delphi编译器又自己插入了代码, 把地址换为了esi
函数头变成了这种形式。
00403884   $  55            push    ebp
00403885   .  8BEC          mov     ebp, esp

00403887   .  53            push    ebx
00403888   .  56            push    esi
00403889   .  BB 5C564000   mov     ebx, 0040565C           ;I的地址也被提前取了。
0040388E   .  BE 60564000   mov     esi, 00405660           ;编译器提前取了串S的基址

00403893   .  33C0          xor     eax, eax
00403895   .  55            push    ebp
00403896   .  68 FD384000   push    004038FD
0040389B   .  64:FF30       push    dword ptr fs:[eax]
0040389E   .  64:8920       mov     dword ptr fs:[eax], esp
所以就出现了
004038DB   .  8B06          mov     eax, dword ptr [esi]  
004038DD   .  8B13          mov     edx, dword ptr [ebx]
的语句, 注意替换。

这样全部
while (I <= Length(S)) and (S[i] <> ',') do
     begin
           Writeln('Hello World');
     end;
的反汇编就可以看了。
只要熟练了,就不再需要这么一点点分解了, 完全在自己脑子里分解就可以了, 有点想做数学题的
意思,省略中间步骤。

看了上面的, 由于编译器会提前的取地址, 那么利用这一特性,我们就可以
构造出类似下面的指令了。
00408992  |.  B8 C48D4000   mov     eax, 00408DC4                    ;  ASCII "0 :"
00408997  |.  E8 94C3FFFF   call    00404D30

#这两个操作数也是在一个 if语句中的条件, 然后也是被提前给取了。或者是做了偏移。
#这有点数组和数组元素的动作, 也就是说来说去我还没有整理关于数组的形式。
0040899C  |.  8B1D 4CD24000 mov     ebx, dword ptr [40D24C]          ;  RUNDL132.0040D164
004089A2  |.  8B5B 18       mov     ebx, dword ptr [ebx+18]
004089A5  |.  85DB          test    ebx, ebx
004089A7  |.  74 12         je      short 004089BB
004089A9  |.  A1 4CD24000   mov     eax, dword ptr [40D24C]
004089AE  |.  E8 DDB9FFFF   call    00404390                         ; [GetACP
004089B3  |.  3BD8          cmp     ebx, eax
004089B5  |.  0F85 D2030000 jnz     00408D8D
004089BB  |>  BA 9CE94000   mov     edx, 0040E99C

好下次再见。