编程语言中的数字数据类型都预设了大小,也就是说,一个数字数据类型的变量,总会有能表达的上限,有上限就会有溢出。本篇从二进制的底层,分析解释一下数值溢出问题。以byte为例。
0x01.问题引入
- 看如下一段Java代码,你能立马说出输出结果吗?
1
2
3
4
5
6
7
8public 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
26int 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
7public 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.推广到所有数值类型的溢出的全部内容,更多相关【数值溢出】从二进制内容请搜索靠谱客的其他文章。
发表评论 取消回复