首页
社区
课程
招聘
[旧帖] [原创]C语言中的i++和++i的逆向分析 0.00雪花
发表于: 2008-3-15 09:25 4370

[旧帖] [原创]C语言中的i++和++i的逆向分析 0.00雪花

2008-3-15 09:25
4370
标 题: 【原创】C语言中的i++和++i的逆向分析
作 者: lonkil
时 间: 2008-03-15
备 注:
可以去以下地址:
http://www.vcfans.com/Article/c-parameter-increase-disassemle.aspx
查看代码着色过的版本。
本人长期潜水,今天是第一次发贴,望大家拍砖。
联 系:
          lonkil_AT_GMail.Com
          www.Vcfans.com

公司同事在讨论一个比较变态的问题,关于前自增和后自增的运行结果。
这种题目经常出现C语言面试试题当中。我也不知道结果,就作了一下逆向,没想到受益不小。
逆向比较简单,高手飘过.....,随便BS一下出这样面试题目的人,至于为什么我后面再作分析。

工具:Vc6.0+IDA
平台:WinXP + SP2

题目:
  
   int i=5,j=5;  
   int p,q;  
   p=(i++)+(i++)+(i++);  
   q=(++j)+(++j)+(++j);  
     
   printf("i=%d,j=%d,p=%d,q=%d\n",i,j,p,q);  


Vc6.0的Debug模式下输出:
i=8,j=8,p=15,q=22

下面是IDA的分析代码:
.text:00401010 _main_0         proc near               ; CODE XREF: _mainj
.text:00401010
.text:00401010 var_50          = byte ptr -50h
.text:00401010 var_10          = dword ptr -10h
.text:00401010 var_C           = dword ptr -0Ch
.text:00401010 var_8           = dword ptr -8
.text:00401010 var_4           = dword ptr -4
.text:00401010
.text:00401010                 push    ebp
.text:00401011                 mov     ebp, esp
.text:00401013                 sub     esp, 50h
.text:00401016                 push    ebx
.text:00401017                 push    esi
.text:00401018                 push    edi
.text:00401019                 lea     edi, [ebp+var_50]
.text:0040101C                 mov     ecx, 14h
.text:00401021                 mov     eax, 0CCCCCCCCh
.text:00401026                 rep stosd
.text:00401028                 mov     [ebp+var_4], 5  ; 将i赋值成5
.text:0040102F                 mov     [ebp+var_8], 5  ; 将j赋值成5
.text:00401036                 mov     eax, [ebp+var_4]
.text:00401039                 add     eax, [ebp+var_4]
.text:0040103C                 add     eax, [ebp+var_4]
.text:0040103F                 mov     [ebp+var_C], eax ; 用i连加三次,将结果保存到P中。
.text:00401042                 mov     ecx, [ebp+var_4]
.text:00401045                 add     ecx, 1
.text:00401048                 mov     [ebp+var_4], ecx
.text:0040104B                 mov     edx, [ebp+var_4]
.text:0040104E                 add     edx, 1
.text:00401051                 mov     [ebp+var_4], edx
.text:00401054                 mov     eax, [ebp+var_4]
.text:00401057                 add     eax, 1
.text:0040105A                 mov     [ebp+var_4], eax ; 对i连加三次1,可以看出(i++)+(i++)+(i++)是将i的三个初始值连加三次,
.text:0040105A                                         ; 保存到结果,然后再作i自增
.text:0040105D                 mov     ecx, [ebp+var_8]
.text:00401060                 add     ecx, 1
.text:00401063                 mov     [ebp+var_8], ecx
.text:00401066                 mov     edx, [ebp+var_8]
.text:00401069                 add     edx, 1
.text:0040106C                 mov     [ebp+var_8], edx ; 对j先加两次1,此时的j=7
.text:0040106F                 mov     eax, [ebp+var_8]
.text:00401072                 add     eax, [ebp+var_8] ; 用eax作暂存,对当前的j值进行两次相加
.text:00401075                 mov     ecx, [ebp+var_8]
.text:00401078                 add     ecx, 1
.text:0040107B                 mov     [ebp+var_8], ecx ; j自增1
.text:0040107E                 add     eax, [ebp+var_8]
.text:00401081                 mov     [ebp+var_10], eax ; 将此时的j与eax相加,并将结果保存到q中
.text:00401084                 mov     edx, [ebp+var_10]
.text:00401087                 push    edx
.text:00401088                 mov     eax, [ebp+var_C]
.text:0040108B                 push    eax
.text:0040108C                 mov     ecx, [ebp+var_8]
.text:0040108F                 push    ecx
.text:00401090                 mov     edx, [ebp+var_4]
.text:00401093                 push    edx             ; 四个参数入栈,调用printf
.text:00401094                 push    offset Format   ; "i=%d,j=%d,p=%d,q=%d\n"
.text:00401099                 call    _printf
.text:0040109E                 add     esp, 14h
.text:004010A1                 xor     eax, eax        ; 调用者清栈
.text:004010A3                 pop     edi
.text:004010A4                 pop     esi
.text:004010A5                 pop     ebx             ; 恢复寄存器值
.text:004010A6                 add     esp, 50h
.text:004010A9                 cmp     ebp, esp
.text:004010AB                 call    __chkesp
.text:004010B0                 mov     esp, ebp
.text:004010B2                 pop     ebp
.text:004010B3                 retn
.text:004010B3 _main_0         endp


每一步我都作了注释,应该比较简单不难理解。下面我将这段ASM代码翻译成C代码:
int main(int argc, char* argv[])
{
    int i=5,j=5;
    int p,q;

    int eax;
    eax = i;
    eax += i;
    eax += i;
    p=eax;
    /*完成P的计算*/

    i += 1;
    i += 1;
    i += 1;
    /*
    完成i的自增
    结束p=(i++)+(i++)+(i++);的运算
    */

    j += 1;
    j += 1;
    
    eax = j;
    eax += j;

    j += 1;/*完成j的计算*/
    eax += j;
    q = eax;
    /*
    结束q=(++j)+(++j)+(++j);的运算
    */
    printf("i=%d,j=%d,p=%d,q=%d\n",i,j,p,q);
    return 0;
}


对照结果,我们分析一下前自增和后自增的过程:
前自增:
1.先用i的初始值相加三次,得到P的值。
2.然后再对i进行三次递增。

后自增:
1.先将j进行两递增
2.再对j进行两次相加,将值保存在EAX中
3.再对j作一次自增
4.再用当前的j值和EAX值相加得出q值

文章开头为什么BS出这样题的人呢?我们将编译模式设成Release模式
运行一下,发现结果和Debug模式下的不一样了。
结果是:i=8,j=8,p=15,q=24
奇怪同样的代码在Debug和Release下怎么这么大的区别呢?

Release下的反汇编的代码:
_main proc near
push    18h
push    0Fh
push    8
push    8               ; 编译器直接将结果给优化。
push    offset aIDJDPDQD ; "i=%d,j=%d,p=%d,q=%d\n"
call    sub_401020
add     esp, 14h
xor     eax, eax
retn
_main endp


呵呵,很简洁吧。原来编译器在编译过程中作了很大的优化,直接将结果给生成好。
而且两种模式下的产生的结果不一样,这正是我为什么要BS出这类题目的人。好好的代码为什么要这样写,产生了很大的歧义。这种写法的代码真的没有实际意义。如果在Debug下产生了相要的结果,等项目发布时,发现了问题,要花多大的代价才能找出这个Bug?
所以代码不能一味的求简洁,要保证结果的正确性才是最重的。

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (2)
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
内容重复贴
http://bbs.pediy.com/showthread.php?p=385306
2008-3-15 09:36
0
雪    币: 244
活跃值: (69)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
不好意思,我没有看到那个贴子。
我自己分析了一下,两个执行过程。两篇贴子还是不一样的。
2008-3-15 09:49
0
游客
登录 | 注册 方可回帖
返回
//