问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

浮点数的存储方式和CTF实战

创作时间:
作者:
@小白创作中心

浮点数的存储方式和CTF实战

引用
1
来源
1.
https://www.cnblogs.com/theseventhson/p/14544262.html

浮点数的存储方式是计算机科学中的一个重要概念,特别是在处理精度要求较高的计算时。本文将详细介绍浮点数的存储方式,并通过一个CTF(Capture The Flag)题目实例展示其实际应用。

浮点数的二进制表示

  1. 整数和小数的二进制转换
  • 整数部分:采用除以2取余数的方式,直到商为0。例如,十进制的9转换为二进制为1001。

  • 小数部分:采用乘以2取整数部分的方式,直到小数部分为0或达到所需精度。例如,0.625转换为二进制为101。

    因此,6.625用二进制表示为110.101。验证方法如下:

  • 整数部分:(2^2 + 2^1 + 0*2^0 = 6)

  • 小数部分:(12^{-1} + 02^{-2} + 1*2^{-3} = 0.5 + 0 + 1/8 = 0.625)

IEEE 754标准下的浮点数存储

内存中的数据都是以二进制形式存储的,因此需要通过人为抽象存储格式来表示浮点数。目前国际通用的浮点数存储规则是IEEE 754,其存储格式如下:

以4字节的float为例,存储格式为:

  • 最高位31位存储符号位s
  • 23~30这8位存储指数E
  • 剩下23位存储尾数M

需要注意的是:

  • 指数E采用偏置表示法,即实际指数值为存储值减去127。例如,指数为2时,E=127+2=129=1000 0001。
  • 尾数M中小数点左边的1可以省略,只存储小数点右边的位数。
  • M只有23位,如果十进制的小数部分乘以2始终不为1,那么只取23位,其余的舍弃,这是部分浮点数有误差、无法精确表示的根本原因。

因此,6.625这个浮点数在内存中的存储形式如下:

CTF实战:浮点数存储的应用

接下来通过一个2018护网杯CTF题目“getting_start”来展示浮点数存储的实际应用。

题目代码如下:

int main() {
    double v7 = 0.1;
    double v8 = 0.1;
    char buf[32];
    read(0, buf, 40);
    if (v7 == v8) {
        system("/bin/sh");
    }
}

要使system("/bin/sh")被执行,需要让if条件不成立,即v7 != v8。由于v7已经固定为0.1,只能通过控制buf的值来改变v8的值。

分析栈布局可知:

  • buf距离v8有32字节
  • read会读取40字节的数据

因此,可以通过控制输入数据来覆盖v8的值。具体payload构造如下:

payload = b"a" * 24 + p64(0x7FFFFFFFFFFFFFFF) + p64(0x3FB999999999999A)

其中,0x3FB999999999999A是0.1在内存中的存储形式,接下来详细解释其计算过程:

  1. 将0.1转换为二进制形式

    通过计算可知,0.1的二进制表示为:

    从第6步开始出现循环,因此二进制形式为:

    0.0 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011...
    
  2. 提取s、M、E三个要素

    小数点向右移4位才遇到第一个1,因此表达式变为:

    1.1 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011*2^(-4)
    

    这里三个要素明确为:

  • s=0

  • M=1 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 001

  • E=(2^10-1)-4=1023-4=1019=011 1111 1011

    注意:这里的M只保留52位时,最后一位是1,这位不能直接去掉,要在末尾+1(小数存放的误差就是这么来的),所以真正的M = 1 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 010

  1. 拼接s、M、E

    按照标准把s、M、E首尾拼接:

    0 || 011 1111 1011 || 1 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 010
    

    即:

    0011 1111 1011 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010
    

    转换为十六进制为:

    0x3FB999999999999A
    

参考资料

  1. 浮点数进制转换(讲的很详细,强烈建议看完)
  2. 浮点数(float或double)10进制和2进制之间转换的工具
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号