4.整数的取模
取模运算可以通过除法指令实现。一般的优化做法是将其转换成等价的位运算或者除法运算,再由除法运算进行优化。
虚函数
C++的三大核心机制是封装,继承,多态,而虚函数就是多态的一种体现。软件逆向中,难免遇到使用面向对象思想设计的软件,而虚函数就是在实际软件逆向过程中的一种还原面向对象的重要手段。本章探讨编译器实现虚函数的原理。
1.虚表
首先在main的入口先申请了对象实例的内存空间。先调用构造函数,调用fun1,fun2.这些成员函数调用的第一个参数就是this指针,也就是rcx=this。C++语法规定在实例化对象时会自动调用构造函数,对象作用域会自动调用析构函数。
在逆向过程中,当一个对象是在某个作用域内调用的第一个函数,就可以怀疑是构造函数,如果是最后一个,就怀疑是析构函数的调用。
构造函数中首先初始化虚表指针,然后初始化数据成员,构造函数完成,返回this指针。
然而为什么析构函数还要赋值虚表,构造函数不是赋值了吗?这是因为C++语法规定,析构函数需要调用虚函数的多态性。在分辨哪一个是构造函数哪一个是析构的时候可以看调用的顺序。
虚标中的函数成员函数声明顺序依次放入的。需要注意的是函数分布顺序在某些情况,和声明顺序不一定相同。不过顺序对逆向还原也没有影响。本例中只写了一个虚析构函数吗,却生成了两个析构函数。其中一个是普通析构函数,一个是放在虚表里的。
2.单重继承虚表
为什么main函数使用new申请对象空间。因为虚表需要8字节虚表空间和8字节的内存对齐。这里可以看出派生类和基类共享一个虚表指针。