PC逆向之代碼還原技術,第六講匯編中除法代碼還原以及原理第二講,被除數是正數 除數非2的冪
目錄
- 一丶簡介
- 二丶代碼還原講解
- 1.被除數無符號 除數非2的冪
- 2.被除數無符號 除數為特例7
- 三丶代碼還原總結
一丶簡介
上一篇博客說的除2的冪. 如果被除數是有符號的,那麽會進行調整,並使用位操作進行優化
本片博客專門講解除數不是2的冪
二丶代碼還原講解
1.被除數無符號 除數非2的冪
高級代碼:
Release匯編
.text:0040101A mov eax, 38E38E39h .text:00401023 mul [esp+10h+var_8] .text:00401027 shr edx, 1
除數怎麽還原
代碼定式:
.text:0040101A mov eax, M
.text:00401023 mul x 此指令等價於 mul x,M
.text:00401027 shr edx, N
還原公式 : (2^(32 +n)) / M = 2^n / M 最後結果向上取整.
代入公式:
2^33 / 38E38E39
= 8589934592 / 954437177
= 8.9999999989522621036795594143123
= 向上取整(8.999...)
= 9
得出被除數是9
原理:
如果不感興趣可不看,或者你有<<C++ 與反匯編與逆向分析揭秘>>這本書,可以觀看第67頁.
原理分析:
首先那麽大數是怎麽來了.
由於除法指令周期比乘法指令周期長.所以編譯器會使用較短的乘法指令來代替除法.
數學證明公式:
設被除數為 x 除數為o
則有下面公式:
由於除數o是一個常量.且2^n次方由編譯器選擇. 所以 2^n / o 可以在編譯期間就算出來. 算出來的結果就是那個很大的數.
為什麽是很大的數,在VC++中,n的取值是大於32的. 也就是大於 2^32次方.所以參與運算就會很大了.這樣的好處就是
可以直接使用乘法的高位了.
既然我們知道那麽最大的數就是 2^n / o(除數) 的出來的.所以我們要求除數o
我們設 2^n/o 為c
則有以下公式:
可以看到最後優化成了 (x * c) >> n 等價於 (x * M) /2^n次方啊.
這裏說一下,為什麽是2^n次方.因為使用mul的時候,n的取值是大於32的.也就是2^32次方. 然後下面 使用了 sar edx,N
直接使用了edx,我們說過 eax,edx用於乘法.那麽eax就是高位.edx就是低位.這裏直接對 edx 右移了一位. 那麽是不是就是相當於
(eax,edx) >> 1位. 而eax是2^32次方.這裏直接對edx移動了.默認就是操作了eax. 所以隱含的eax不要忘記.
所以我們有了公式: (x * c) / (2^(32 + n)次方.這也跟我們上面的匯編指令相對應.
此時我們求o,進行反推: 2^n / c . 求出除數
所以還原公式為: 2^n / c.
2.被除數無符號 除數為特例7
高級代碼:
int main(int argc, char* argv[])
{
unsigned int nValue = 16;
scanf("%d",&nValue);// 防止優化.核心代碼不是這個
int nTemp = nValue / 7; //核心代碼 一會觀看反匯編
scanf("%d",&nTemp); //防止優化
return 0;
}
Debug下的匯編
.text:00401040 mov eax, [ebp+var_4]
.text:00401043 xor edx, edx
.text:00401045 mov ecx, 7
.text:0040104A div ecx
.text:0040104C mov [ebp+var_8], eax
Debug下的匯編很簡單. 獲取被除數,因為被除數是無符號.所以edx為0.所以會使用指令 xor edx,edx
進行清零. 這條語句也可以是 cdq.因為是無符號.所以使用cdq符號擴展那麽edx也是0.可能xor指令周期
比xor周期長.所以沒有使用. 雖然Debug不進行有效優化. 註意不進行有效優化是方便我們調試.但是也會
進行優化.當然不會影響你的調試. 比如 xor 也可以是cdq
如下:
.text:00401040 mov eax, [ebp+var_4]
.text:00401043 cdq
.text:00401045 mov ecx, 7
.text:0040104A div ecx eax = eax / ecx 等價於 eax = eax / 7;
.text:0040104C mov [ebp+var_8], eax
Debug下直接進行還原即可.很簡單.
Release下的匯編
.text:0040101A mov ecx, [esp+10h+var_8]
.text:0040101E mov eax, 24924925h
.text:00401023 mul ecx
.text:00401025 sub ecx, edx
.text:00401027 shr ecx, 1
.text:00401029 add ecx, edx
.text:0040102B shr ecx, 2
.text:0040102E mov [esp+10h+var_4], ecx
Release下的匯編就比較煩了.為什麽指令是這樣. 有一個超大的數, 還有各種乘法, 減法. 移位 加法.
其實這都是有數學原理進行支撐了.而且這個還是個特例.如果不想知道數學原理.直接記住匯編順序
乘 減 移 加 移. 也算是特征. 正好對應 mul sub shr add shr
還原方法:
還原的時候我們可以設置未知數.這樣直接給一個公式進行還原
如下:
.text:0040101A mov ecx, [esp+10h+var_8]
.text:0040101E mov eax, M 設最大數為M
.text:00401023 mul ecx
.text:00401025 sub ecx, edx
.text:00401027 shr ecx, N 設移位為N
.text:00401029 add ecx, edx
.text:0040102B shr ecx, N 設置移位為N
.text:0040102E mov [esp+10h+var_4], ecx
還原方法: 2^N/(2^32+M)的商向上取整
可以帶入公式:
M = 24924925h 十進制 = 613566757
n 有兩個,一個是1 一個是2 兩個n相加就是3, 因為使用edx.沒有使用eax 除法會使用 eax,edx. 所以使用edx變相的相當於以及有了2^32次方了.
代入公式:
2^35 / (2 ^32 + M)
= 34359738368 / (4294967296 + 613566757)
= 34359738368 / 4908534053
= 6.9999999993888195604619552997935
= 商向上取整 (6.9999999993888195604619552997935)
= 7
所以得出了除數為7
代碼還原的時候直接還原成 Var_8 / 7 即可.如果想看原理,且向下看.
三丶代碼還原總結
學習了新的兩種定式:
第一種,被除數為正數, 除數為正數. MUL是無符號,所以不需要進行調整.直接套用公式還原
.text:0040101A mov eax, M
.text:00401023 mul x 此指令等價於 mul x,M
.text:00401027 shr edx, N
還原公式 : (2^(32 +n)) / M = 2^n / M 最後結果向上取整.
第二種 被除數為正數 除數是特例
特征: 匯編中出現 乘 減 移 加 移
.text:0040101A mov ecx, [esp+10h+var_8]
.text:0040101E mov eax, M 設最大數為M
.text:00401023 mul ecx ecx = ecx * M
.text:00401025 sub ecx, edx
.text:00401027 shr ecx, N 設移位為N
.text:00401029 add ecx, edx
.text:0040102B shr ecx, N 設置移位為N
.text:0040102E mov [esp+10h+var_4], ecx
還原方法: 2^(32 + N)/(2^32+M)的商向上取整
簡化公式: 2^N / (2^32 + M) 一定註意隱藏的N大於32.
PC逆向之代碼還原技術,第六講匯編中除法代碼還原以及原理第二講,被除數是正數 除數非2的冪