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.

原文地址:https://www.cnblogs.com/iBinary/p/10041143.html

时间: 2024-11-06 12:40:34

PC逆向之代码还原技术,第六讲汇编中除法代码还原以及原理第二讲,被除数是正数 除数非2的幂的相关文章

Android 中使用代码动态布局

Android 中使用代码动态布局 本文介绍在android中使用代码动态布局,有时候根据不同的需求,比如需要根据服务器上的条目个数来决定app中页面布局控件(显示个数,图标等).此处介绍通过java代码进行动态布局. 一.效果图: 图片随便找的,将就将就吧 二.给出xml文件布局 <?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schema

Java中动态代码块,构造代码块,静态代码块之间的执行顺序

Java中动态代码块,构造代码块,静态代码块之间的执行顺序 /** * Java中动态代码块,构造代码块,静态代码块之间的执行顺序 * @author Administrator * */ class HelloA { public HelloA() { System.out.println("HelloA"); } { System.out.println("I'm A class"); } static { System.out.println("st

java中静态代码块、构造代码块、构造方法、main函数的执行顺序?

静态代码块:在类被加载的时候就运行了,且只运行一次.static{} 构造代码块:在创建对象时被调用,每次创建对象都会调用一次.比静态代码块少了static.{} 构造方法:用于创建对象时定义初始化的状态.构造函数不能直接调用,必须通过new运算符在创建对象时才会自动调用,一般方法是在程序执行到它的时候被调用. 先不创建对象,如下: public class Test { // 静态代码块 static { System.out.println("静态代码块"); } // 构造代码块

捕捉WPF应用程序中XAML代码解析异常

原文:捕捉WPF应用程序中XAML代码解析异常 由于WPF应用程序中XAML代码在很多时候是运行时加载处理的.比如DynamicResource,但是在编译或者运行的过程中,编写的XAML代码很可能有错误,此时XAML代码解析器通常会抛出称为XamlParseException的异常.但是抛出的XamlParseException异常提供的信息非常简单,或者是很不准确.此时我们关于通过对变通的方法来获取更多的异常信息: 我们知道,WPF应用程序中的XAML代码是在InitializeCompon

PC逆向之代码还原技术,第二讲寻找程序入口点

PC逆向之代码还原技术,第二讲寻找程序入口点 一丶简介 程序逆向的时候.我们需要知道程序入口点.动态分析的时候以便于看是什么程序编写的. 比如VC++6.0 我们可以写一个程序测试一下 我们写一段代码. F10进行调试.看看是谁调用的main即可. 部分代码拷贝如下: __try { _ioinit(); /* initialize lowio */ #ifdef WPRFLAG /* get wide cmd line info */ _wcmdln = (wchar_t *)__crtGet

爬虫技术(六)-- 使用HtmlAgilityPack获取页面链接(附c#代码及插件下载)

菜鸟HtmlAgilityPack初体验...弱弱的代码... Html Agility Pack是一个开源项目,为网页提供了标准的DOM API和XPath导航.使用WebBrowser和HttpWebRequest下载的网页可以用Html Agility Pack来解析. HtmlAgilityPack的文档是CHM格式的,有时会无法正常阅读CHM格式的文件.如果是IE不能链接到您请求的网页或者打开后"页面无法显示".请在要打开的CHM文件上右击属性,会在底下属性多了个"

逆向知识第十讲,循环在汇编中的表现形式,以及代码还原

一丶do While在汇编中的表现形式 1.1高级代码: #include "stdafx.h" int main(int argc, char* argv[]) { int nSum = 0; int i = 0; do { nSum = nSum + i; } while (i <=100); return 0; } 高级代码很简单,只是一个简单的求1~100的累加 1.2 Debug版本下的汇编表现形式 代码定式很简单 ADDR .....do While逻辑代码块 xxx

Android代码混淆技术

Android混淆是Android开发者经常使用的一种用于防止被反编译的常见手法.Android开发基于java语言的,很容易被别人反编译出来,一下就相当于裸奔了,特别是用于商业用途的时候,防止反编译是必要的措施.而Android混淆的确可以保证Android源代码的一定安全. Android混淆技术 Java类名.方法名混淆 Dalvik字节码包含了大量的调试信息,如类名.方法名.字段名.参数名.变量名等,使用反编译工具可以还原这些信息.由于类名.方法名等通常都会遵循一定的命名规范,破解者很容

大型.NET商业软件代码保护技术 技术与实践相结合保护辛苦创造的劳动成果

列举工作以来遇到的各种类型的软件所采用的代码保护技术,只讲原理不涉及技术细节实现,以避免产生法律问题.有些朋友说直接把代码放在Github开源下载,开源可以促进技术交流与进步,然而值钱的代码都积压在硬盘里面,即使很烂的代码都卖了很多钱,赢得了许多客户与市场.珍惜爱护自己写的代码,他们都是宝贵的财富. 以下保护技术主要测重于脱机验证与保护,不涉及联网(连接到许可证服务器)验证. 1 程序集混淆 Asembly obfuscate CLR代码的运行是即时编译执行的,.NET编译器只是将源代码文件编译