逆向练习8

每日逆向的理解和积累。

汇编阅读练习

练习1

汇编代码及解释——

  • main函数





注意:分析过程中值得注意的几个问题——

(1)如图中红框框起来的部分所示,注意对于除法和求余运算的汇编表示。

(2)如图中标注的部分所示,注意对于源码中数组l[4]相关的操作在汇编层面的对应表示。


参考资料——

C语言time()函数:获取当前时间(以秒数表示);
C++中随机函数rand()和srand()的用法;

对应c语言源码——

问题描述——C语言人机猜数问题:由计算机随机产生一个四位整数,请人猜这四位整数是多少。人输入一个四位数后,计算机首先判断其中有几位猜对了,并且对的数字中有几位位置也正确,将结果显示出来,给人以提示,请人再猜,直到人猜出计算机随机产生的四位数是多少为止。


例如:计算机产生一个四位整数“1234”请人猜,可能的提示如下。人猜的整数计算机判断有几个数字正确,有几个位置正确:

  • 1122 2A1B
  • 3344 2A1B
  • 3312 3A0B
  • 4123 4A0B
  • 1243 4A2B
  • 1234 4A4B

问题分析如下——

具体编程实现如下——

练习2

吐槽:实在忍不住,还是要吐槽一下。今天开始尝试分析release版的应用程序,之前分析的都是debug版的应用程序。结果一上来就碰上C++和浮点数操作这两大难点结合起来的程序。分析着分析着,心态就爆炸了哈哈哈。言归正传,我觉得c++的release版简直不要太坑,可能也是我对于c++的基础不了解,以为懂c就懂c++(太天真了嘤嘤嘤)。所以后面还是先从release版的c程序入手,至于c++程序的分析,等我学完《c++反汇编》再说。当然,万一是这个程序的问题,下一个程序就没问题了呢?虽然不太可能,我还是打算明天再挣扎一下试试。另外,我觉得有必要搞一个浮点数相关的汇编指令的查询器之类的,不然每次找都很麻烦。

汇编代码及解释——


C++的release版本

  • main函数





C的release版本

  • main函数


注意:这里先比较一下c和c++下的汇编——我们可以看到,同样的功能,c++的代码量要大将近一半,而且如图中备注所示,c++的汇编有很大一部分代码是跟程序要实现的具体功能是无关的,增加了分析的难度。


C的debug版本

  • main函数

  • prime函数

  • pow函数

  • pow函数(ida F5得到的c伪码)

注意:这里比较一下c的release版和debug版——我们可以对比发现,c语言release版程序比起debug程序,比较大的优化体现在——

(1)debug版中是通过函数调用的方式调用pow函数和prime函数的,而在release版中则直接将这两个函数的汇编实现集成在了main函数中,省略了函数调用的步骤。

(2)release版中没有了debug版中对于函数j___RTC_CheckEsp的调用


补充1:我也比较了c++的debug版和release版程序,二者之间的不同和c语言情况下类似,但是还是不太清楚c++的release版程序中的最后那部分与程序功能无关的代码是个什么情况。另外,在分析时可以适当参考ida的F5伪代码,这样可以提高分析速度。


补充2——关于VS2010的内置库函数__ftol2_sse,用于实现浮点数到整数的转换,_ftol2_sse 函数底层是调用 cvtsi2sd 函数的。

参考链接——
汇编学习:float与double速度问题
代码优化-之-优化浮点数取整

对应源码——

问题描述——C语言求梅森素数:梅森数(Mersenne Prime)指的是形如2n-1的正整数,其中指数n是素数,即为Mn。如果一个梅森数是素数,则称其为梅森素数。例如22-1=3、23-1=7都是梅森素数。当n=2,3,5,7时,Mn 都是素数,但n=11时,Mn=M11=211-1=2047=23X89,显然不是梅森素数。试求出指数n<20的所有梅森素数。

具体编程实现如下——


c语言版——


c++版——

参考资料——

1.关于浮点数相关汇编指令

汇编浮点数运算指令大全
汇编中NEG和NOT的区别

2.其他

and esp, 0FFFFFFF8h有什么作用?
关于C++中的用于清除错误状态的函数clear()

练习3

汇编代码及解释——


C的release版本

  • main函数


  • input函数

  • input函数(ida F5得到的c伪码)


C的debug版本

  • main函数

  • copu函数

  • input函数

  • input函数(ida F5得到的c伪码)

注意:这里比较一下c的release版和debug版——我们可以对比发现,c语言release版程序比起debug程序,比较大的优化体现在——

(1)debug版中很多变量都是通过[ebp+xxh]的形式进行访问和操作的,而在release版中除了需要用户输入赋值的变量,其余变量都是通过寄存器的方式进行存储和访问的。

(2)对于如printf等库函数的调用,debug版中是通过2个jmp跳转实现调用,而在release版中则直接将库函数的地址保存在寄存器中,然后通过call 寄存器实现函数调用。

(3)release版中没有了debug版中对于如j___RTC_CheckEsp等实现各种保护机制的函数的调用

(4)debug版中是通过函数调用的方式调用copu函数和input函数的,而在release版中则将没有涉及到用户输入的子函数copu的汇编实现集成在了main函数中,省略了函数调用的步骤。

(5)在release版中对于重复的汇编代码实现进行了结构优化和精简。

(6)在release版中对于调用函数的参数传递,并不都是通过push操作传递的,虽然这是一个32位的exe,但是在release版中还是存在通过寄存器传递调用函数参数的情况。

对应源码——

问题描述——C语言“抢30”游戏:由两个人玩“抢30”游戏,游戏规则是:第一个人先说“1”或“2”,第二个人要接着往下说一个或两个数,然后又轮到第一个人,再接着往下说一个或两个数。这样两人反复轮流,每次每个人说一个或两个数都可以,但是不可以连说三个数,谁先抢到30,谁得胜。

具体编程实现如下——

参考资料——

一段求数字%2模代码的反汇编

练习4

汇编代码及解释——


C的release版本

  • main函数



  • main函数(ida F5得到的部分c伪码)


C的debug版本

  • main函数

注意:这里比较一下c的release版和debug版——我们可以对比发现,c语言release版程序比起debug程序,除了练习3中归纳的优化之外,本程序中特有的优化体现在——

(1)最明显的区别就是release版中比起debug版在结构上的精简和优化。

(2)对于变量的优化,这里补充一下,对于一般的int型变量,release版中都是通过寄存器来访问和操作的,但是对于数组类的变量,release版中也是通过[寄存器+偏移]的形式进行访问和操作的。

(3)release版中将debug版中的乘法操作如imul替换成了加法操作。

(4)此程序的release版中保存了程序起始和结尾处的cookie安全检查机制,优化省略掉了其他安全校验。


补充1——关于.pdb文件

我们知道,release版的应用程序,比起debug版的应用程序,最大的区别就在于其优化省略了很多调试信息。而用VS编译生成的release版的应用程序,VS将其调试信息保存在与exe文件同名的.pdb文件中,并在exe中指明了生成时对应.pdb文件的具体路径。所以在ida中加载此类exe时,会弹出如下图所示的提示框。

在分析exe时加载对应.pdb文件和不加载的主要区别在于——对于一些数据结构具体信息的标识,比如本程序中,加载了.pdb文件就只会标识出数组的起始地址信息,而未加载的话,ida就会无法识别出具体的数组这一结构,而是将数组的所有元素都列出来,分析起来就会比较麻烦,如下图对比所示。

对应源码——

问题描述——C语言旅客国籍问题:在一个旅馆中住着6个不同国籍的人,他们分别来自美国、德国、英国、法国、俄罗斯和意大利这几个国家。他们的名字分别叫A、B、C、D、E和F,要说明的是名字的顺序与前面提到的国籍不一定是相互对应的。现在已知:

  • A和美国人是医生。
  • E和俄罗斯人是教师。
  • C和德国人是技师。
  • B和F曾经当过兵,而德国人从未参过军。
  • 法国人比A年龄大,意大利人比C年龄大。
  • B同美国人下周要去西安旅行,而C同法国人下周要去杭州度假。

现要求根据上述已知条件,编程求出A、B、C、D、E和F各是哪国人。

具体编程实现如下——

参考资料——

for循环加大括号{}和不加有什么区别
for循环不加括号作用域到哪?
test 和 jg 配合起来用时,什么情况跳转?

练习5

汇编代码及解释——


C的release版本

注意release版程序中如何辨识结构体的成员变量以及结构体数组。

  • main函数

  • CaculateTax函数


C的debug版本

  • main函数

  • CaculateTax函数

注意:这里比较一下c的release版和debug版——我们可以对比发现,c语言release版程序比起debug程序,除了练习3和4中归纳的优化之外,本程序中特有的优化体现在——

release版中对于结构体数组中各个结构体的成员变量的访问比起debug版的精简和优化(详见图上标注)。


对应源码——

问题描述——C语言个人所得税问题:编写一个计算个人所得税的程序,要求输入收入金额后,能够输出应缴的个人所得税。 个人所得税征收办法如下:

  • 起征点为3500元;
  • 不超过1500元的部分,征收3%;
  • 超过1500〜4500元的部分,征收10%;
  • 超过4500〜9000元的部分,征收20%;
  • 超过9000〜35000元的部分,征收25%;
  • 超过35000〜55000元的部分,征收30%;
  • 超过55000〜80000元的部分,征收35%;
  • 超过80000元以上的,征收45%。

具体编程实现如下——

参考资料——

在C语言中,double、long、unsigned、int、char类型数据所占字节数