整形溢出基础
C 语言中,整数的基本数据类型分为短整型 (short),整型 (int),长整型 (long),这三个数据类型还分为有符号和无符号,每种数据类型都有各自的大小范围(数据类型的大小范围是编译器决定),当程序中的数据超过其数据类型的范围,则会造成溢出,整数类型的溢出被称为整数溢出。
整数的表示范围以及字节数如下表:
溢出原理
整型溢出主要分为上界溢出和下界溢出,下面分别进行介绍。
上界溢出
先看代码:
# 伪代码 |
上界溢出有两种情况,一种是 0x7fff + 1
, 另一种是 0xffff + 1
。
因为计算机底层指令是不区分有符号和无符号的,数据都是以二进制形式存在 (编译器的层面才对有符号和无符号进行区分,产生不同的汇编指令)。
所以 add 0x7fff, 1 == 0x8000
,这种上界溢出对无符号整型就没有影响,但是在有符号短整型中,0x7fff
表示的是 32767
,但是 0x8000
表示的是 -32768
,用数学表达式来表示就是在有符号短整型中 32767+1 == -32768
。
第二种情况是 add 0xffff, 1
,这种情况需要考虑的是第一个操作数。
比如上面的有符号型加法的汇编代码是 add eax, 1
,因为 eax=0xffff
,所以 add eax, 1 == 0x10000
,但是无符号的汇编代码是对内存进行加法运算 add word ptr [rbp - 0x1a], 1 == 0x0000
。
在有符号的加法中,虽然 eax
的结果为 0x10000,但是只把 ax=0x0000
的值储存到了内存中,从结果看和无符号是一样的。
再从数字层面看看这种溢出的结果,在有符号短整型中,0xffff==-1,-1 + 1 == 0
,从有符号看这种计算没问题。
但是在无符号短整型中,0xffff == 65535, 65535 + 1 == 0
。
下界溢出
下届溢出的道理和上界溢出一样,在汇编代码中,只是把 add
替换成了 sub
。
一样也是有两种情况:
第一种是 sub 0x0000, 1 == 0xffff
,对于有符号来说 0 - 1 == -1
没问题,但是对于无符号来说就成了 0 - 1 == 65535
。
第二种是 sub 0x8000, 1 == 0x7fff
,对于无符号来说是 32768 - 1 == 32767
是正确的,但是对于有符号来说就变成了 -32768 - 1 = 32767
。
典例
常见的整型溢出可以总结为两种情况
未限制范围
这种漏洞由于对用户输入的数值未进行过滤检查而导致,例子如下:
$ cat test.c |
只申请 0x20
大小的堆,但是却能输入 0xffffffff
长度的数据,从整型溢出到堆溢出。
错误的类型转换
即使正确的对变量进行约束,也仍然有可能出现整数溢出漏洞,我认为可以概括为错误的类型转换,如果继续细分下去,可以分为:
- 1、范围大的变量赋值给范围小的变量
$ cat test2.c |
上述代码就是一个范围大的变量 (长整型 a),传入 check 函数后变为范围小的变量 (整型变量 n),造成整数溢出的例子。
已知长整型的占有 8 byte 的内存空间,而整型只有 4 byte 的内存空间,所以当 long -> int,将会造成截断,只把长整型的低 4byte 的值传给整型变量。
在上述例子中,就是把 long: 0x100000000 -> int: 0x00000000
。
但是当范围更小的变量就能完全的把值传递给范围更大的变量,而不会造成数据丢失。
- 2、只做了单边限制
这种情况只针对有符号类型:
$ cat test3.c |
从表面上看,我们对变量 len 进行了限制,但是仔细思考可以发现,len 是有符号整型,所以 len 的长度可以为负数,但是在 read 函数中,第三个参数的类型是 size_t
,该类型相当于 unsigned long int
,属于无符号长整型
上面举例的两种情况都有一个共性,就是函数的形参和实参的类型不同,所以我认为可以总结为错误的类型转换。
- Post Title: 整形溢出基础
- Post Author: ggb0n
- Post Link: http://ggb0n.cool/2020/06/17/整形溢出基础/
- Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
1.TCTF2020部分题解
2.第五空间pwn题练习
3.堆溢出-Tcache_Attack
4.堆溢出-Housese_Of_XXX
5.堆溢出基础
6.入坑二进制