注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

风雨夜归人

专业收集资料,个人爱好!

 
 
 

日志

 
 

逆向_第七课  

2010-03-20 08:52:16|  分类: 逆向 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
在以C为代表的高级语言中用if-then-else,switch-case等高级语句来构成程序的判断流程,

不仅条理清晰且维护性还是不错的.而汇编语言的代码则复杂得多,会看到cmp等指令的后面跟着

各类的跳转指令jz jnz等.识别关键跳转是软件破解的一个重要技能,许多软件用一个或多个跳转

来实现注册或非注册.
    下面先说说if-then-else语句
    将语句if-then-else语句编译代码后,整数用cmp指令比较,而浮点值则是使用fcom fcomp比

较.语句if-then-else编译后,其汇编代码形式如下:
cmp    a,b    ;a和b只是代表
jz(jnz)    xxxx    ;xxxx是一个地址
    cmp指令不修改操作符,根据两个操作数的相减,影响处理的几个标志,如零标志 进位标志 符

号标志和溢出标志,jz等指令就是条件跳转指令,根据a,b的值大小决定跳转方向.
    实际上,许多情况下编译器都用test或or之类的较短的逻辑指令来替换cmp指令.一般形式

是"test eax,eax",如eax如果为0,则逻辑与运算结果为零,否则设为0.
来看一个C反汇编的实例.
#include <stdio.h>
int main()
{
    int a,b=5;
    scanf("%d",&a);
    if (a==0)
     a=8;
    return a+b;
}
看反汇编的代码:
push    ecx            ;为局部变量分配内存相当于sub esp,4
lea    eax,dword ptr ss:[esp]    ;eax指向局部变量空间
push    eax            
push    00407030        ;指向字符串"%d"
call    00401030        ;c语言的scanf函数
mov    eax,dword ptr [esp+8]    ;将输入的字符传出
add    esp,8            ;由于是cdecl调用,所以在函数外调用
test    eax,eax            ;若eax为0,则ZF置1,否则ZF置0
jnz    00401020        ;若ZF=1不跳转,否则跳转
mov    eax,8
add    eax,8            ;return a+b
pop    ecx            ;释放局部变量用到的内存,相当于add esp,4
retn
    好,看完if-then-else了,下面砍switch-case语句.
    SWITCH语句是多分支选择语句.SWITCH语句编译后,实质就是多个IF-THEN语句嵌套组合.编译

器会将SWITCH编译成一组不同关系运算组成的语句.具体点,来看一个例子.
#include <stdio.h>
int main(void)
{
int a
scanf("%d",&a);
switch(a)
{
    case1:printf("a=1");
    break;
    case2:printf("a=2");
     break;
    case3:printf("a=10");
    break;
    default:printf("a=default");
    break;
}
    return 0;
}
    把它编译,然后反汇编看看汇编代码:
push    ebp
mov    ebp,esp
sub    esp,8        ;为局部变量分配空间
lea    eax,[ebp-04]    
push    eax        ;指向字符("%d")
call    004010A2    ;scanf("%d",&a)
add    esp,8        ;
mov    ecx,[ebp-04]    ;输入的结果给ecx
mov    [ebp-08],ecx
cmp    [ebp-08],1    ;如果是1
je    00401031
cmp    [ebp-08],2    ;如果是2
je    00401040    
cmp    [ebp-08],0a    ;如果是10
je    0040104F
jmp    0040105E
push    00408034
call    00401071    ;printf("a=1")
add    esp,04
jmp    0040106B
push    00408038    
call    00401071    ;printf("a=2")
add    esp,04
jmp    0040106B
push    0040803C
call    00401071    ;printf ("a=10")
jmp    0040106B
push    00408044
call    00401071    ;printf("a=default")
add    esp,04
xor    eax,eax
mov    esp,ebp
pop    ebp
ret
上面的是未优化的反汇编,再看看优化过后的:
push    ecx        ;为局部变量分配内存,相当于sub    esp,4
lea    eax,[esp]
push    eax
push    0040804c
call    004010A1    ;scanf("%d",&a)
mov    eax,[esp+08]    ;scanf输入的结果传给eax
add    esp,08    
dec    eax        ;检测eax是否为1,如果是下面的那句就跳转
je    00401055    ;相当于case    1
dec    eax        ;eax再减一,即eax的值是2
je    00401044    ;相当于ease    2
sub    eax,8        ;eax两次减1后的值为8,所以值为18
je    00401033
    编译器优化后用"dec eax"指令代替cmp指令,这样指令更短,并且执行速度更快,并且优化后,编译器会合理排列switch后各case节点,有最优化方式找到所需要的节点.
     如果case取值表示一个算数级数,那么编译器会利用一个跳转表来实现例如"
switch(a)
{
    case 1:printf("a=1");break;
    case 2:printf("a=2");break;
    case 3:printf("a=3");break;
    case 4:printf("a=3");break;
    case 5:printf("a=3");break;
    case 6:printf("a=3");break;
    case 7:printf("a=3");break;
    default :printf("a=default"):break;
}
    编译器编译后,"jmp dword ptr [4*eax+004010B0]"指令就相当于switch(a),其根据eax的值进行索引,计算出指向相应case处理代码的指针.汇编代码如下:
    lea    eax,dword ptr [ecx-0]
    cmp    eax,6            ;判断是否为default节点
    ja    0040109D
    jmp    dword ptr [4*eax+004010B0]    ;跳转表
    push    00408054        ;case1:printf("a=1")
    call    004010D0
    add    esp,04
    xor    eax,eax
    pop    ecx
    ret
下面的我就不写了,反正大概就是这样.
  评论这张
 
阅读(321)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017