现在的位置: 首页 > 综合 > 正文

C/C++条件(三目)运算符巧妙原理解析

2013年08月02日 ⁄ 综合 ⁄ 共 2129字 ⁄ 字号 评论关闭

     最近一直研究一个对个人而言很有价值的一个LIB库的逆向。在今天下班后突然灵感闪现,这个断断续续逆了接近一周的核心管理类。终于在今天给逆完了。在最后一个函数里,碰到了之前基本没有用过的一条指令。(呵呵,高手见笑了!)当然光看单句的汇编指令,是没有办法看出具体的作用的,而且还很可能会认为原作者本来就是用汇编来实现的!呵呵,先不废话,先贴出反汇编代码一睹为快:

  mov        dword ptr [b],64h   // int b
  xor         eax,eax
  cmp        dword ptr [b],0
  setg        al  
  sub         eax,1
  and         eax,64h
  add         eax,0C8h
  mov         dword ptr [a],eax   // int a

 

     今天的LIB里面的那段迷惑的代码就跟这段代码一致,唯独a、b变量不一样。当然这个不影响结果。一开始可能会对setg这条指令的用途不了解。二是看下面蓝色的三条指令,什么又是减,又是and,又是add一些莫名奇妙的立即数。还真让人迷惑这段代码翻译成C++将怎么写。难道就一句一句的翻译?这样的话恐怕一条汇编就是一句C++。而且到了setg这条指令时还真不知道怎么单独的将其翻译成C++的什么语句。呵呵!这可能也就是逆向所带来的乐趣之一吧(个人观点)!

     好了。不废话,先分析下。首先b是一个变量,首先被赋值成0x64(100)。然后将b与0进行比较,如果有心的朋友会觉得奇怪,这个cmp的下面一条语句怎么不是跳转语句,一般都是比较后,然后根据比较结果进行跳转。否则cmp有什么意义呢?到这里的话误导我们的就是setg这条指令了。要了解它,首先得知道cmp会影响到标志寄存器的标志位。cmp是执行的减法操作,将前面的操作数减去后面的操作数。与sub的区别就是它不将减后的值放到目的操作数中。所以cmp有可能减溢出等,从而影响到了标志位。由此一来我们就算猜测都能知道setg应该与标志位有关系。然后通过资料或者奔腾X86指令集查找表(我使用的平台是INTEL X86 CPU)。就可以知道setg指令乃是大于零为真,setg al 就是如果cmp比较后大于零,al里的值将是0x01。setg的判断表达式就是(ZF=0 and SF=OF),还有个setle( ZF = 1 or SF <> OF ),等等还有几个,这里就不一一说明。有兴趣的朋友可以去查阅。

     之后蓝色的三条指令,仔细分析会发现,如果eax为0的话,sub eax, 1后eax将是0xffffffff。之后再and eax, 64h结果eax为64h,之后再是加C8h。之后就给a变量了。然后再分析另外一种情况,假如eax为1, sub eax, 1后将是0,再and等于没运算,之后才是加上C8h,后面就一样了。前面的xor eax, eax就不说了,就是把eax清零。这样一分析,可以有个大概的C++语句的锥形。那就是三目运算符:(?:)。

    好了,这句C++语句很短,那就是:

 

int b = 100;

int a = ( b <= 0 ) ? 300 : 200;

 

     哈哈,很简单吧!其实逆向就是这样,分析一大段很可能翻译过来的C++代码就一句而已。这篇文章说是巧妙原理解析。这里的巧妙之处就在于编译器很聪明(MS很厉害),这些细节的技巧可以给我们很多的启发。这个三目运算在汇编层面上,首先编译器会把后面冒号两边的数字求差,差将用于and运算。b <= 100后eax要么为1要么为0。因此后面的sub eax, 1后eax要么为0xffffffff,要么为0,为0xffffffff表示b小于等于0。b小于等于0之后的and eax, 64h也就将300 - 200的差100赋值给了eax,之后再add C8h(200),便得到了300。反之,sub eax, 1后。 eax将为0。and运算后等于就没有算上差值,之后加上冒号后面的数。就是小的200了。呵呵!这些细节,MS的程序很细心啊!

     还有就是一点,这里记录的差值是有符号的,而且是固定的冒号前面的数减去后面的数。如果前面的数小于后面的数,那么这里记录的将是一个负值,原理一样。

     再举一个例子吧:

      mov         dword ptr [b],1 
      xor         eax,eax 
      cmp         dword ptr [b],0
      setg        al  
      lea         eax,[eax+eax-1]
      mov         dword ptr [a],eax 

     这个例子就不用说了,直接贴C++代码:

     int b = 1;
     int a = ( b > 0 )? 1 : -1; 

     或者是BOOL类型的。注意看红色指令的巧用,它直接代替了and和add指令。大家慢慢体会!   

 

     如果后面冒号两边任意一边是变量的话,就不会被编译成这样子了,就会被编译成普通的跳转类似于if语句了。

     好了,这里就是一点小小的心得,当是长个记性。高手大牛略过哈! - -  准备一会儿睡觉了!

抱歉!评论已关闭.