Hex-Rays反编译器最初是为了解析C代码而创建的,因此其伪代码输出主要使用C语法。然而,输入的二进制文件可能是用其他语言编译的:C++、Pascal、Basic、ADA等。虽然大多数代码可以用C语言表示而没有实际问题,但有些代码有其特殊性,需要语言扩展或通过用户输入来处理。
还有一些语言使用的方式与标准编译的C代码非常不同,因此需要特别处理。例如,Go使用的调用约定(基于栈或寄存器)与标准C调用约定非常不同,因此需要为其在IDA中添加自定义支持。
多重返回值
即使有自定义调用约定,IDA的类型系统仍然存在一个基本限制(截至IDA 8.0):一个函数只能返回一个值。然而,即使在其他C风格的程序中,你也可能遇到返回多个值的函数。一个例子是编译器助手如idivmod
/uidivmod
。
它们同时返回除法运算的商和余数。反编译器知道标准的(例如ARM EABI的__aeabi_idivmod
),但你可能会遇到非标准实现,或使用类似方法的无关函数(例如手动用汇编编写的函数)。
因为反编译器不期望函数返回多个值,你可能需要检查反汇编或查看调用位置来识别这样的函数。例如,这里是一个反编译的ARM32代码片段,似乎使用了未定义的寄存器值:
该函数似乎修改了R1
寄存器,尽管通常返回值(对于32位类型)放在R0中。可能这是一个等价的divmod函数,它在R0
中返回商,在R1
中返回余数?
为了解决这个问题,我们可以使用一个人工结构体和一个自定义调用约定,指定它应该放置的寄存器和/或栈位置。例如,将这样的结构体添加到本地类型:
ounter(lineounter(lineounter(lineounter(lineounter(line
structdivmod_t
{
int quot;
int rem;
};
并设置函数原型:divmod_t __usercall my_divmod@<R1:R0>(int@<R0>, int@<R1>);
然后反编译器将调用后的寄存器值解释为结构体字段:
类似的方法可以用于原生支持多重返回值函数的语言:Go、Swift、Rust等。
学习资源
立即关注【二进制磨剑】公众号
原文始发于微信公众号(二进制磨剑):IDA技巧(107)多重返回值
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论