能力值:
( LV2,RANK:10 )
2 楼
简单反汇编阅读(二)
上一个动画我们已经知道了函数反汇编后最基本的调用过程,今天我们来看看复杂一点的函数
由于水平有限,而且动画定位于初学者,所以动画会显得有点唠叨,请各位多多包涵 ------------------------------------------------------------------------------
看下面程序:
int boxer(int a,int b)
{
int c=a+b;
for(int i=0;i<10;i++)
{
c=c+i;
}
return c;
}
void main()
{
boxer(1,2);
}
变动的只是boxer()里面加了一个for循环,我们就从for循环那里看就行了
------------------------------------------------------------------------------
4: for(int i=0;i<10;i++)
00401041 mov dword ptr [ebp-8],0 ;上一个动画我们提到[ebp-4]处为第一个局部变量(这里是c)
;那么这里的[ebp-8]就是第二个局部变量i
;对i赋值i=0
00401048 jmp boxer+33h (00401053) ;赋值后跳到00401053
;上面2句的模板是这样的:
;
;mov <循环变量>,<初值>
;jmp 检查循环条件 0040104A mov ecx,dword ptr [ebp-8] ;把i的值给ecx
0040104D add ecx,1 ;ecx加1
00401050 mov dword ptr [ebp-8],ecx ;加1后的ecx给i,这样通过ecx互换实现i++ ;上面3句只是简单的实现i++,然后让下面的比较语句去比较 00401053 cmp dword ptr [ebp-8],0Ah ;来到这里,i的值与0Ah比较,也就是10比较
00401057 jge boxer+44h (00401064) ;i大于或等于0Ah就跳转,就是说小于10时执行循环 ;上面2句的模板是这样的:
;cmp <循环变量>,<限制条件>
;jge 跳出循环
;(循环体) 5: {
6: c=c+i;
00401059 mov edx,dword ptr [ebp-4] ;把第一个局部变量c给edx
0040105C add edx,dword ptr [ebp-8] ;把第二个局部变量i加edx
0040105F mov dword ptr [ebp-4],edx ;把相加的结果给c,就是c=c+i
;上面3句是循环体了,非常的简单,只要弄懂[ebp-N]就行了 7: }
00401062 jmp boxer+2Ah (0040104a) ;跳会修改循环变量继续循环,这里是跳回i++那里
;这句也是模板
;
;jmp 修改循环变量 8: return c;
00401064 mov eax,dword ptr [ebp-4] ;把c的值给eax作为函数返回的结果 ;所有的函数默认都是用eax作返回值。这个定理在爆破的时候非常的有用。 ------------------------------------------------------------------------------
总结一个for循环的反汇编结构如下:
mov <循环变量>,<初值>
jmp 检查循环条件B
A: (修改循环变量)
...
...
B: cmp <循环变量>,<限制条件>
jge 跳出循环
(循环体)
...
...
jmp 修改循环变量A ------------------------------------------------------------------------------
接下来我们用OD看看没有调试信息的release版的情况,这样比较接近实践呢!
00401000 /$ 8B4424 08 mov eax, dword ptr [esp+8] ; 第一个局部参数c
00401004 |. 8B4C24 04 mov ecx, dword ptr [esp+4] ; 第二个局部参数i
00401008 |. 03C1 add eax, ecx ; c=c+i
0040100A |. 33C9 xor ecx, ecx ; ecx清零,就是i为0开始循环
0040100C |> 03C1 /add eax, ecx ; c=c+i
0040100E |. 41 |inc ecx ; i++
0040100F |. 83F9 0A |cmp ecx, 0A
00401012 |.^ 7C F8 \jl short 0040100C
00401014 \. C3 retn 可以看到Release后的代码跟OD的引擎优化后出来的代码简单明了!
但是在很多情况下,发行版的代码都比debug版的难读,这个就要靠我们多练习了! ------------------------------------------------------------------------------
今天提到了for循环,其他的循环体比如do,while循环也是差不多的,看完动画后不妨自己试试
动画下载地址:http://3800hk.com/donghua/g/18973.html
能力值:
( LV2,RANK:10 )
3 楼
简单反汇编阅读(三)
上次我们看了for循环的反汇编,今天我们来看看if等的判断分支的语句
由于水平有限,而且动画定位于初学者,所以动画会显得有点唠叨,请各位多多包涵 ------------------------------------------------------------------------------
看下面程序:
#include "stdio.h"
int boxer()
{
printf("please input a number: ");
int a;
scanf("%d",&a);
if(a>50 && a<100)
{
printf("The number is %d\n",a);
}
else if(a<=50)
{
printf("Too small!\n");
}
else
{
printf("Too large!\n");
}
return 0;
}
void main()
{
boxer();
} 我们像往常一样F10单步调试,然后转到看反汇编,我们把boxer()函数那段copy过来
按照上次我们学的分好各个功能,这样看起来比较清晰 ------------------------------------------------------------------------------
3: int boxer()
4: {
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,44h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-44h]
0040102C mov ecx,11h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi] ;上面的代码大家很熟悉了,就是函数初始化的过程 5: printf("please input a number: ");
00401038 push offset string "please input a number: " (00425058)
0040103D call printf (00401100)
00401042 add esp,4 ;上面3句先字符串入栈,然后调用C语言函数显示字符串,最后用add esp,4恢复栈
;add esp,4是因为前面有1个push,4字节 6: int a;
7: scanf("%d",&a);
00401045 lea eax,[ebp-4] ;将第一个局部变量a的地址给eax寄存器
00401048 push eax ;将a的地址入栈
00401049 push offset string "please input a number: %d\n" (00425054)
0040104E call scanf (00401180) ;调用输入函数等待输入
00401053 add esp,8 ;调用完后恢复栈 ;上面的语句也不难理解,add esp,8是因为前面有2个push,8字节 8: if(a>50 && a<100)
00401056 cmp dword ptr [ebp-4],32h ;第一个局部变量a与32h(50)比较
0040105A jle boxer+55h (00401075) ;大于就不跳
0040105C cmp dword ptr [ebp-4],64h ;与64h(100)比较
00401060 jge boxer+55h (00401075) ;小于就不跳 ;if的模板如下
;
;cmp <各种条件>
;各种跳转跳到下一个分支 9: {
10: printf("The number is %d\n",a);
00401062 mov ecx,dword ptr [ebp-4]
00401065 push ecx
00401066 push offset string "The number is %d\n" (0042503c)
0040106B call printf (00401100)
00401070 add esp,8 ;打印语句,没什么难度 11: }
12: else if(a<=50)
00401073 jmp boxer+77h (00401097) ;如果之前执行了if语句后,来到这里直接跳出判断体
00401075 cmp dword ptr [ebp-4],32h ;如果之前没执行if语句,就跳到这里比较
00401079 jg boxer+6Ah (0040108a) ;大于32h就跳走,直接执行最后的else分支 ;else if模板
;
;jmp 跳出判断
;cmp <条件>
;各种跳转跳到下一个分支 13: {
14: printf("Too small!\n");
0040107B push offset string "Too small!\n" (0042502c)
00401080 call printf (00401100)
00401085 add esp,4 ;说过了。。。 15: }
16: else
00401088 jmp boxer+77h (00401097) ;如果能执行上面的语句,就直接一个硬跳转跳出判断 17: {
18: printf("Too large!\n");
0040108A push offset string "Too large!\n" (0042501c)
0040108F call printf (00401100)
00401094 add esp,4
19: }
20: return 0;
00401097 xor eax,eax ;清空寄存器
21: }
00401099 pop edi
0040109A pop esi
0040109B pop ebx
0040109C add esp,44h
0040109F cmp ebp,esp
004010A1 call __chkesp (004011e0)
004010A6 mov esp,ebp
004010A8 pop ebp
004010A9 ret 看完了if-else语句,我们看看其他判断语句
------------------------------------------------------------------------------
再看下面代码:
#include "stdio.h"
int boxer()
{
printf("please input a number: ");
int a;
scanf("%d",&a);
switch(a)
{
case 0:
{
printf("c=0");
}
case 1:
{
printf("c=1");
break;
}
case 100:
{
printf("a=100");
}
default:
{
printf("default");
}
}
return 0;
}
void main()
{
boxer();
} 反汇编看看。。。由于只是改了判断语句,所以我们只看改的部分
------------------------------------------------------------------------------
8: switch(a)
9: {
00401056 mov ecx,dword ptr [ebp-4] ;第一个局部变量a放进ecx
00401059 mov dword ptr [ebp-8],ecx ;把a的值放在ebp-8里面,下面都是用ebp-8进行比较
0040105C cmp dword ptr [ebp-8],0 ;与0比较
00401060 je boxer+50h (00401070) ;相等就跳case 0那里
00401062 cmp dword ptr [ebp-8],1 ;与1比较
00401066 je boxer+5Dh (0040107d) ;相等就跳case 1那里
00401068 cmp dword ptr [ebp-8],64h ;与100比较
0040106C je boxer+6Ch (0040108c) ;相等就跳case 100那里
0040106E jmp boxer+79h (00401099) ;上面都不符合就跳到default那里 ;这里唯一疑惑的是为什么会把原本放在ebp-4的第一个局部变量a又多此一举放在ebp-8比较
;这个是debug版本的特点,估计是为了防止直接操作堆栈造成破坏
;switch()的模板如下:
;
;cmp <条件1>
;je <case 分支1>
;cmp <条件2>
;je <case 分支2>
;...
;...
;jmp <default分支>
;(下面就是一堆的执行代码,如果期间出现jmp就是对应的break语句了) 10: case 0:
11: {
12: printf("c=0");
00401070 push offset string "c>0" (00425054)
00401075 call printf (00401100)
0040107A add esp,4
13: }
14: case 1:
15: {
16: printf("c=1");
0040107D push offset string "c=1" (0042503c)
00401082 call printf (00401100)
00401087 add esp,4 ;跳到对应分支就直接执行里面的语句,不在做任何判断 17: break;
0040108A jmp boxer+86h (004010a6)
;唯一注意的是break对应的是一个jmp硬跳转 18: }
19: case 100:
20: {
21: printf("a=100");
0040108C push offset string "case 100" (0042502c)
00401091 call printf (00401100)
00401096 add esp,4
22: }
23:
24: default:
25: {
26: printf("default");
00401099 push offset string "Too large!\n" (0042501c)
0040109E call printf (00401100)
004010A3 add esp,4
27: }
28: } 由于时间关系,release版的OD代码就留给大家自己练习了! ------------------------------------------------------------------------------
判断分支语句大致就到这里了,我们不单是学习怎么看汇编,最重要的是学习方法
运用我们学习反汇编的方法,不断去学习其它C/C++语句或者是其它编程语言的反汇编
下次我们就会开始尝试只知道反汇编语句写出大概的原代码(C/C++)
动画下载地址:http://3800hk.com/donghua/g/18987.html
能力值:
( LV2,RANK:10 )
4 楼
简单反汇编阅读(四) 上次我们看过了if等的判断分支的语句,今天开始进入由反汇编写出反C代码
由于水平有限,而且动画定位于初学者,所以动画会显得有点唠叨,请各位多多包涵 ------------------------------------------------------------------------------
看下面debug版本的反汇编代码,我已经删掉源代码了,所以只能靠我们自己写出来
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,50h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-50h]
0040102C mov ecx,14h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi] 00401038 mov dword ptr [ebp-4],64h
0040103F jmp boxer+2Ah (0040104a)
00401041 mov eax,dword ptr [ebp-4]
00401044 add eax,1
00401047 mov dword ptr [ebp-4],eax
0040104A cmp dword ptr [ebp-4],3E8h
00401051 jge boxer+0A5h (004010c5)
00401053 mov eax,dword ptr [ebp-4]
00401056 cdq
00401057 mov ecx,64h
0040105C idiv eax,ecx
0040105E mov dword ptr [i],eax
00401061 mov eax,dword ptr [ebp-4]
00401064 cdq
00401065 mov ecx,0Ah
0040106A idiv eax,ecx
0040106C mov edx,dword ptr [i]
0040106F imul edx,edx,0Ah
00401072 sub eax,edx
00401074 mov dword ptr [j],eax
00401077 mov eax,dword ptr [ebp-4]
0040107A cdq
0040107B mov ecx,0Ah
00401080 idiv eax,ecx
00401082 mov dword ptr [k],edx
00401085 mov edx,dword ptr [i]
00401088 imul edx,dword ptr [i]
0040108C imul edx,dword ptr [i]
00401090 mov eax,dword ptr [j]
00401093 imul eax,dword ptr [j]
00401097 imul eax,dword ptr [j]
0040109B add edx,eax
0040109D mov ecx,dword ptr [k]
004010A0 imul ecx,dword ptr [k]
004010A4 imul ecx,dword ptr [k]
004010A8 add edx,ecx
004010AA cmp dword ptr [ebp-4],edx
004010AD jne boxer+0A0h (004010c0)
004010AF mov edx,dword ptr [ebp-4]
004010B2 push edx
004010B3 push offset string "%4d" (00422020)
004010B8 call printf (00401160)
004010BD add esp,8
004010C0 jmp boxer+21h (00401041)
004010C5 push offset string "\n" (0042201c)
004010CA call printf (00401160)
004010CF add esp,4
004010D2 xor eax,eax 004010D4 pop edi
004010D5 pop esi
004010D6 pop ebx
004010D7 add esp,50h
004010DA cmp ebp,esp
004010DC call __chkesp (004011e0)
004010E1 mov esp,ebp
004010E3 pop ebp
004010E4 ret ------------------------------------------------------------------------------ 00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,50h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-50h]
0040102C mov ecx,14h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
;函数初始化以及分配栈的代码
00401038 mov dword ptr [ebp-4],64h ;将100给第一个局部变量,我们假设它是n ,int n=100;
0040103F jmp boxer+2Ah (0040104a) ;之后一个只直接跳转到0040104a 00401041 mov eax,dword ptr [ebp-4] ;n放到eax
00401044 add eax,1 ;eax+1
00401047 mov dword ptr [ebp-4],eax ;就是n++ 0040104A cmp dword ptr [ebp-4],3E8h ;n与3E8比较,我们看看10进制的数是什么,n<1000;
00401051 jge boxer+0A5h (004010c5) ;小于就不跳,继续 00401053 mov eax,dword ptr [ebp-4] ;n放进eax
00401056 cdq ;CDQ 双字扩展. (把EAX中的值与符号扩展为EDX:EAX)
00401057 mov ecx,64h ;100放进ecx
0040105C idiv eax,ecx ;整数除法,eax除以ecx,就是n/100,商保存在eax,余数放在edx
0040105E mov dword ptr [i],eax ;把结果放在变量i中,i=n/100 00401061 mov eax,dword ptr [ebp-4] ;再把n放进eax
00401064 cdq
00401065 mov ecx,0Ah ;把0Ah(10)放进ecx
0040106A idiv eax,ecx ;n/10,商保存在eax,余数放在edx
0040106C mov edx,dword ptr [i] ;将之前n/100的结果放到edx中
0040106F imul edx,edx,0Ah ;edx乘10结果放在edx中
00401072 sub eax,edx ;n/10-i*10
00401074 mov dword ptr [j],eax ;j=n/10-i*10; 00401077 mov eax,dword ptr [ebp-4] ;n放进eax
0040107A cdq
0040107B mov ecx,0Ah ;10放进ecx
00401080 idiv eax,ecx ;n/10,商放在eax(其实是ax),余数在edx(其实是dx)
00401082 mov dword ptr [k],edx ;将n/10余数放进变量k,就是k=n%10; 00401085 mov edx,dword ptr [i] ;这三句是i*i*i
00401088 imul edx,dword ptr [i]
0040108C imul edx,dword ptr [i]
00401090 mov eax,dword ptr [j] ;这三句是j*j*j
00401093 imul eax,dword ptr [j]
00401097 imul eax,dword ptr [j]
0040109B add edx,eax ;i*i*i+j*j*j
0040109D mov ecx,dword ptr [k] ;这三句是k*k*k
004010A0 imul ecx,dword ptr [k]
004010A4 imul ecx,dword ptr [k]
004010A8 add edx,ecx ;i*i*i+j*j*j+k*k*k,放在edx 004010AA cmp dword ptr [ebp-4],edx ;将上面的结果和变量n比较
004010AD jne boxer+0A0h (004010c0) ;不等就跳004010c0
;一个cmp加一个跳转,是if语句了 004010AF mov edx,dword ptr [ebp-4] ;n放在edx,然后入栈,调用printf函数
004010B2 push edx
004010B3 push offset string "%4d" (00422020)
004010B8 call printf (00401160)
004010BD add esp,8 004010C0 jmp boxer+21h (00401041) ;一个硬跳转回到开头,上面应该是一个for循环体了
;我们可以复习一下for循环的debug版模板 ;又一个printf语句
004010C5 push offset string "\n" (0042201c)
004010CA call printf (00401160)
004010CF add esp,4
004010D2 xor eax,eax 清空eax,就是返回值是0,return 0; ;上面就是代码的主体了,我们看看怎么写反C代码
004010D4 pop edi
004010D5 pop esi
004010D6 pop ebx
004010D7 add esp,50h
004010DA cmp ebp,esp
004010DC call __chkesp (004011e0)
004010E1 mov esp,ebp
004010E3 pop ebp
004010E4 ret ;函数结束时候恢复寄存器恢复堆栈检查esp等代码 ------------------------------------------------------------------------------
完整分析了上面代码后,我们去写出C代码 int boxer()
{
for (int n=100; n<1000; n++)
{
int i=n/100;
int j=n/10-i*10;
int k=n%10;
if (n==i*i*i+j*j*j+k*k*k)
{
printf("%4d",n);
}
}
printf("\n");
return 0;
} ------------------------------------------------------------------------------
写完代码后,我们补全这个程序然后验证一下,显示结果了看看程序的用途
比如说153=1^3+5^3+3^3,这个是一个水仙花数
水仙花数:一个3位数,其各位数字的立方和等于该数本身
所以这个程序是打印出所有的水仙花数的 ------------------------------------------------------------------------------
今天我们看了从不知道源码的情况下尝试写出反C代码,不过这个程序是最基础的
下次我们不妨来点更难的算法
动画下载地址:http://3800hk.com/donghua/g/19001.html
能力值:
( LV2,RANK:10 )
5 楼
简单反汇编阅读(五) 上次我们尝试了从不知道源码的情况下写出反C代码,今天我们不妨来点更难的算法
而且是OD实战release发行版的代码
由于水平有限,而且动画定位于初学者,所以动画会显得有点唠叨,请各位多多包涵
------------------------------------------------------------------------------ 在实战代码前,我们先看看数组的反汇编代码
C源码如下:
int boxer()
{
int a[2], b[2], c[2];
for (int i=0; i<2; i++)
{
c[i]=a[i]*b[i];
}
return 0;
}
void main()
{
boxer();
}
------------------------------------------------------------------------------
数组部分反汇编代码如下: 3: int a[2], b[2], c[2];
4:
5: for (int i=0; i<2; i++)
00401038 mov dword ptr [ebp-1Ch],0 ;ebp-1Ch为第4个局部变量i,因为前面3个数组变量各占了8字节
;ebp-4 :a[0], ebp-8 :a[1]
;ebp-Ch :b[0], ebp-10h :b[1]
;ebp-14h :c[0], ebp-18h :c[1]
;ebp-1Ch :i
0040103F jmp boxer+2Ah (0040104a) 00401041 mov eax,dword ptr [ebp-1Ch]
00401044 add eax,1
00401047 mov dword ptr [ebp-1Ch],eax
0040104A cmp dword ptr [ebp-1Ch],2
0040104E jge boxer+48h (00401068) 6: {
7: c[i]=a[i]*b[i];
00401050 mov ecx,dword ptr [ebp-1Ch] ;把i值放进ecx,这里假设ecx为a,则这句意思是a[i]
00401053 mov edx,dword ptr [ebp-1Ch] ;把i值放进edx,这里假设edx为b,则这句意思是b[i]
00401056 mov eax,dword ptr [ebp+ecx*4-8] ;ebp+ecx*4-8就是当ecx(i)为0时,这句变成ebp-8,a[1]
;当ecx(i)为1时,这句变成ebp+4-8==ebp-4,a[0]
0040105A imul eax,dword ptr [ebp+edx*4-10h] ;ebp+edx*4-10h就是当edx(i)为0时,这句变成ebp-10h,b[1]
;当edx(i)为1时,这句变成ebp+4-10h==ebp-C,b[0]
0040105F mov ecx,dword ptr [ebp-1Ch] ;再把i放进ecx
00401062 mov dword ptr [ebp+ecx*4-18h],eax ;ebp+ecx*4-18h,ecx为0时为c[1],为1时为c[0]
;就是c[1]=a[1]*b[1],c[0]=a[0]*b[0]
;从这里看出数组在反汇编中是从后面取起的,是不是有点意外呢? 8: }
00401066 jmp boxer+21h (00401041)
9:
10: return 0;
00401068 xor eax,eax
11: } ------------------------------------------------------------------------------
知道了数组的大概反汇编后,我们就可以进入OD反汇编发行版的代码看看
以下的一段来历不明的OD反汇编代码我也不知道源代码的,我们一起把它反C代码出来 00401000 /$ 8>mov eax, dword ptr [esp+8] ; 第二个形参给eax,我们假设是b
00401004 |. 5>push edi ; 保存edi,那么edi就是第一个循环变量了,假设为i
00401005 |. 8>lea edi, dword ptr [eax-1] ; edi指向eax-1,就是说将b-1的值赋给edi
00401008 |. 8>test edi, edi ; 判断edi是否为0
0040100A |. 7>jl short 00401033 ; 小于0就跳出循环,大于等于就执行循环体 i>=0
0040100C |. 5>push ebx
0040100D |. 8>mov ebx, dword ptr [esp+C] ; 第一个形参(假设为a)给ebx
00401011 |. 5>push esi ; 保存esi,那么esi就是第二个循环变量了,假设为j
00401012 |> 8>/test edi, edi ; edi与0比较
00401014 |. 7>|jle short 0040102E ; 小于或等于就跳转,就是大于0就执行循环体
00401016 |. 8>|mov eax, ebx ; 从下面看eax,eax+4可以看出a是数组,这里eax指向a[j]
00401018 |. 8>|mov esi, edi ; b-1的值给esi
0040101A |> 8>|/mov ecx, dword ptr [eax] ; 栈中的一个数放在ecx
0040101C |. 8>||mov edx, dword ptr [eax+4] ; 接着下一个数放在edx
0040101F |. 3>||cmp ecx, edx ; 两数比较
00401021 |. 7>||jle short 00401028 ; 小于或者等于就跳转 a[j]>a[j+1],好明显的if语句
00401023 |. 8>||mov dword ptr [eax], edx ; 第一个数如果比第二个数大,就两数交换
00401025 |. 8>||mov dword ptr [eax+4], ecx ; 小的数放在[eax],大的放在[eax+4]
00401028 |> 8>||add eax, 4 ; eax指向栈的下一个数
0040102B |. 4>||dec esi ; esi循环变量减1
0040102C |.^ 7>|\jnz short 0040101A ; 循环还没有结束就跳回去继续
0040102E |> 4>|dec edi ; edi循环变量减1,就是i--
0040102F |.^ 7>\jns short 00401012 ; 如果edi不为-1时候,继续跳回去,j>-1
00401031 |. 5>pop esi
00401032 |. 5>pop ebx
00401033 |> 5>pop edi
00401034 \. C>retn 由于时间关系我写好了注释,我们现在一句一句看,然后写出反C代码
int boxer(int a[], int b)
{
for (int i=b-1; i>=0; i--)
{
for (int j=b-1; j>=0; j--)
{
if (a[j]>a[j+1])
{
int c=a[j];
a[j]=a[j+1];
a[j+1]=c;
}
}
}
}
冒泡法,两两对比,然后把小的数放在前面,大的数就放在后面
实现了从小到大的一个排序
动画下载地址:http://3800hk.com/donghua/g/19016.html