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

     最近一直研究一個對個人而言很有價值的一個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語句了。

     好了,這裏就是一點小小的心得,當是長個記性。高手大牛略過哈! - -  準備一會兒睡覺了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章