逆向工程之表示式優化識別 3 除法 上

2021-07-14 10:09:01 字數 3085 閱讀 8638

說明:除法相對其他幾種運算的優化又要更難了,主要是其包含的數學知識(離散數學?)

由於我本人比較笨,也不喜歡深究數學,所以盡量跳過數學,以更直觀的方式來解釋(其實也就是我自己的理解方法了)

取整分為向上取整和向下取整以及向0取整,我們在程式語言中的取整都是向0取整,但是有一些運算卻是向下取整的,比如右移運算就是向下取整的,所以在負數的時候,為了保證向0取整,就會做一些調整。

說明:分為被除數大於0和小於0兩種情況討論。

mov eax, val

cdq ;符號擴充套件,eax符號位為1,設edx為-1,否則設edx為0

sub eax, edx

sar eax, 1

這段**是x / 2的**,使用cdq避免了分支。

當x >= 0, edx為0,sub eax, edx沒有作用,直接右移1位

當x < 0,edx為-1, sub eax, edx使得eax = eax + 1(在這裡由於2^1-1 == 1所以沒有後續操作),然後移位

另外乙個示例:

mov eax, val

cdqand edx, 7

add eax, edx

sar eax, 3

這段**是x/8的**。

當x >= 0時,cdq使得edx為0,and和add操作沒有作用,sar移位直接完成操作。

當x < 0時,cdq使得edx為-1,即全f,全f和7按位和的結果是保留7的結果,於是add完成了eax = eax + 2^3 - 1即eax = eax + 7的操作,最後移位完成運算。 由於8是常量,所以這裡的2^n - 1是可以提前算出來的。

說明:

示例1:

;ecx 為某通用暫存器(可替換)

mov ecx, val

mov eax, 38e38e39h

imul ecx ;有符號乘法,用eax乘ecx,edx存放高4位元組,eax存放低4位元組

sar edx, 1

;有符號移位

mov eax, edx

shr eax, 1fh ;無符號移位

add edx, eax

這段**是x/9的**。令magic number m = 38e38e39h

對於x>=0時,eax = eax*m,相當於完成了x * (2^n/y)的操作,我們現在暫時還不知道n具體是多少。imul之後使用的是edx,edx為高4位元組,之後eax棄用,說明用edx記錄結果,相當於使用了右移了32位的值,加之sar的右移1位,所以移位了33位。然後將eax用來記錄該值右移33位之後再右移1f(31)位。之後的右移31位即獲取到了sar之後edx的符號位,用eax存,大於0,為0,所以add不做操作。由此,移動了共33位,即n=33,m = 2^n / y = 2^33 / y = 38e38e39h,可得y約等於9。

對於x<0時,eax將會得到符號位,小於0,eax為符號位,也就是1,所以add使得結果+1。

更難一點的示例2:

;ecx為某通用暫存器(可替換)

mov eax, 24924925h

mul ecx

sub ecx, edx

shr ecx, 1

add ecx, edx

shr ecx, 2

這段**是x/7的**。和上一例的區別在於,magic number的計算得出的結果超出了乙個四位元組整數的範圍,所以對其做出了調整,導致之後的步驟也做出了調整,但總體思路一致。另外還要注意,由於是mul,這裡的計算是無符號計算,可以認為都是正數,沒有負數處理的部分。

首先m = 24924925h,剩下的部分就需要用筆算了,因為涉及到化簡,我在這裡不再贅述,基本上就是把等效的表示式寫出來然後化簡,最後結果為ecx = ecx0 * (2^32+m) /2^35,ecx0為最初的ecx(除數)。根據之前的原理,如圖有:

所以m其實是(2^32 * 2^n/y) n為3,就符合之前得到的運算結果了。

根據n和m即可算出來y = 7了。

示例3:

;ecx 為某通用暫存器(可替換為其他)

mov eax, 92492493h(大於0x7fffffff)

imul ecx

add edx, ecx

sar edx, 2

mov eax, edx

shr eax, 1fh

add edx, eax

這裡也是x/7的**。但是是有符號運算。

乙個細節:對於有符號運算,大於0x8000 0000的數值,會被認為是負數,其值為 val-2^32,val為對應無符號數值。因此對公式做出相應變形:

由此分析**

首先我們應該通過示例1,可以識別

mov eax, edx

shr eax, 1fh

add edx, eax

是在針對負數做調整,所以關注點為前面幾行。

根據公式的變形,magic number現在是(2^n/y-2^32),加之對**的公式進行轉換,可以得到

edx = (ecx*eax+2^32*ecx)/2^34 符合我們的形式,所以即可得到最終為x/7.

對於除數大於0的情況,首先我們得認識到乙個取整的公式,就是將被除數為負的情況的向下取整變成向上取整,然後其餘的情況就簡單了。 分析的方法基本上就是把彙編**對應的表示式寫出來並化簡,然後通過對公式的恒等變形,盡力化成表示式的形式,對於負數,一般是在彙編**中設法獲得符號位然後做乙個加法。

shell程式設計之正式表示式

二 文字處理器 正規表示式又稱正規表示式 常規表示式。在 中常簡寫為 regex regexp 或 re。正規表示式是使用單個字串來描述 匹配一系列符合某個句法規則的字串,簡單來說,是 一種匹配字串的方法,通過一些特殊符號,實現快速查詢 刪除 替換某個特定字串 正規表示式的字串表達方法根據不同的嚴謹...

正規表示式優化

正規表示式的優化 在jeffrey e.f.friedl 的 精通正規表示式 中提到了幾種技巧。今天著重說一種比較實用的。比較簡單的 在類似 或者 s s 中匹配的時候,量詞 預設是貪婪的,啟用最大匹配模式,會匹配到盡量多的字串,如果我們的需求是匹配text中的text,這樣就不適用了。具體來說,我...

正規表示式優化

正規表示式的優化 在jeffrey e.f.friedl 的 精通正規表示式 中提到了幾種技巧。今天著重說一種比較實用的。比較簡單的 在類似 或者 s s 中匹配的時候,量詞 預設是貪婪的,啟用最大匹配模式,會匹配到盡量多的字串,如果我們的需求是匹配text中的text,這樣就不適用了。具體來說,我...