zoukankan      html  css  js  c++  java
  • PC逆向之代码还原技术,第六讲汇编中除法代码还原以及原理第二讲,被除数是正数 除数非2的幂

    一丶简介

    上一篇博客说的除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
    则有下面公式:

    其实上图所说就是一个除法 转化成分子乘法的转变.
    如下:

    5/2 == 5 * ½  ==> 5 * (2N/2 * 2N)  ==  5 * (2n/2) * (1/2n);
    其实 2n/2 (2分之2n) 是在编译器的时候进行计算的。也就是说可以的出来的。
    

    设 (2n/2) == m
    那么 5 * M * (1/2n)
    继续简化既可以得出 (5*m) / 2n == 结果
    那么根据反汇编也明白了。

    .text:0040101A                 mov     eax, 38E38E39h              编译器计算出的(2n/2)
    .text:00401023                 mul     [esp+10h+var_8]             这句代码的意思 = x * (2n/2)
    .text:00401027                 shr     edx, 1                      这句代码的意思是 结果 /2n  2n此时的取值是33 因为 edx,eax 直接操作的edx的一位等价于操作 2^1 + 2^32次方
    

    根据上面的公式
    eax = 2N/o (o)为常量。
    mul == x * (2n/0)
    shr edx,1 == x * (2n/o) / 2n
    此时 2n我们可以知道是 2^33次方。
    带入公式得到

    x * (2^33/o) / 2^33
    求o

    2n / 2^33/o == 最终的常量就可以求出来了。
    带入公式得到 2^33 / 38E38E39h ≈ 8.99999999 结果向上取整就是9

    最终的结果就是
    x / 9

    公式如下

    所以还原的时候。 首先确定 38E38E39h 他其实是除法演变成乘法的一个产物。
    而后统计n。 n就是 右移的次数。 我们这里看到的是 2^33次方。
    带入公式解方程
    o = 2^n / M
    M = 2N/o

    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.

  • 相关阅读:
    [Taro] 解决 使用 Taro UI 小程序下 Iconfont 图标 不显示问题
    [Taro] Taro 环境安装 (一)
    [RN] react-native FlatList 实现列表选中的最佳方式(刷新指定Item)
    [RN] React Native FlatList 选中后 状态没有立即发生改变,而在下一次生效的问题
    [RN] React Native 使用 Redux 比较详细和深刻的教程
    [Taro] taro 缓存
    个人总结第十五周
    个人总结第十四周
    个人总结第十三周
    个人总结第十二周
  • 原文地址:https://www.cnblogs.com/iBinary/p/10041143.html
Copyright © 2011-2022 走看看