今天本来想做个有浮点,有开平方的一个小程序,但是OD看到fsqrt,fstw,fild,st0这些就崩溃了,而且明明已经开平方了然后又call了个C的sqrt函数,对我这个菜鸟还是有点难度了,先做个简单的吧
程序源码:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
void main()
{
int x,y,z,t;
scanf("%d%d%d",&x,&y,&z);
if(x>y)
{
t=x;
x=y;
y=t;
}
if(x>z)
{
t=z;
z=x;
x=t;
}
if(y>z)
{
t=y;
y=z;
z=t;
}
printf("small to big: %d,%d,%d",x,y,z);
}
废话就不说了,直接来到main函数里面吧。
00401000 /$ 83EC 0C sub esp, 0C ; 3个变量
00401003 |. 8D4424 08 lea eax, dword ptr [esp+8] ; p3=eax
00401007 |. 8D4C24 04 lea ecx, dword ptr [esp+4] ; p2=ecx
0040100B |. 8D5424 00 lea edx, dword ptr [esp] ; p1=edx
0040100F |. 50 push eax
00401010 |. 51 push ecx
00401011 |. 52 push edx
00401012 |. 68 48804000 push 00408048 ; ASCII "%d%d%d"
00401017 |. E8 95000000 call 004010B1 ; scanf三个数 eax直接返回第3个数
0040101C |. 8B5424 10 mov edx, dword ptr [esp+10] ; edx=p1
00401020 |. 8B4C24 14 mov ecx, dword ptr [esp+14] ; ecx=p2(ecx和edx则要自己取)类似的以&变量名为参数的函数应该都是用这样的方法来返回值的
00401024 |. 83C4 10 add esp, 10 ; 上面两条语句取完了数据再恢复4个变量的堆栈
00401027 |. 3BD1 cmp edx, ecx ; x y
00401029 |. 7E 0E jle short 00401039 ; x<y则跳转,即如果大于则要做一下交换了
0040102B |. 8BC2 mov eax, edx ; z=x
0040102D |. 8BD1 mov edx, ecx ; x=y
0040102F |. 8BC8 mov ecx, eax ; y=z(上面三条即:x和y交换 但貌似把z给牺牲掉了)
00401031 |. 895424 00 mov dword ptr [esp], edx
00401035 |. 894C24 04 mov dword ptr [esp+4], ecx
00401039 |> 8B4424 08 mov eax, dword ptr [esp+8] ; z又回来了~
0040103D |. 56 push esi ; ??
0040103E |. 3BD0 cmp edx, eax ; x z
00401040 |. 7E 0E jle short 00401050 ; 小于跳转 大于的话 嘿嘿 做点交换
00401042 |. 8BF0 mov esi, eax ; t=z
00401044 |. 8BC2 mov eax, edx ; z=x
00401046 |. 8BD6 mov edx, esi ; x=t(z和x交换,t为中间变量)
00401048 |. 894424 0C mov dword ptr [esp+C], eax ; 因为push了esi 所以第三个变量变成了esp+8+4=esp+c
0040104C |. 895424 04 mov dword ptr [esp+4], edx ; 同理esp变成esp+4
00401050 |> 3BC8 cmp ecx, eax ; y z
00401052 |. 7E 0E jle short 00401062 ; 老话题。。
00401054 |. 8BF1 mov esi, ecx
00401056 |. 8BC8 mov ecx, eax
00401058 |. 8BC6 mov eax, esi ; 也是中间变量的方式来交换y和z,只是不知道为什么后两次交换用中间变量 第一次却用牺牲z的方法~~
0040105A |. 894C24 08 mov dword ptr [esp+8], ecx
0040105E |. 894424 0C mov dword ptr [esp+C], eax
00401062 |> 50 push eax
00401063 |. 51 push ecx
00401064 |. 52 push edx
00401065 |. 68 30804000 push 00408030 ; ASCII "small to big: %d,%d,%d"
0040106A |. E8 11000000 call 00401080
0040106F |. 83C4 10 add esp, 10
00401072 |. 5E pop esi
00401073 |. 83C4 0C add esp, 0C
00401076 \. C3 retn
00401003 |. 8D4424 08 lea eax, dword ptr [esp+8] ; p3=eax
00401007 |. 8D4C24 04 lea ecx, dword ptr [esp+4] ; p2=ecx
0040100B |. 8D5424 00 lea edx, dword ptr [esp] ; p1=edx
0040100F |. 50 push eax
00401010 |. 51 push ecx
00401011 |. 52 push edx
00401012 |. 68 48804000 push 00408048 ; ASCII "%d%d%d"
00401017 |. E8 95000000 call 004010B1 ; scanf三个数 eax直接返回第3个数
0040101C |. 8B5424 10 mov edx, dword ptr [esp+10] ; edx=p1
00401020 |. 8B4C24 14 mov ecx, dword ptr [esp+14] ; ecx=p2(ecx和edx则要自己取)类似的以&变量名为参数的函数应该都是用这样的方法来返回值的
00401024 |. 83C4 10 add esp, 10 ; 上面两条语句取完了数据再恢复4个变量的堆栈
00401027 |. 3BD1 cmp edx, ecx ; x y
00401029 |. 7E 0E jle short 00401039 ; x<y则跳转,即如果大于则要做一下交换了
0040102B |. 8BC2 mov eax, edx ; z=x
0040102D |. 8BD1 mov edx, ecx ; x=y
0040102F |. 8BC8 mov ecx, eax ; y=z(上面三条即:x和y交换 但貌似把z给牺牲掉了)
00401031 |. 895424 00 mov dword ptr [esp], edx
00401035 |. 894C24 04 mov dword ptr [esp+4], ecx
00401039 |> 8B4424 08 mov eax, dword ptr [esp+8] ; z又回来了~
0040103D |. 56 push esi ; ??
0040103E |. 3BD0 cmp edx, eax ; x z
00401040 |. 7E 0E jle short 00401050 ; 小于跳转 大于的话 嘿嘿 做点交换
00401042 |. 8BF0 mov esi, eax ; t=z
00401044 |. 8BC2 mov eax, edx ; z=x
00401046 |. 8BD6 mov edx, esi ; x=t(z和x交换,t为中间变量)
00401048 |. 894424 0C mov dword ptr [esp+C], eax ; 因为push了esi 所以第三个变量变成了esp+8+4=esp+c
0040104C |. 895424 04 mov dword ptr [esp+4], edx ; 同理esp变成esp+4
00401050 |> 3BC8 cmp ecx, eax ; y z
00401052 |. 7E 0E jle short 00401062 ; 老话题。。
00401054 |. 8BF1 mov esi, ecx
00401056 |. 8BC8 mov ecx, eax
00401058 |. 8BC6 mov eax, esi ; 也是中间变量的方式来交换y和z,只是不知道为什么后两次交换用中间变量 第一次却用牺牲z的方法~~
0040105A |. 894C24 08 mov dword ptr [esp+8], ecx
0040105E |. 894424 0C mov dword ptr [esp+C], eax
00401062 |> 50 push eax
00401063 |. 51 push ecx
00401064 |. 52 push edx
00401065 |. 68 30804000 push 00408030 ; ASCII "small to big: %d,%d,%d"
0040106A |. E8 11000000 call 00401080
0040106F |. 83C4 10 add esp, 10
00401072 |. 5E pop esi
00401073 |. 83C4 0C add esp, 0C
00401076 \. C3 retn
总结下,从上面代码可以看出 形如 函数名(&变量名)这样的函数返回值在反汇编中是在函数外直接从堆栈中取出到一些寄存器或者存储单元里面的,做了这个操作之后才把堆栈恢复平衡。其他的跳转,堆栈操作都比较熟悉了,ok。
顺便逆一个hello world级的程序
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
void main()
{
printf("Hello World \n");
printf("****\n");
printf("*\n");
printf("*\n");
printf("****\n");
}
{
printf("Hello World \n");
printf("****\n");
printf("*\n");
printf("*\n");
printf("****\n");
}
OD:
00401000 /$ 68 3C704000 push 0040703C ; ASCII "Hello World ",LF
00401005 |. E8 36000000 call 00401040
0040100A |. 68 34704000 push 00407034 ; ASCII "****",LF
0040100F |. E8 2C000000 call 00401040
00401014 |. 68 30704000 push 00407030 ; ASCII "*",LF
00401019 |. E8 22000000 call 00401040
0040101E |. 68 30704000 push 00407030 ; ASCII "*",LF
00401023 |. E8 18000000 call 00401040
00401028 |. 68 34704000 push 00407034 ; ASCII "****",LF
0040102D |. E8 0E000000 call 00401040
00401032 |. 83C4 14 add esp, 14
00401035 \. C3 retn
00401005 |. E8 36000000 call 00401040
0040100A |. 68 34704000 push 00407034 ; ASCII "****",LF
0040100F |. E8 2C000000 call 00401040
00401014 |. 68 30704000 push 00407030 ; ASCII "*",LF
00401019 |. E8 22000000 call 00401040
0040101E |. 68 30704000 push 00407030 ; ASCII "*",LF
00401023 |. E8 18000000 call 00401040
00401028 |. 68 34704000 push 00407034 ; ASCII "****",LF
0040102D |. E8 0E000000 call 00401040
00401032 |. 83C4 14 add esp, 14
00401035 \. C3 retn
很失望是这样弱智的结果。。看到LF,应该是换行符吧,在数据窗口跟踪到LF对应0A。
有趣的是把0A填充成00 就是NULL了,C遇到这个NULL便以为已经结束了,所以没换行而最终输出了密密麻麻的星号。。。。。。。