版本:Release
优化选项:O2
调试工具:OD
源码:
//堆排序
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX_SIZE 10
#define SWAP(x,y,t) ((t)=(x),(x)=(y),(y)=(t))
void adjust(int a[],int rootn,int n)//调整堆
{
int temp,parent,child;
temp=a[rootn];
parent=rootn;
child=2*parent;
while(child<=n)
{
if(child<n&&a[child]<a[child+1])
child++;
if(temp>a[child])
break;
a[parent]=a[child];
parent=child;
child*=2;
}
a[parent]=temp;
}
void heapsort(int a[],int n)
{
int i,temp;
for(i=n/2;i>0;i--)
adjust(a,i,n);//自下而上逐层建堆
for(i=n-1;i>0;i--)
{
SWAP(a[1],a[i+1],temp);//取堆顶点最大元素
adjust(a,1,i);//调整堆
}
}
int main()
{
int i;
int a[MAX_SIZE+1];//={0,5,8,4,6,2,15,12,58,12,10};
srand((unsigned)time(0));
for(i=1;i<=MAX_SIZE;i++)
a[i]=rand()%100;
for(i=1;i<=MAX_SIZE;i++)
printf("%4d",a[i]);
putchar('\n');
heapsort(a,MAX_SIZE);
for(i=1;i<=MAX_SIZE;i++)
printf("%4d",a[i]);
system("pause");
return 0;
}
首先把主函数架子给反出来,比较简单
012610A0 >/$ 83EC 2C sub esp, 2C
012610A3 |. 56 push esi
012610A4 |. 57 push edi
012610A5 |. 6A 00 push 0
012610A7 |. FF15 A0202601 call dword ptr [<&MSVCR90._time64>] ; MSVCR90._time64
012610AD |. 50 push eax ; /seed
012610AE |. FF15 A8202601 call dword ptr [<&MSVCR90.srand>] ; \srand
012610B4 |. 8B3D AC202601 mov edi, dword ptr [<&MSVCR90.rand>] ; MSVCR90.rand
012610BA |. 83C4 08 add esp, 8
012610BD |. BE 01000000 mov esi, 1
012610C2 |> FFD7 /call edi
012610C4 |. 99 |cdq
012610C5 |. B9 64000000 |mov ecx, 64 ; 100取余
012610CA |. F7F9 |idiv ecx
012610CC |. 46 |inc esi
012610CD |. 83FE 0A |cmp esi, 0A ; 分配10个元素
012610D0 |. 8954B4 04 |mov dword ptr [esp+esi*4+4], edx ; 第一次到这里其实esi==2,也就是说随机数存储在分配数组中下标1开始
012610D4 |.^ 7E EC \jle short 012610C2
012610D6 |. 8B3D B0202601 mov edi, dword ptr [<&MSVCR90.printf>; MSVCR90.printf
012610DC |. BE 01000000 mov esi, 1
012610E1 |> 8B54B4 08 /mov edx, dword ptr [esp+esi*4+8] ; 这个循环是输出随机取得的数
012610E5 |. 52 |push edx
012610E6 |. 68 04212601 |push 01262104 ; ASCII "%4d"
012610EB |. FFD7 |call edi
012610ED |. 46 |inc esi
012610EE |. 83C4 08 |add esp, 8
012610F1 |. 83FE 0A |cmp esi, 0A ; 依旧是循环10次
012610F4 |.^ 7E EB \jle short 012610E1
012610F6 |. 6A 0A push 0A ; /c = 0A (Line Feed)
012610F8 |. FF15 A4202601 call dword ptr [<&MSVCR90.putchar>] ; \putchar
012610FE |. 83C4 04 add esp, 4
01261101 |. 8D4424 08 lea eax, dword ptr [esp+8] ; 取分配数组的地址
01261105 |. E8 F6FEFFFF call heapsort ; 排序函数是我们要主要分析的
0126110A |. BE 01000000 mov esi, 1
0126110F |. 90 nop
01261110 |> 8B44B4 08 /mov eax, dword ptr [esp+esi*4+8] ; 看过前面几个循环应该不会陌生了吧,一个模式,输出排序后的数
01261114 |. 50 |push eax
01261115 |. 68 04212601 |push 01262104 ; ASCII "%4d"
0126111A |. FFD7 |call edi
0126111C |. 46 |inc esi
0126111D |. 83C4 08 |add esp, 8
01261120 |. 83FE 0A |cmp esi, 0A
01261123 |.^ 7E EB \jle short 01261110
01261125 |. 68 08212601 push 01262108 ; /command = "pause"
0126112A |. FF15 B4202601 call dword ptr [<&MSVCR90.system>] ; \system
01261130 |. 83C4 04 add esp, 4
01261133 |. 5F pop edi
01261134 |. 33C0 xor eax, eax
01261136 |. 5E pop esi
01261137 |. 83C4 2C add esp, 2C
0126113A \. C3 retn
嗯。下面分析我们的排序函数,这才是重头戏嘛
先大概浏览了下主要的跳转,发现函数调用直接插进来优化掉了没有了函数调用,本来还想从子函数入手的,既然如此就直接上吧,虽然会有点复杂
01261000 >/$ 53 push ebx
01261001 |. 55 push ebp
01261002 |. 56 push esi
01261003 |. 57 push edi
01261004 |. BF 05000000 mov edi, 5 ; 姑且认为edi是数组下标吧,令edi=n
01261009 |. 8DA424 000000>lea esp, dword ptr [esp]
01261010 |> 83FF 05 /cmp edi, 5
01261013 |. 8B1CB8 |mov ebx, dword ptr [eax+edi*4]
01261016 |. 8BF7 |mov esi, edi
01261018 |. 8D0C3F |lea ecx, dword ptr [edi+edi] ; ecx=2n
0126101B |. 7F 22 |jg short 0126103F ; 粗略来看下面这些跳转是为了找出a[n],a[2n],a[2n+1]中较大的数
0126101D |. 83F9 0A |cmp ecx, 0A ; if (2n<10)
01261020 |> 7D 0A |/jge short 0126102C
01261022 |. 8B1488 ||mov edx, dword ptr [eax+ecx*4]
01261025 |. 3B5488 04 ||cmp edx, dword ptr [eax+ecx*4+4] ; if(a[2n]<a[2n+1])
01261029 |. 7D 01 ||jge short 0126102C
0126102B |. 41 ||inc ecx
0126102C |> 8B1488 ||mov edx, dword ptr [eax+ecx*4]
0126102F |. 3BDA ||cmp ebx, edx ; if (a[n]<=a[2n+1])
01261031 |. 7F 0C ||jg short 0126103F
01261033 |. 8914B0 ||mov dword ptr [eax+esi*4], edx ; a[n]=a[较大那个]
01261036 |. 8BF1 ||mov esi, ecx ; esi=ecx,这里ecx是最大数的下标
01261038 |. 03C9 ||add ecx, ecx ; ecx=2*ecx,继续向下搜索调整
0126103A |. 83F9 0A ||cmp ecx, 0A
0126103D |.^ 7E E1 |\jle short 01261020
0126103F |> 4F |dec edi ; n--
01261040 |. 891CB0 |mov dword ptr [eax+esi*4], ebx ; 到这里,在看下上面涉及到了ebx的代码,观察下就可以发现a[n]==ebx,而这些代码的目的应该是找到最大数的位置,然后将a[n]替换进去
01261043 |. 85FF |test edi, edi ; n>0则继续循环
01261045 |.^ 7F C9 \jg short 01261010
01261047 |. BA 09000000 mov edx, 9
0126104C |. 8D68 28 lea ebp, dword ptr [eax+28] ; a[10]
0126104F |. 90 nop
01261050 |> 8B75 00 /mov esi, dword ptr [ebp]
01261053 |. 8B48 04 |mov ecx, dword ptr [eax+4] ; SWAP a[1],a[10]
01261056 |. 8970 04 |mov dword ptr [eax+4], esi ; a[10]
01261059 |. 894D 00 |mov dword ptr [ebp], ecx
0126105C |. 8B58 04 |mov ebx, dword ptr [eax+4]
0126105F |. B9 02000000 |mov ecx, 2
01261064 |. 3BCA |cmp ecx, edx ; 我们假设edx就是n吧,这样的话if(2<n)
01261066 |. BE 01000000 |mov esi, 1
0126106B |. 7F 1E |jg short 0126108B
0126106D |> 7D 0A |/jge short 01261079 ; 下面这段循环和上面一个套路,只是优化后略有不同(明明是一个函数,妹的)
0126106F |. 8B3C88 ||mov edi, dword ptr [eax+ecx*4]
01261072 |. 3B7C88 04 ||cmp edi, dword ptr [eax+ecx*4+4]
01261076 |. 7D 01 ||jge short 01261079
01261078 |. 41 ||inc ecx
01261079 |> 8B3C88 ||mov edi, dword ptr [eax+ecx*4]
0126107C |. 3BDF ||cmp ebx, edi
0126107E |. 7F 0B ||jg short 0126108B
01261080 |. 893CB0 ||mov dword ptr [eax+esi*4], edi
01261083 |. 8BF1 ||mov esi, ecx
01261085 |. 03C9 ||add ecx, ecx
01261087 |. 3BCA ||cmp ecx, edx
01261089 |.^ 7E E2 |\jle short 0126106D
0126108B |> 4A |dec edx ; n--
0126108C |. 83ED 04 |sub ebp, 4
0126108F |. 891CB0 |mov dword ptr [eax+esi*4], ebx
01261092 |. 85D2 |test edx, edx
01261094 |.^ 7F BA \jg short 01261050
01261096 |. 5F pop edi
01261097 |. 5E pop esi
01261098 |. 5D pop ebp
01261099 |. 5B pop ebx
0126109A \. C3 retn
总结:
逆向的时候在进入一个很多跳转的指令中时,最好大概看下循环中一些常用到的寄存器,然后可以将寄存器记在纸上,而不是向无头苍蝇一样直接冲进指令堆中,结果常常是找不着北,这样应该会有助于分析流程控制条件和实现的功能等
虽然一条条指令分析并记在纸上简单又准确我也常这样做,不过我想如果在遇到一些大,复杂的跳转循环之类的指令应该会很麻烦吧,我是这么认为。。总之有一定目的性的分析应该会更好些