【第22期】观点:IT 行业加班,到底有没有价值?

整数除法(除以2的幂, 除以非2的幂)

转载 2016年08月30日 02:23:00

前言

除法推导过程还不理解, 先用公式吧.
这里写图片描述
这里写图片描述

试验总结

找个Demo来逆向.
只有除法(变量/常量)会有魔法数, +-*都不会.
看到魔法数(很怪的很大的16进制数)时, 先套除法公式, 看是不是整数除法. 一共就7种, 判断完, 都不是, 再按照正常的做法翻译成C代码.
如果不能用C语言直接描述, 还是得看看是否看错了. e.g. cdq出现在魔法数的上下文中.
如果做个除法片段判断程序, 将反汇编定式都收录进来, 就不用人工判断是否为除法了, 能提高效率.
在没作出除法定式判断程序前, 可以封装一个函数, 根据m,n算出b, 看像不像除数.

逆向完后, 先比对RE工程生成的反汇编代码和原始工程的反汇编代码的区别, 在release模式下, 因为使用了寄存器变量, 无法保证完全相同.

如果测试数据够全或输入数据够少或输出结果可预测, 可以输入数据跑一下, 看RE工程和原始工程输出是否一样.

试验代码

// RE.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <math.h>

//     int CalcDiv_B(double fM, int n) {
//         int iC = 0;
// 
//         /*
//         变量除以非2的幂 公式:
// 
//         被除数 a
//         除数 b
//         商q
//         余数 r
// 
//         a = qb + r
//         a/b = q + r/b
// 
//         a/b = a* (1/b) = a* (2^n) /b * (1/2^n) = a* 2^n /b >> n
//         设m = (2^n)/b
//         b = (2^n)/m
//         a/b = a * m >> n
// 
//         m已知, 出现的除法反汇编实现中
//         被除数a已知, 和m做乘积的那个数就是a
//         n要根据除法反汇编中移位次数算出来, 也是已知的
//         */
// 
//         double fB = pow(2, n);
//         fB = fB / fM;
// 
//         // 一直要向上取整
//         iC = (int)fB + 1;
//         return iC;
//     }

int main(int argc, char* argv[])
{
    // int iC = CalcDiv_B(0x6BCA1AF3, 35); ///< 19
    // int iC = CalcDiv_B(0x55555556, 32);

//     ; =============== S U B R O U T I N E =======================================
// 
//     ; .text:00401000
// 
//     ; int __cdecl main(int argc, const char **argv, const char **envp)
//     _main           proc near               ; CODE XREF: start+AF.p
// 
//     argc            = dword ptr  4
//     argv            = dword ptr  8
//     envp            = dword ptr  0Ch
// 
//                     push    esi
//                     push    edi

    int eax = 0;
//                     xor     eax, eax

    int ecx = 0;
//                     xor     ecx, ecx

    int esi = 8;
//                     mov     esi, 8

    int edi = 0;
//                     xor     edi, edi
// 
    do {
        if (0 == eax) {

//     L_BEGIN:                                ; CODE XREF: _main+1D.j _main+3C.j ...
//                     test    eax, eax        ; .text:0040100D
//                     jnz     short L_NEXT1   ; .text:0040101F
            edi = 1;
//                     mov     edi, 1

            eax = 4;
//                     mov     eax, 4

            ecx = edi;
//                     mov     ecx, edi
            continue;
//                     jmp     short L_BEGIN   ; .text:0040100D
//     ; ---------------------------------------------------------------------------
        }

// 
//     L_NEXT1:                                ; CODE XREF: _main+F.j
        if (1 == eax) {
//                     cmp     eax, 1          ; .text:0040101F
//                     jnz     short L_NEXT2   ; .text:0040103E

                        // 变量/常量的除法么?
                        // m = (2^n)/c
                        // c = (2^n)/m

                        // 如果是除法
                        // n = 32 + 3 = 35
                        // m = 6BCA1AF3h
                        // c(除数) = (2^35)/6BCA1AF3h = 

//                     mov     eax, 6BCA1AF3h
//                     imul    ecx
//                     sar     edx, 3

//                     mov     eax, edx
//                     shr     eax, 1Fh
//                     add     edx, eax
            ecx = ecx / 19;

//                     mov     eax, 3
            eax = 3;
//                     mov     ecx, edx
//                     jmp     short L_BEGIN   ; .text:0040100D
            continue;
        }
//     ; ---------------------------------------------------------------------------
// 
//     L_NEXT2:                                ; CODE XREF: _main+22.j
//                     cmp     eax, 2          ; .text:0040103E
//                     jnz     short L_NEXT3   ; .text:0040105A
        if (2 == eax) {
//                     mov     eax, edi
//                     imul    eax, ecx
            eax = edi * ecx;

            // cdq不是C语言能直接描述的
//                     cdq
//                     and     edx, 3
//                     add     eax, edx
//                     sar     eax, 2
//                     mov     ecx, eax
            /*
            变量除以2的幂 公式:
            对零取整[X / 2^n] = 下取整[(x + 2^n - 1)/2^n]
            */
            // 依赖[变量除以2的幂]公式, 看出 n = 2, 被乘数是eax
            ecx = eax / 4;

//                     mov     eax, 1
            eax = 1;
//                     jmp     short L_BEGIN   ; .text:0040100D
            continue;
        }
//     ; ---------------------------------------------------------------------------
// 
//     L_NEXT3:                                ; CODE XREF: _main+41.j
//                     cmp     eax, 3          ; .text:0040105A
//                     jz      short L_NEXT4   ; .text:00401083
        if (eax == 3) {
            break;
        }
//                     cmp     eax, 4
//                     jnz     short L_BEGIN   ; .text:0040100D
        if (eax != 4) {
            continue;
        }

//                     mov     edx, esi
//                     mov     eax, 55555556h

//                     imul    edx, esi
//                     imul    esi, edx
        esi = esi * esi *esi;

        // imul esi 开始操作魔法数eax, 开始算除法(变量/非2的幂), m = 0x55555556
        // esi * eax => edx:eax
        // a/b = am >> n
        // a*m后, 没有继续向右移位, 直接使用edx调整商, 说明 n = 32
        // CalcDiv_B(0x55555556, 32) 算出除数 = 3
//                     imul    esi

        // 用符号位调整商
//                     mov     eax, edx
//                     shr     eax, 1Fh
//                     add     edx, eax
        edi = esi / 3;
//                     mov     eax, 2
        eax = 2;
//                     mov     edi, edx

//                     jmp     short L_BEGIN   ; .text:0040100D
            continue;

//     ; ---------------------------------------------------------------------------
    } while (1);
// 
//     L_NEXT4:                                ; CODE XREF: _main+5D.j
//                     lea     edx, [ecx+ecx*8] ; .text:00401083
                        // 9 * ecx
//                     lea     eax, [ecx+edx*2]
                        // 变量扩散
                        // lea eax, [ecx + 9 * ecx * 2]
                        // lea eax, 19*ecx
//                     shl     eax, 2
                        // 19*ecx *4 = 76*ecx
                        eax = 76*ecx;
//                     push    eax
//                     push    offset Format   ; "%d\r\n"
//                     call    _printf
                        printf("%d\r\n", eax);

//                     add     esp, 8
//                     xor     eax, eax
//                     pop     edi
//                     pop     esi
//                     retn                    ; .text:0040109E
//     _main           endp
// 
//     ; ---------------------------------------------------------------------------

    return 0;
}

举报

相关文章推荐

有符号数除以非2的幂

前言 当x为有符号数时, 此公式适用. 此公式推导的前提是 x 0时, 等于 + 0, 和无符号除以非2的幂相同了).试验// hw.cpp : Defines the entry poin...

【计算机系统学习-信息表示和处理】【二、整数表示】

现在来看计算机是怎样存储整数的。 首先,通过c语言来看看c中的各种整数类型的大小。 其实通过sizeof函数我们已经了解到了,比如,sizeof(char) = 1表示使用一个字节来存储char类型,而1byte = 8bit 所以,28 = 256 ,所以我们可以表示256个数字,如...
  • airu
  • airu
  • 2010-10-26 17:06
  • 278

程序员升职加薪指南!还缺一个“证”!

CSDN出品,立即查看!

计算机中如何实现除数是2的幂次的除法【转载自CSDN】

前言: 本来是在看汇编里面的数据条件传送指令,做习题的时候看着这么一道有关于2的幂次方除法的题目。结果傻眼了,又尼玛不会了。。。。。。。。。第二章看的时候就稀里糊涂的,看了几遍也没看太懂,这回又涉及到...

Python学习笔记2

5.数据类型简介     a.列表(List)        List 是处理一组有序项目的数据结构,即你可以在一个列表中存储一个序列的项目。        List格式...
  • gq913
  • gq913
  • 2008-07-23 17:28
  • 1767

Q for Mortals2笔记 -- 基本操作

  函数的介绍 调用函数的时候用[]来分隔函数名和参数,在参数列表中用;分隔各参数,如f[x;y]。操作符实际上也是函数,只不过函数通常写法是函数名+参数,而操作符是介于操作数中间。   Verb:二元操作符,例如2+3中的+操作符,verb也可以使用通常的函
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)