IDA 技巧(67) 反编译器辅助工具

admin 2024年11月5日13:42:55评论9 views字数 2195阅读7分19秒阅读模式

我们之前已经描述过自定义类型在反编译代码中的使用,但你可能还会遇到一些类似函数调用的不寻常关键字。它们被反编译器用来表示无法映射到优雅C代码的操作,或者只是为了使输出更紧凑。

它们列在反编译器提供的defs.h头文件中(可以在你的IDA目录下的plugins/hexrays_sdk/include中找到),但这里是常见的一些高层概述。

部分访问宏

有时代码可能会访问大变量的较小部分。为了不让代码充满大量的类型转换,反编译器使用辅助宏来实现这一目的。

  1. LOWORD(x), LOWORD(x), LODWORD(x)返回变量x的最低字节/字/双字作为无符号值;
  2. HIWORD(x), HIWORD(x), HIDWORD(x)返回相应的高位部分;
  3. BYTE1(x), BYTE2(x)等返回内存顺序中的单个字节。变量被认为从内存中的字节0开始。
  4. 同样的宏但带有S前缀(SLOBYTE, SBYTE1等)返回有符号值。

注意:这种方法可能会在像PPC这样的高端处理器上导致一些混淆情况。因为大端数据是从高字节开始存储的,所以它的低位字节存储在最高的内存地址,并因此使用HIBYTE宏访问。例如,考虑一个32位变量包含值0x1A2B3C4D。它将在小端(LE)和大端(BE)平台上以不同的顺序存储:

 LE BE
┌──┬──┐
│4D│1A├◄───LOBYTE
├──┼──┤
│3C│2B├◄───BYTE1
├──┼──┤
│2B│3C├◄───BYTE2
├──┼──┤
│1A│4D├◄───HIBYTE
└──┴──┘

组合值

有时编译器需要表示相反的操作:两个值组合成一个更大的值。为此,使用“对”宏:

  1. __PAIR16__(high, low)从两个8位值创建一个16位值。与部分访问宏不同,它不依赖于内存顺序,而是使用简单的位移,因此结果在小端和大端代码中是相同的。例如,__PAIR16__(0x1A, 0x2B)在任何情况下都返回0x1A2B
  2. __PAIR32__, __PAIR64__, __PAIR128__执行相应的操作以获得更大尺寸的值;
  3. __SPAIR16__等返回有符号值。

位和标志操作

一些汇编指令没有简单的C表示,因此使用自定义辅助函数。

  1. __ROLn__(value, count)__RORn__(value, count)(n=1,2,4,8)表示n字节的左旋和右旋;
  2. __OFADD____OFSUB__返回两个值的加法(减法)操作的溢出标志。
  3. __CFADD____CFSUB__执行相同的进位标志。
  4. __SETP__(x, y)用于表示由表达式x-y生成的奇偶标志。

溢出检查乘法

最近的编译器开始在常见情况下添加溢出检查。例如,当调用operator new[]时,编译器在后台必须将元素的大小乘以它们的数量。如果此操作溢出,可能会产生错误的值,导致分配不足或分配失败。程序员也可能添加手动溢出检查。以下辅助函数用于表示此类代码模式:

  1. is_mul_ok(count, elsize)表示对count*elsize结果的溢出检查。假设如果没有发生溢出,则返回true。
  2. 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指令时,这些指令使用相同的寄存器来存储浮点和整数值。因此,反编译器必须使用特殊宏来表示此类代码:

  1. COERCE_FLOAT(v), COERCE_DOUBLE(v), COERCE_LONG_DOUBLE(v)用于将v的位模式视为相应的浮点类型。
  2. COERCE_UNSIGNED_INT(v)COERCE_UNSIGNED_INT64(v)用于相反的转换。
  3. 当浮点值被视为有符号整数时,你可能还会看到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) 反编译器辅助工具

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月5日13:42:55
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   IDA 技巧(67) 反编译器辅助工具https://cn-sec.com/archives/3357579.html

发表评论

匿名网友 填写信息