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