1. 程式人生 > >逆向知識第六講,取摸優化的幾種方式

逆向知識第六講,取摸優化的幾種方式

ebp 有符號 基本 什麽是 說了 xxx 個數 color 常量

        逆向知識第六講,取摸優化的幾種方式

除法講完之後,直接開始講 % 運算符在匯編中表現形式

首先C的高級代碼貼上來.

高級代碼:

// Tedy.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

int main(int argc, char* argv[])
{
    unsigned Number;
    scanf("%d",&Number); //防止常量傳播
    printf("%ud \r\n",Number % 8);//無符號/2的冪
    __asm 
    {
        nop;
        nop;            ;內聯匯編,觀看的時候知道怎麽看
        nop;
    }

    printf(
"%d",Number % 3);//無符號/非二的冪 __asm { nop; nop; nop; } printf("%d" , 8 % Number);//常量/變量 __asm { nop; nop; nop; } printf("%d",argc % 8);//有符號/2的冪 __asm { nop; nop; nop; } printf("%d",argc %-8
);//有符號/2的冪,除數為負數 __asm { nop; nop; nop; } printf("%d",argc %3);//有符號/非2的冪 return 0; }

一丶無符號% 2的冪在匯編中的表現形式

匯編代碼:

技術分享

高級對應語句:

 printf("%ud \r\n",Number % 8);

可以看出,當無符號%2的冪的時候,直接用and計算. 其值是 2^n - 1的值

比如我們的number %8,那麽and的值則是 8-1,而8是2^3次方.

現在說下7這個數, 0111 它正好是3個指數位,所以還原的時候,直接看占的二進制位數,占了幾位就是指數是多少.

需要註意:

  我們一般看%某某個數的時候,我們要知道其結果保存在edx寄存器當中,而eax寄存器保存的是商

比如:

  8 % 6 = 1 ..... 2 那麽其值 1在eax中,其余數2在edx中

二丶無符號/非二的冪

高級代碼:

printf("%d",Number % 3);//無符號/非二的冪

匯編代碼:
技術分享

可以看出,無符號 / 非2的冪的時候,直接使用 DIV 了,同理有符號 / 非2的冪的時候,就會使用IDIV了.

技術分享

這個時候還原 除數則看 給的除數是多少了.

比如這裏 DIV ecx,而ecx給的是3,則除數是3了.

還可以看到,這個地方 push的edx,那麽說明使用的edx,而上面也說了,edx中的值存放的是余數的值.

三丶常量 % 變量

高級代碼:

printf("%d" , 8 % Number);
匯編代碼:

技術分享

可以看出這段匯編代碼, eax給的是常量,直接使用DIV了,用的是EDX,由此判定, 除數是一個變量,而這個變量是無符號類型了,因為上面的 XOR EDX,EDX清空了. 
看到這一段代碼的時候,還原手法:
eax % [ebp + var_4] = xxx ... edx
代入公式得:
8 % 變量 = 商...余數.
如果你知道變量是具體的值,比如現在是3
那麽8 % 3 = 商...余數 即可.

四丶重點: 有符號 % 2的冪

高級代碼:

 printf("%d",argc % 8);//有符號/2的冪
匯編代碼:

技術分享

有符號的處理,比無符號復雜一些.主要判斷一下符號位.

比如上面的高級代碼對應的這些匯編代碼.看不懂沒關系.依次講解.

我們知道有符號%一個數的時候,需要判斷符號位對吧

那麽此時分為三部分去看.

第一部分 : 正數的情況下

技術分享

上面匯編代碼表示, 我用有符號變量 % 一個80000007h,得出的結果如果不是負數(jns)那麽余數就是正數,直接跳走了

首先我說下為什麽是 800...7h

上面也說過了,要保留符號位

那麽8則是符號位,也就是1, 而為什麽最後是7那?,這個則是保留指數位.

800...7h換算成二進制表達形式為:

10000000000000000000000000000111

高位為符號位,低3位為指數位(當然不是固定的,它的指數位是 2^n-1,比如我們的除數為8,那麽指數位則是 2^3 - 1 = 7,而7正好是占3個指數位,比如是16,那麽2^4 - 1 = 15,而15的指數位則是占了4個二進制位)

這一段匯編代碼,是計算正數的

假設我們的argc有符號變量為9

那麽9對應的二進制則為

00000000000000000000000000001001

and (那麽與剛才的二進制去 &(與)一下結果還是正數)

10000000000000000000000000000111

=

00000000000000000000000000000001 那麽其值結果為1,也就是余數為1,而不是負數,此時就可以跳走直接運算了.

第二部分: 負數的情況下

上面說了正數的情況下,你直接and 2^n-1 的值即可.那麽得出的結果還是正數.

那麽現在是負數額情況下.

技術分享

我們看到一個16進制的數字 0FFFFFFF8h,那麽是什麽意思那?

當然對應的二進制表達方式也寫出來.

11111111111111111111111111111000

此時看匯編代碼 ,註意 dec inc這些是特殊情況下需要用到的,暫時不管,現在只看 中間的or指令.

我們試想一下,如果我們余數是負數的情況下,

舉例子:

  -9 % 8 = 1 ... -1

也就是上面判斷為正數的先走一遍.得到的余數二進制為 -1

那麽對應二進制也就是

100000000000000000000000000000001 (現在的EDX的值).

然後現在有or了一下0FFFFFFF8h 這個值,那麽說下這個怎麽得到的.

我們上面說過了,保留了符號位,符號位置為1,還有保留指數位 (2^n - 1)

那麽這個時候, 這個值就是 把中間的值變為1,保留(2^n-1的位數)

11111111111111111111111111111000

高位一個符號位,中間的0變為1,最後三個則是指數位,此時or之後

10000000000000000000000000001

or(或 |)

11111111111111111111111111111000

=

1000000000000000000000000001

那麽則得出結果是 -1

第三部分: 特殊情況下

特殊情況下,則是 一個 dec,然後最後一個inc回來的時候.

技術分享

這個則是當余數為0的情況下才會觸發.

比如 8 % 8 = 0;

走第一部分匯編代碼的時候,edx裏面的值都是0了.

然後-1,繼續or, or出來的結果加1還是0.這個主要是余數為0的情況下.

重點: 還原手法

上面只是說的原理.(其實也不算高深點的原理,這裏是站在匯編代碼的角度下說的,其實真正的都有數學定理和公式)

以後凡是看到這塊匯編代碼:

我們直接看指數位是多少位即可. 比如上面我們%8,那麽指數位是3個,那麽還原的時候就是 r = 2^n次方即可.

n = 指數位

n = 3

如果計算上面的余數則

r = 2^ 3

r = 8即可.

當然我們要看一下最後用的寄存器是不是edx,如果是edx,那麽就是 %,如果是用的eax,那麽結果就是 /

很顯然上面是用的edx,

還原回來的匯編代碼為:

[ebp + argc] % 2^n

有符號局部變量 % 8 即可.

五丶有符號 % -2的冪

高級代碼:

printf("%d",argc %-8);//有符號/2的冪,除數為負數
對應匯編代碼:

技術分享

首先在講解之前,我們要明白一下.

我們舉例子:

8 % 6 = 1 ... 2

8 %-6 = -1.....2

但是我們看一下,我們的余數並沒有改變其結果, 余數都是2

比如我們列一個公式

a(被除數) b(除數) q(商) r (余數)

a % b = q ... r 這個是基本的.

那麽

a % |b| = |q| ... r 摸不摸 b的絕對值,其 r值不變的.影響的只是 q對不起.

但是

|a| % b = |q| ...|r| 那麽這個時候,如果把a變為絕對值,那麽絕對會影響r的值.

上面的匯編代碼.則是寫了一個無分支求絕對值而已.如果數學公式搞懂了,那麽看上面的匯編代碼則會懂了

第一部分,無分支求絕對值

技術分享

這個則是無分支求絕對值的代碼.

首先esi的值是上面 argc局部變量的值,只不過上下文中沒有修改esi,所以在這裏直接使用.這裏就想象成一個變量

然後CDQ, edx的值,跟隨者eax的符號位填充,如果 eax(也就是現在變量的值是正數,那麽eax的高位則是0,那麽edx的值全部都是0)

如果是正數的情況下:

正數的情況下,eax是正數,edx因為符號擴展,所以結果是0, xor之後,其結果還是原值.

此時 原值 eax - edx (相當於, - 0 )那麽其結果還是原值.

然後

技術分享

此時把除數變為正數了,那麽 直接使用and 7即可.(7是 2^n-1的值)

and之後,其eax的值則是余數(這裏不是EDX了,有時候我們要看,這裏是eax去弄得,所以放到裏面了)

and之後,下方繼續幾行匯編代碼,這些匯編代碼都一樣得出的結果還是原來的值.

如果是負數的情況下:

技術分享

匯編代碼就是這麽一大堆.

然後負數的情況下,執行完求絕對值的代碼之後,其結果就變成了正數. 在and eax,7上面弄得.

那麽此時如果原來是負數的情況下,那麽下方繼續再來一遍,變為負數.

那麽此時得出的除數是負數. 也就是 b為負數.(除數)

還原手法:

不管怎麽做,上面先把絕對值求出來,然後和 (2^n-1)去and,此時得出了除數是 (2^n) ,那麽怎麽判斷正數還是負數.

判斷下方是否在取反了即可.

逆向知識第六講,取摸優化的幾種方式