我是靠谱客的博主 傻傻皮皮虾,这篇文章主要介绍【数值溢出】从二进制的角度看数值溢出0x01.问题引入0x02.byte能表示的数据范围0x03.byte的溢出原理0x04.快速计算溢出后的值0x05.推广到所有数值类型的溢出,现在分享给大家,希望可以做个参考。

编程语言中的数字数据类型都预设了大小,也就是说,一个数字数据类型的变量,总会有能表达的上限,有上限就会有溢出。本篇从二进制的底层,分析解释一下数值溢出问题。以byte为例。


0x01.问题引入

  • 看如下一段Java代码,你能立马说出输出结果吗?
复制代码
1
2
3
4
5
6
7
8
public class Main { public static void main(String[] args) { int a=1888; byte b=(byte) a; System.out.println(b); } }
  • 或许你只能意识到:反正不是1888,反正不会超过byte能表示的数据范围。
  • 赶紧把代码复制下来编译运行一下,一看输出结果:96
  • 为什么会是96呢?
  • 清楚这个之前,我们不妨去探究一下,底层都干了些什么。

0x02.byte能表示的数据范围

  • 我们都知道,byte能表示的数据范围是:-128-127,但是,为什么是这个范围呢?

  • 原因:在Java中,对于byte类型的变量,JVM会为其分配一个字节的内存,一个字节也就是8位,但是由于最高位是符号位,所以能够表示的数据范围就是 -2^7 --2^7-1。原码,反码,补码知识补充:

    • 原码:最高位为符号位,“0”表示正,“1”表示负,其余位表示数值的大小。
    • 反码:正数的反码与其原码相同;负数的反码是对其原码逐位取反,符号位除外。
    • 补码:正数的补码与其原码相同;负数的补码是在其反码的末位加1(负数的补码是其绝对值取反)。
    • 在计算机中,用补码表示二进制数。
  • 范围可视化:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 其它数值数据类型的范围,跟byte范围的计算,一模一样。

0x03.byte的溢出原理

  • 回到最初的例子,我们看一下那个转换的过程都发生了什么。
  • int类型变量占4个字节的内存,也就是32位,所以它能表示的数据范围是:-2^31--2^31-1
  • 那么a变量在内存中的表示,是这样吗?

在这里插入图片描述

  • 必然不是,因为上面说到,一个int类型的变量,占了32位,那么完整的形式应该是:

在这里插入图片描述

  • 而此时一个未初始化的byte变量b在内存中应该是:

在这里插入图片描述

  • 此时如果进行强制类型转换 b=(byte)a,int类型变量的低8位会给byte,然后int其它位的数据就会丢失。如下:

在这里插入图片描述

  • 这也正好解释了最初b的输出问题。

0x04.快速计算溢出后的值

  • 使用二进制去理解原理固然清晰,但是如果都转到二进制计算的话,肯定是会比较复杂的。
  • 我们不如看下它的转换规律:
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int a=128; byte b=(byte) a; System.out.println(b); //上述代码输出-128 int a=129; byte b=(byte) a; System.out.println(b); //上述代码输出-127 int a=383; byte b=(byte) a; System.out.println(b); //上述代码输出127 int a=384; byte b=(byte) a; System.out.println(b); //上述代码输出-128 int a=-129; byte b=(byte) a; System.out.println(b); //上述代码输出127
  • 不难发现,随着int的增大,byte转换后的值都在一个循环内。如下:

在这里插入图片描述

  • 可以这样看这个图,在byte范围内127+1=-128-128-1=127

  • 那么整个的计算可以总结为:

    • 若a>127,上界溢出,顺时针循环,那么强转成byte后,若以0为起点,对256取余得到的值,就是在这个圈中从0开始往右走了多少。例如:1888,对256取余得96,相当于从0向右走了96,所以最后的值就是96。1920,对256取余得128,相当于从0向右走了128,所以最后的值就是-128。

    • 若a<-128,下界溢出,逆时针循环,那么强转成byte后,若以0为起点,对256取余得到的值,就是在这个圈中从0开始往左走了多少。例如:-385,对256取余得129,相当于从0开始向左走了129,所以最后的值就是127。依次类推。

  • 只要记住循环圆,计算还是比较简单的。

0x05.推广到所有数值类型的溢出

  • byte的溢出是一个数值溢出的典型例子,其它数值类型因为表示的范围比较大,发生溢出的情况比较少,不过其所有溢出的原理和byte类型一模一样。
  • int类型溢出的例子:
复制代码
1
2
3
4
5
6
7
public class Main { public static void main(String[] args) { System.out.println(Integer.MAX_VALUE+1==Integer.MIN_VALUE); //输出true } }
  • 数值溢出也算是一个很经典的漏洞,如果有数值溢出点,经过攻击者精心的构造利用,可能会造成巨大的损失。具体数值溢出漏洞利用,可以参考:整型溢出漏洞

随时注意数据类型范围很重要哦~~~

最后

以上就是傻傻皮皮虾最近收集整理的关于【数值溢出】从二进制的角度看数值溢出0x01.问题引入0x02.byte能表示的数据范围0x03.byte的溢出原理0x04.快速计算溢出后的值0x05.推广到所有数值类型的溢出的全部内容,更多相关【数值溢出】从二进制内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(76)

评论列表共有 0 条评论

立即
投稿
返回
顶部