7.变量还原(1)变量的机器级表示

什么是变量?

看到这个有人会问,我这每天写代码,这变量是啥我还不知道吗?

那么对于计算机,或者对于汇编,对于机器指令,有没有变量这个概念?

甚至说,对于计算机来说,有没有"编程语言"这个概念?

有人说:计算机系统的本质就是一组离散状态空间及其变化规则,程序不过是符合规则的一组变化动作的组合。

有人说:电子计算机,是人利用电子电路和电子运动的物理规律,精心构建输入与输出效果,表达自己的设定。

有人说。。。。。。

------------------------------

好吧,这里也不是讨论哲学问题的地方,让我们回到现实里来。

什么是变量?:对于计算机来说没有变量,只有状态间的变化。编程语言和变量都是给人类看的,它们是一种符合程序员思考并操作的状态抽象。

什么是反编译的变量还原:通过研究编译器如何将上层抽象转换到计算机底层实现,依照一系列的约定与特征,从汇编中还原出上层抽象。

因此反编译的前提是:明确编译器的行为。

------------------------------

这还有一个绕不开的问题:在反编译器的眼中,变量是什么?

对于现在流行的编译器而言,变量是什么?变量就是SSA。就是有且在一个地方定义,在其它地方使用的东西。

变量在汇编中的表示(x86_64_ELF示例)

拆分常见的各种变量,展示他们在二进制汇编中的样子。知道了编译器对变量的表示才知道如何进行还原。

全局变量

全局变量在汇编中是比较好识别的,通常是对BSS、data、rdata段上数据的引用;

他们的地址固定(静态分析场景,不考虑动态的地址随机化保护),在IDA中会以unk_<address>标签展示,本质上是第一个数据地址的立即数:

函数局部变量(函数参数)

函数参数的局部变量,取决于函数的调用约定。通常是在栈与寄存器上;具体的判断逻辑,可以回头看一下之前调用约定与函数推导的那一节。

在IDA中,将鼠标放到F5后的函数参数上,会提示这个参数是如何传入的。比如下面这个this指正就是rdi传入的。

函数局部变量(寄存器)

编译器的寄存器着色分配算法;会尽可能的将所有的变量分配到寄存器中。如果变量很多,还会尽可能的复用寄存器。

在IDA的F5代码中,会在局部变量后的注释显示使用的寄存器:

多个变量经常使用相同的寄存器,这很正常,因为寄存器复用的机制。

函数局部变量(栈)

局部变量分配到栈上,有这么几个场景:

  • 数组形态的局部变量,如果数组很大会在栈上开辟固定长度的空间

  • 无法分配到寄存器的局部变量:寄存器不够用了,或者强制要求在栈上的变量(比如canary)

  • 动态开辟的栈空间(这种情况比较罕见)

在IDA中栈上变量后的注释会以相对于栈顶和栈底的偏移,注意,这里面的rbp/rsp只是栈顶和栈底的抽象,并不一定是真正存在的。

堆变量

现在的反编译的维度通常是函数级的,没有对于动态分配的“堆抽象”因此,堆变量就会被翻译成指针操作。

临时变量

除此之外,还有一些临时变量,他们是数据操作过程的中间产物。比如,代码中有一个很复杂的计算语句:

a = b + c + d + e .......

在编译器转换为ssa的过程中,每个加法操作符,都会添加一个ssa的临时变量。转换到汇编中去,如果寄存器着色算法能够分配足够的寄存器存放临时变量,那么寄存器会被多次重用,否则就需要栈变量作为临时变量的存放地点了

Last updated