逆向工程未知浮点格式

Posted

技术标签:

【中文标题】逆向工程未知浮点格式【英文标题】:Reverse engineering unknown floating point format 【发布时间】:2016-01-13 17:45:07 【问题描述】:

我正在尝试对一些旧文件格式(Cinema4D 旧版本)进行逆向工程,但我找不到相关规范。

在这种文件格式中,我设法发现浮点值存储为四个字节,但它们似乎不是正常的 IEEE 格式,这不是字节序问题。我最近花了很多时间使用 hexfloat 转换工具来解决这个问题。

以下是一些示例值:

0     = 00 00 00 00
1     = 80 00 00 41
2     = 80 00 00 42
4     = 80 00 00 43
8     = 80 00 00 44

0.25  = 80 00 00 3F
16384 = 80 00 00 4F

我从上面两行观察到,当从 3F 到 4F 时,这里似乎有一些东西环绕

1.5  = C0 00 00 41
2.5  = A0 00 00 42

-1   = 80 00 00 C1
-1.5 = C0 00 00 C1
-2   = 80 00 00 C2
-3   = C0 00 00 C2

所以,这里有一些观察:

    增加最后一个字节+1,值加倍 如果设置了最后一个字节的高位,则为负数 第一个字节处理非整数值

虽然有一些明显的模式,并且有一些指数/尾数在发生,但我无法弄清楚这一点。也许我什至错过了一些明显的东西,这是正常的 IEEE 吗?弄清楚尾数/指数等有多少位不是问题(在上面的示例中,两个中间字节为零),首先我需要弄清楚得到浮点值的公式

【问题讨论】:

前三个字节可能是完整的有效数字(包括前导1)。 @Sneftel:对我来说也是这样。将第一个三个字节视为在[0.5, 1.0) 中给出有效数,最后一个字节的最高位是符号,剩余的 7 位给出超过 64 的指数。不过,这与我所知道的任何常见浮点格式都不匹配(不是 IEEE 754,不是 VAX,不是 IBM,不是 Cray,...)。 非常感谢两位,原来是这样(这可能是在fpu普及之前,可能在包含前导1的情况下在软件中实现更快) 如果它们在有效数字中显示领先的数字,请注意输入中可能存在非标准化数字。也就是说,也可以将 2 表示为 40 00 00 43。 【参考方案1】:

这里的线索是Cinema 4D 在 Commodore Amiga 平台上首次亮相,该平台使用 FFP 浮点格式,似乎是为简单的软件仿真而设计的。在Amiga ROM Kernel Reference Manual的第35章有解释:

尾数被认为是二进制定点分数;除了 0,它总是被归一化(尾数被移动并调整指数,因此尾数在其最高位置有一个 1 位)。因此,它表示小于 1 但大于或等于 1/2 的值。

指数是正确定位尾数以反映数字的真实算术值所需的二的幂。它以超 64 表示法保存,这意味着将二进制补码值向上调整 64,从而将 $40 (-64) 到 $3F (+63) 更改为 $00 到 $7F

0的值定义为32位全部为0

尾数位存储在最高有效三个字节中,而最低有效字节由最高有效位中的符号位和最低有效六位中的偏置指数组成。除零外,32 位数字x 的数值因此为 (-1)x * (x / 224) * 2(x - 64).

基于此,以下 ISO-C99 代码提供了一个函数 decode_ffp(),它返回以无符号 32 位整数形式提供的 FFP 浮点数的数值。请注意,伪零和非规范化编码的行为未定义,因为官方文档没有说明应如何处理它们。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>

float decode_ffp (uint32_t a)

    const uint32_t FFP_EXPO_BIAS = 64;
    const uint32_t FFP_MANT_BITS = 24;
    const uint32_t FFP_EXPO_BITS = 7;
    const uint32_t FFP_EXPO_MASK = (1 << FFP_EXPO_BITS) - 1;
    uint32_t mant = a >> (FFP_EXPO_BITS + 1);
    uint32_t sign = (a >> FFP_EXPO_BITS) & 1;
    int32_t expo = (a & FFP_EXPO_MASK) -  FFP_EXPO_BIAS;
    float val;

    if (a == 0) 
        val = 0.0f;
     else 
        val = exp2f (expo) * mant / (1 << FFP_MANT_BITS);
        val = (sign) ? (-val) : val;
    
    return val;


int main (void)

    uint32_t test_vec[] = 
        0x00000000,
        0x80000041,
        0x80000042,
        0x80000043,
        0x80000044,
        0x8000003F,
        0x8000004F,
        0xC0000041,
        0xA0000042,
        0x800000C1,
        0xC00000C1,
        0x800000C2,
        0xC00000C2
    ;
    int num_test_vec = sizeof test_vec / sizeof test_vec[0];

    for (int i = 0; i < num_test_vec; i++) 
        printf ("%08x ==> % 15.8e\n", test_vec[i], decode_ffp (test_vec[i]));
    
    return EXIT_SUCCESS;

【讨论】:

以上是关于逆向工程未知浮点格式的主要内容,如果未能解决你的问题,请参考以下文章

读写非标准浮点值

《逆向分析实战》1.1

同向逆向多车道线检测

特征工程

为你的服务打针疫苗 —— 混沌工程

为你的服务打针疫苗 —— 混沌工程