PC逆向之程式碼還原技術,第六講彙編中除法程式碼還原以及原理第一講,除數是2的冪
目錄
一丶除法簡介
除法,在彙編中是 DIV 指令 跟 IDIV指令,跟乘法一樣.指令週期時間長.所以也必須進行優化.
但是除法的優化有很多原理.也就是很複雜. 逆向工作人員.也要搞清楚除法才算是真正的入了逆向的的小門.
除法搞不定.以後程式碼還原.等等.自己根本還原不了.有人說 可以使用IDA靜態分析工具. F5外掛. 我可以告訴你
F5搞不定除法的.會給你還原的亂七八糟.還不如看彙編.所以這也是我們必須搞定的.
二丶簡介除法原理
除法原理是由數學上來決定的.也就是說.優化是按照數學公式來定的.這也早就了.不管你是VC6.0寫的程式
還是VS2017等更高版本寫的程式.都不會有很大變化.原因是.這種優化已經是最優的優化了.除非數學上有很大
變動.(不可能的.如果有就是數學界的一大震動)否則不會改變的.還有一種情況就是.CPU越來越好.優化的時候
使用了新指令了. 新指令進行優化.不過佔很小一部分.因為如果都是新指令優化.那麼這個程式就沒法相容以前系統了
1.搞明白數學中的向上取整 向下取整. 以及程式中的向零取整.
首先畫出一個座標系,如下:
-∞ 0 +∞
<-------------*------------------>
向下取整:
向下取整就是往負無窮方向接近 x的數值. 不大於x的最大整數. 例如x為3.5 那麼往負無窮接近,不大於
3.5的最大整數是多少. 是3.
-3.5向下取整就是-4 向上取整就是-3
在C語言中是 floor函式. 向下取整也稱為地板取整
向上取整:
向上取整就是往正無窮接近 x的數值. 不小於x的最大整數. 例如3.5 向上取整就是4
向上取整在C語言中是ceil()函式.也成為天花板取整
向零取整:
向零取整就很簡單了.可以理解為 正數是向下取整, 負數是向上取整.反正靠近0就可以.
向零取整是計算機整數除法規定的.計算機會使用這種除法.也稱為截斷除法.
疑問?
為什麼要學習取整.雖說取整很簡單.原因是在計算機中.除法都是向零取整的除法.
例如我們上面說過的向下取整. 假設: a為被除數 b為除數 那麼
公式: ⌊-a/b⌋ ≠ -⌊a/b⌋
我們可以帶入計算:
-7 / 2 = -3.5 向下取整 = -4
-⌊7/2⌋ = -3 首先計算出 7 / 2 = 3.5 向下取整則是3 外面有個負號 所以是-3
向上取整問題:
向上取整是一樣的.結果也是不一樣.
公式: ⌈-a/b⌉ ≠ -⌈a/b⌉
一樣代入
⌈-7/2⌉ = -3
-⌈7/2⌉ = -4
向零取整:
計算機中的除法就是整數除法,就是向零取整.
[-a / b] = [a / -b] = -[a / b]
我們可以代入公式:
[-7 / 2] = -3
[7 / -2] = -3
-[7 / 2] = -3
所以三個公式是一樣的.
所以必須要了解取整.
2.除法的擴充套件知識
除法的擴充套件知識:
在整數的除法中,只有能整除和不能整除的兩種情況則會產生餘數.
設 a = 被除數 b = 除數 c = 商 r = 餘數
那麼可以得到下面的公式:
除法原型:
a / b = c .... r
6/ 4 = 1 ...2
-
|r| < |b| : 餘數的絕對值,絕對會小於除數的. 比如 6 / 4 = 1 .... 2 那麼 餘數2 不關是正數還是負數,絕對都是絕對會小於除數的,也就是4
-
a = q * b + r : 求被除數,被除數是商*除數+餘數
-
b = (a - r)/c : 求除數,除數等於 被除數-餘數 / 商
-
q = (a - r)/b : 求商: 被除數 - 餘數 / 除數
- r = a - (q * b) : 求餘數 被除數 - (商 * 除數)
-
三丶除法的程式碼還原.
有了以上的公式支撐.那麼我們則可以進行除法的程式碼還原的學習了.
1.除數為2的一次方
高階程式碼:
int main(int argc, char* argv[]) { int nValue = 10; scanf("%d",&nValue); //防止變數nValue優化成常量. 所以不讓他優化 int nTemp = nValue / 2; //常量是2的一次方重要程式碼 scanf("%d",&nTemp);//防止優化 return 0; }
Debug下的彙編
.text:00401280moveax, [ebp+var_4] .text:00401283cdq .text:00401284subeax, edx重要程式碼 .text:00401286sareax, 1
其實如果是2的1次方,Debug跟Release下.都會產生程式碼定式.
程式碼定式:
mov reg,[ebp - ?];獲得被除數reg存放的是被除數的值 cdq符號擴充套件. 被除數是負數,那麼 擴充套件符號位之後 edx = 0xFFFFFFFF 也就是-1 否則就是0 sub eax,edx調整符號位,被除數是正數,那麼此條語句執行完相當於沒制定. 被除數是負數. 則會形成(被除數 - 1) 因為是負數.所以被除數是補碼形式存在 sar eax,1sar相當於向下取整. sar是有符號右移,右移一位就是 / 2
程式碼定式可以進行總結:
mov eax,[ebp - ?] cdq sub eax,edx sar eax, B
還原方法:
除數的還原 = 2^b次方
被除數的還原: = 被除數就是eax 如果是補碼,則是負數. 且cdq之後 edx肯定是 0xFF... 也就是-1
還原原理:
設a 是被除數
設b 是除數
則有下面公式:
b > 0 則有下面公式

b < 0 則有下面的公式

關於證明我就不說了.具體可以看下 錢林松的 <<C++反彙編與逆向分析技術揭祕>>這本書.
有了以上公式,那麼上面的彙編程式碼則能看明白了.
假設:
7 / 2 = 3..1 這個是數學上的公式.
7 / 2 = 3 這個是計算機中的整數除法.向零取整.
根據以上公式, b > 0. b是除數. 也就是2, 它的大於0的. 所以我們使用第一條公式.
向下取整(a / b) = 向上取整 ( (a - b + 1) / b); 或者使用
向上取整(a / b) = 向下取整 ((a + b -1) / b);
帶入公式可以得出:
向上取整((7 - 2 + 1) / 2) = 3; 結果就是對的.
向下取整((7 + 2 - 1) / 2) = 3; 結果也是對的.
那麼彙編定力我們就能看明白.
mov eax,[ebp - ?] cdq sub eax,edx調整商,被除數是負數.那麼商也是負數. sar eax, n向下取整
代入公式:
向下取整((eax + eax - edx(-1 or 0)) / 2^n)
b > 0,那麼使用第一條公式即可. 被除數是正數. 那麼edx就是0
向下取整((10 + 2^n - 0) / 2^1)
= (10 + 2 - 0) / 2
= 12 / 2
= 6
= 向下取整(6)
= 5 商
所以不懂公式,那麼直接進行最笨的方法,記下來.怎麼還原即可.
總結:
除數為2的一次方
彙編定式:
mov eax,[ebp - ?] cdq sub eax,edx sar eax, n
還原方法: a + b - 1 (-1 or 0)
向下取整(eax + 2^n - edx) / 2^n) 根據公式還原.
不使用公式:
除數: 2^n
被除數: eax
商:
被除數為正:
eax / 2^n
被除數為負數:
(eax -1) / 2^n次方.
2.除數為2的冪
高階程式碼:
int main(int argc, char* argv[]) { int nValue = 16; scanf("%d",&nValue);// 防止優化.核心程式碼不是這個 int nTemp = nValue / 8;//核心程式碼 一會觀看反彙編 scanf("%d",&nTemp); //防止優化 return 0; }
Debug下的彙編跟Release彙編一樣,Release有流水線優化程式碼.去掉則會跟Debug下彙編一樣.
產生以下彙編程式碼
.text:00401040moveax, [ebp+var_4]獲得被除數 .text:00401043cdq符號擴充套件 eax,edx 被除數為正, edx = 0, 否則 edx = -1 .text:00401044andedx, 7位與運算.被除數為正數,此條指令沒用,因為edx = 0. 0 & 7還是0 被除數為負數 edx結果為7 .text:00401047addeax, edx(eax + edx)/2^n edx = 0 則被除數是0edx = -1 則被除數是負數. 且公式會有改變(eax + 7) / 2^n .text:00401049sareax, 3
我們觀看這個程式碼可以產生程式碼定式:
.text:00401040moveax, [ebp+var_4] .text:00401043cdq .text:00401044andedx, b .text:00401047addeax, edx .text:00401049sareax, n
除數的還原: (2^n - 1) == b 那麼被除數就是2^n次方
主要是還原除數.
被除數就是eax,判斷正負看是否是補碼就可以.
四丶除法第一講總結
今天主要就是講了兩個除法的還原.除法很多.但是鑑於篇幅.一個部落格只更新兩個.便於以後複習,也便於
自己的理解.
1.除數是2的一次方
mov eax,[ebp - ?] cdq sub eax,edx sar eax, n
除數進行還原: 2^n
被除數: eax eax是補碼,則商為負,則 sub eax,edx會執行. 被除數為負數 edx = -1 正數為0
sub eax,edx也是判斷被除數是否為正負數.而進行的無分支優化.
除法原理:
b > 0 也就是除數大於0
使用公式:

如果代入公式則是: 向下取整((eax + 2^n - edx) / 2^n) 或者使用 向上取整((a - 2^n + edx) / 2^n);
b < 0 則有下面的公式

2.除數為2的冪總結:
程式碼定式:
.text:00401040moveax, [ebp+var_4] .text:00401043cdq .text:00401044andedx, b .text:00401047addeax, edx .text:00401049sareax, n
除數的還原: 如果: (2^n - 1) == b 那麼被除數就是2^n次方
主要是還原除數.
被除數就是eax,判斷正負看是否是補碼就可以.