PC逆向之代码还原技术,第六讲汇编中除法代码还原以及原理第一讲,除数是2的幂

2019-05-25 16:20:28 浏览数 (1)

目录

  • 一丶除法简介
  • 二丶简介除法原理
    • 1.搞明白数学中的向上取整 向下取整. 以及程序中的向零取整.
    • 2.除法的扩展知识
    • 2.除数为2的幂
  • 四丶除法第一讲总结
    • 1.除数是2的一次方
    • 2.除数为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

代码语言:javascript复制
6  / 4 = 1 ...2
  1. |r| < |b|     : 余数的绝对值,绝对会小于除数的. 比如 6 / 4 = 1 .... 2 那么 余数2 不关是正数还是负数,绝对都是绝对会小于除数的,也就是4
  2. a = q * b r     : 求被除数,被除数是商*除数 余数
  3. b = (a - r)/c    : 求除数,除数等于 被除数-余数 / 商
  4. q = (a - r)/b     : 求商: 被除数 - 余数 / 除数
  5. r = a - (q * b) : 求余数 被除数 - (商 * 除数)
  6. 三丶除法的代码还原. 有了以上的公式支撑.那么我们则可以进行除法的代码还原的学习了. 1.除数为2的一次方 高级代码:
代码语言:javascript复制
int main(int argc, char* argv[])
{

    int nValue = 10;
    scanf("%d",&nValue); //防止变量nValue优化成常量. 所以不让他优化

    int nTemp = nValue / 2; //常量是2的一次方  重要代码

    scanf("%d",&nTemp);//防止优化
    return 0;
}

Debug下的汇编

代码语言:javascript复制
.text:00401280                 mov     eax, [ebp var_4]
.text:00401283                 cdq
.text:00401284                 sub     eax, edx           重要代码
.text:00401286                 sar     eax, 1

其实如果是2的1次方,Debug跟Release下.都会产生代码定式.

代码定式:

代码语言:javascript复制
   mov reg,[ebp - ?];  获得被除数  reg存放的是被除数的值
   cdq                 符号扩展. 被除数是负数,那么 扩展符号位之后 edx = 0xFFFFFFFF 也就是-1 否则就是0
   sub eax,edx         调整符号位,被除数是正数,那么此条语句执行完相当于没制定. 被除数是负数. 则会形成  (被除数 - 1) 因为是负数.所以被除数是补码形式存在
   sar eax,1           sar相当于向下取整. sar是有符号右移,右移一位就是 / 2

代码定式可以进行总结:

代码语言:javascript复制
   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; 结果也是对的. 那么汇编定力我们就能看明白.

代码语言:javascript复制
   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的一次方 汇编定式:

代码语言:javascript复制
   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的幂

高级代码:

代码语言:javascript复制
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下汇编一样. 产生以下汇编代码

代码语言:javascript复制
.text:00401040                 mov     eax, [ebp var_4]    获得被除数
.text:00401043                 cdq                         符号扩展 eax,edx 被除数为正, edx = 0, 否则 edx = -1
.text:00401044                 and     edx, 7              位与运算.被除数为正数,此条指令没用,因为edx = 0. 0 & 7还是0 被除数为负数 edx结果为7
.text:00401047                 add     eax, edx            (eax   edx)/2^n edx = 0 则被除数是0   edx = -1 则被除数是负数. 且公式会有改变  (eax   7) / 2^n
.text:00401049                 sar     eax, 3

我们观看这个代码可以产生代码定式:

代码语言:javascript复制
.text:00401040                 mov     eax, [ebp var_4]    
.text:00401043                 cdq                         
.text:00401044                 and     edx, b            
.text:00401047                 add     eax, edx            
.text:00401049                 sar     eax, n

除数的还原: (2^n - 1) == b 那么被除数就是2^n次方 主要是还原除数. 被除数就是eax,判断正负看是否是补码就可以.

四丶除法第一讲总结

今天主要就是讲了两个除法的还原.除法很多.但是鉴于篇幅.一个博客只更新两个.便于以后复习,也便于 自己的理解.

1.除数是2的一次方

代码语言:javascript复制
   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的幂总结:

代码定式:

代码语言:javascript复制
.text:00401040                 mov     eax, [ebp var_4]    
.text:00401043                 cdq                         
.text:00401044                 and     edx, b            
.text:00401047                 add     eax, edx            
.text:00401049                 sar     eax, n

除数的还原: 如果: (2^n - 1) == b 那么被除数就是2^n次方 主要是还原除数. 被除数就是eax,判断正负看是否是补码就可以.

0 人点赞