[标题]随便反汇编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
好下次再见。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课