整数溢出
CTFWiki:https://ctf-wiki.org/pwn/linux/user-mode/integeroverflow/introduction/
计算机并不能存储无限大的整数,计算机中的整数类型代表的数值只是自然数的一个子集。比如在32位C程序中,unsigned int类型的长度是32位,能表示的最大的数是0xffffffff。如果再加上1,变成了
0x100000000,其结果产生了溢出,按照汇编课上讲的那样,溢出标志位OF为1。其结果0x100000000就会超过32位能表示的范围,而只能截取其低32位,最终这个数字会变成0.这就是无符号的溢出。
计算机中有4种溢出情况,以32位整数为例:
-
无符号上溢
-
无符号下溢
-
有符号上溢
-
有符号下溢
除此之外,有符号数与无符号数直接的转换会导致整数大小突变。比如,有符号数字-1和无符号数字0xffffffff的二进制表示是相同的,二者直接进行转换会导致程序产生非预期的结果。
存储的角度看待”整数溢出“漏洞
浮点数,主要是涉及一个"无论多少位都无法精确表达”的问题,就是IEEE754中,浮点数设计的缺陷。在十进制中,唯有尾数是0或5的小数,才可以理论上被精确表示。浮点数,用一个三元组表示{Sign, Exponent, Significance}。其中,指数位(Exponent),主要是一-个移码的设计。当指数位为全0的时候,可以表示+进制数0。当指数位全1的时候,可以表示正/负无穷。此上述两者为特殊数值,故去掉后,码位减少了2个位置。在减少两个位置的情况下,将正数、负数,"均等” 地编码在这些码位上,则移码的偏移量为127。如果指数是3,则机器存储,为3+ 127=130;负数同理,亦是加上127,再进行机器存储。这个移码设计,保证了存储上,正数大于负数。
运算的角度看待“整数溢出”漏洞
如果连浮点数都能轻易理解,那么理解整数,整数上的模2群运算,应该不成问题。顶多涉及-些编码(负数挪位置)的问题。
显然,一个数(无符号数),加着加着,或者减着减着,或者去做乘法,它有可能溢出,跑到负数的编码位置上。如果程序设计不好,将这个"不符合预期理论设计"得出的数,当作一个有符号数(负数)进行处理, 那肯定出问题。
条件判断角度看待“整数溢出”漏洞
这个,涉及X86的汇编,就是OF标志位,和SF标志位。OF标志位,只对无符号数有意义,因为,正数加正数,不会出现一个负数(符号位为1);负数加负数,不会出现一个正数(符号位为0)。 至于, SF,就更简单了,记录了运算结果的符号位。SF,运算结果是正数,就是0;运算结果是负数, 就是1。X86的计算机,根据这两个标志位,就可以进行有符号数、无符号数的大小判断了。
整数溢出的利用
整数溢出虽然简单,但是利用不简单。整数溢出不像栈溢出等内存破坏可以直接通过覆盖内存进行利用,常常需要 进行一定转换才能溢出。常见的转换方式有两种。
整数溢出转换成缓冲区溢出
整数溢出可以将一个很小的数突变成很大的数。比如,无符号下溢可以将一个表示缓冲区大小的较小的数通过减法变成一个超大的整数。导致缓冲区的溢出。
另一种情况是通过输入负数的方法来绕过一些长度检查,如一些程序会使用有符号数字表示长度。那么就可以使用负数来绕过长度上限检查。而大多数系统API使用无符号数字来表示长度,此时负数就会变成超大的正数导致溢出。
整数溢出转数组越界
数组越界的思路很简单。在C语言中,数组索引的操作只是简单地将数组指针加上索引来实现,并不 会检查边界。因此,很大的索引会访问到数组后的数据,如果索引是负数,那么还会访问到数组之前的内存。