我们之前已经描述过自定义类型在反编译代码中的使用,但你可能还会遇到一些类似函数调用的不寻常关键字。它们被反编译器用来表示无法映射到优雅C代码的操作,或者只是为了使输出更紧凑。
它们列在反编译器提供的defs.h
头文件中(可以在你的IDA目录下的plugins/hexrays_sdk/include
中找到),但这里是常见的一些高层概述。
部分访问宏
有时代码可能会访问大变量的较小部分。为了不让代码充满大量的类型转换,反编译器使用辅助宏来实现这一目的。
-
LOWORD(x), LOWORD(x), LODWORD(x)
返回变量x
的最低字节/字/双字作为无符号值; -
HIWORD(x), HIWORD(x), HIDWORD(x)
返回相应的高位部分; -
BYTE1(x), BYTE2(x)
等返回内存顺序中的单个字节。变量被认为从内存中的字节0开始。 -
同样的宏但带有S前缀( SLOBYTE
,SBYTE1
等)返回有符号值。
注意:这种方法可能会在像PPC这样的高端处理器上导致一些混淆情况。因为大端数据是从高字节开始存储的,所以它的低位字节存储在最高的内存地址,并因此使用HIBYTE
宏访问。例如,考虑一个32位变量包含值0x1A2B3C4D。它将在小端(LE)和大端(BE)平台上以不同的顺序存储:
LE BE
┌──┬──┐
│4D│1A├◄───LOBYTE
├──┼──┤
│3C│2B├◄───BYTE1
├──┼──┤
│2B│3C├◄───BYTE2
├──┼──┤
│1A│4D├◄───HIBYTE
└──┴──┘
组合值
有时编译器需要表示相反的操作:两个值组合成一个更大的值。为此,使用“对”宏:
-
__PAIR16__(high, low)
从两个8位值创建一个16位值。与部分访问宏不同,它不依赖于内存顺序,而是使用简单的位移,因此结果在小端和大端代码中是相同的。例如,__PAIR16__(0x1A, 0x2B)
在任何情况下都返回0x1A2B
; -
__PAIR32__
,__PAIR64__
,__PAIR128__
执行相应的操作以获得更大尺寸的值; -
__SPAIR16__
等返回有符号值。
位和标志操作
一些汇编指令没有简单的C表示,因此使用自定义辅助函数。
-
__ROLn__(value, count)
和__RORn__(value, count)
(n=1,2,4,8)表示n字节的左旋和右旋; -
__OFADD__
和__OFSUB__
返回两个值的加法(减法)操作的溢出标志。 -
__CFADD__
和__CFSUB__
执行相同的进位标志。 -
__SETP__(x, y)
用于表示由表达式x-y生成的奇偶标志。
溢出检查乘法
最近的编译器开始在常见情况下添加溢出检查。例如,当调用operator new[]
时,编译器在后台必须将元素的大小乘以它们的数量。如果此操作溢出,可能会产生错误的值,导致分配不足或分配失败。程序员也可能添加手动溢出检查。以下辅助函数用于表示此类代码模式:
-
is_mul_ok(count, elsize)
表示对count*elsize
结果的溢出检查。假设如果没有发生溢出,则返回true。 -
saturated_mul(count, elsize)
返回乘法结果(如果可以安全计算),或相应大小的最大无符号整数值(例如0xFFFFFFFF
)。后者应确保在溢出情况下分配失败。这种模式在最近版本的Visual C++中对operator new[]
的调用中很常见。
值强制
有时代码将相同的底层值视为不同的类型。例如,著名的Quake的反平方根函数将32位浮点数视为整数,反之亦然:
float InvSqrt (float x){
float xhalf = 0.5f*x;
int i = *(int*)&x;
i = 0x5f3759df - (i>>1);
x = *(float*)&i;
x = x*(1.5f - xhalf*x*x);
return x;
}
虽然在源代码中这种转换是通过类型转换和解引用表示的,但在优化代码中,它们可能被简单的寄存器间移动替代,特别是在使用SSE或AVX指令时,这些指令使用相同的寄存器来存储浮点和整数值。因此,反编译器必须使用特殊宏来表示此类代码:
-
COERCE_FLOAT(v)
,COERCE_DOUBLE(v)
,COERCE_LONG_DOUBLE(v)
用于将v
的位模式视为相应的浮点类型。 -
COERCE_UNSIGNED_INT(v)
和COERCE_UNSIGNED_INT64(v)
用于相反的转换。 -
当浮点值被视为有符号整数时,你可能还会看到 SLODWORD
。
例如,以下是上述函数反编译后的伪代码:
double __cdecl InvSqrt(float a1)
{
float v2; // [esp+0h] [ebp-8h]
v2 = a1 * 0.5;
return (float)((1.5 - v2 * COERCE_FLOAT(0x5F3759DF - (SLODWORD(a1) >> 1)) * COERCE_FLOAT(0x5F3759DF - (SLODWORD(a1) >> 1))) * COERCE_FLOAT(0x5F3759DF - (SLODWORD(a1) >> 1)));
}
原文始发于微信公众号(二进制磨剑):IDA 技巧(67) 反编译器辅助工具
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论