整数溢出
来源:互联网 发布:java图解教程 pdf 编辑:程序博客网 时间:2024/05/16 09:08
什么情况下会出现整数溢出呢?
由于整数在内存里面保存在一个固定长度 (在本章中使用32位)的空间内,它能存储的最大值就是固定的,当尝试去存储一个数,而这个数又大于这个固定的最大值时,将会导致整数溢出。
举个例子,有两个无符号的整数,num1和num2,两个数都是32位长,首先赋值给num1 一个32位整数的最大值,num2被赋值为1。然后让num1和num2相加,然后存储结果到第3个无符号32位的整数num3,代码如下:
- num1 = 0xFFFFFFFF;
- num2 = 0x00000001;
- num3 = num1 + num2;
很显然,num1的值是:11111111 11111111 11111111 11111111;
num2的值是:00000000 00000000 00000000 00000001;
两者相加,得到结果为:00000000 00000000 00000000 00000000。因此,num3中的值是0,发生了整数溢出。
此时,如果一个整数用来计算一些敏感数值,如缓冲区大小或数组索引,就会产生潜在的危险。
不过,并不是所有的整数溢出都可以被利用,毕竟,整数溢出并没有改写额外的内存;但是,在有些情况下,整数溢出将会导致"不能确定的行为",由于整数溢出出现之后,很难被被立即察觉,比较难用一个有效的办法去判断是否出现或者可能出现整数溢出。
就发现的难度而言,和缓冲区溢出相比,整数溢出更加难被发现。因此,即使是审核过的代码,有时候也难以幸免。
综上所述,一言以蔽之,整数溢出是尝试存储一个很大的数到一个变量中,由于这个变量能够存储的数值范围太小,不足以存储那个很大的数,造成溢出。下面用最简单的程序来说明这个问题(使用的环境是DEV C++,用户也可以使用TurboC,稍有不同):
P02_08.c
- #include <stdio.h>
- int main(void)
- {
- int l;
- short s;
- char c;
- l = 0xdeadbeef;
- s = l;
- c = l;
- printf("l=0x%x(%d bytes)/n", l, sizeof(l));
- printf("s=0x%x(%d bytes)/n", s, sizeof(s));
- printf("c=0x%x(%d bytes)/n", c, sizeof(c));
- return 0;
- }
生成可执行文件,运行,显示:
如前所述,整数溢出并不像普通的漏洞类型,一般不会允许直接改写内存。但是一个精巧的设计可以直接改变程序的控制流程,程序员此时很难有办法在进程里面检查计算发生后的结果,带给用户的感觉就是:计算结果和正确结果之间,有一定的偏差。此种攻击一般的方法是:攻击者强迫一个变量包含错误的值,从而在后面的代码中出现问题。看下面的例子:
P02_09.c
- #include <stdio.h>
- #include <string.h>
- int main(int argc, char *argv[])
- {
- unsigned short s;
- int i;
- char buf[100];
- i = atoi(argv[1]);
- s = i;
- if(s >= 100)
- {
- printf("拷贝字节数太大,请退出!/n");
- return -1;
- }
- memcpy(buf, argv[2], i);
- buf[i] = '/0';
- printf("成功拷贝%d个字节/n", i);
- return 0;
- }
该程序的作用是将argv[2]的内容拷贝到buf中,由argv[1]指定拷贝的字节数,在程序中,进行了相对严格的大小检查:如果argv[1]的值大于等于buf数组的大小(100),则不进行拷贝。
生成可执行文件,运行:
完全正常。运行:
显示 该程序看似正常,但是输入:
程序显示:
该程序中,程序从命令行参数中得到一个整数值存放在整形变量i当中,然后这个值被赋予unsigned short类型的整数s,由于s在内存中是用16位进行存储,16位能够存储的最大数是:
- 11111111 11111111
即:十进制的65535,因此,unsigned short存储的范围是0 - 65535,如果这个值大于unsigned short类型所能够存储的值65535,它将被截断。因此,输入65536,系统会将其认为0。因为65536的二进制是:
- 1 00000000 00000000
系统只取后面16位进行存储。
实际上,整数溢出的危害还在于能够产生"并发攻击",类似于医学中的"并发症"。以上面的例子为例,程序绕过代码中的大小判断部分的边界检测,又可以导致缓冲区溢出,只要使用一般的栈溢出技术就能够利用这个溢出程序。
下面的例子列举了另外几个出现问题的运算:
P02_10.c
- #include <stdio.h>
- int main(int argc, char *argv[])
- {
- int n1 = 0x7fffffff;
- int n2 = 0x40000000;
- int n6 = 0x8fffffff;
- printf("%d(0x%x)+1=%d(0x%x)/n", n1, n1, n1+1, n1+1);
- printf("%d(0x%x)+%d(0x%x)=%d(0x%x)/n",
n2, n2, n2, n2, n2+n2, n2+n2);- printf("%d(0x%x)*4=%d(0x%x)/n", n2, n2, n2*4, n2*4);
- printf("%d(0x%x)-%d(0x%x)=%d(0x%x)/n",
n2, n2, n6, n6, n2-n6, n2-n6);- return 0;
- }
生成可执行文件,运行,得到如下结果:
以上显示的基本上是可以被攻击者利用的一些运算。很显然,这些不正常的结果都是由于整数溢出引起的,读者可以自己分析其原理。
整数溢出还有可能在动态分配内存时被利用。看如下代码:
P02_11.c
- #include <stdio.h>
- #include <stdlib.h>
- int* arraycpy(int *array, int len)
- {
- int *newarray, i;
- newarray = malloc(len*sizeof(int));
- printf("为newarray成功分配%d字节内存/n",len*sizeof(int));
- if(newarray == NULL)
- {
- return -1;
- }
- printf("循环运行次数:%d(0x%x)/n",len,len);
- for(i = 0; i < len; i++)
- {
- newarray[i] = array[i];
- }
- return newarray;
- }
- int main(int argc, char *argv[]){
- int array[] = {1,2,3,4,5};
- arraycpy(array,atoi(argv[1]));
- return 0;
- }
该代码将array拷贝到newarray中,生成exe文件,运行:
显示:看似没有问题。但是如果输入下面的命令:
我们知道,1073741824的16进制是0x40000000,从前一个例子可以看出,0x40000000*4=0x0。因此,屏幕上显示:
很显然,这个看起来没有问题的函数,可能出现在没有为newarray分配内存的情况下,却向其里面拷贝数组元素,循环的次数还非常多,严重时造成系统崩溃。攻击者通过选择一个合适的值给len可以使得循环反复执行导致缓冲区溢出。
还有一种情况,通过改写malloc的控制结构,也能够在正常的函数运行的过程中插入其他可执行恶意代码。
P02_12.c
- #include <stdio.h>
- #include <stdlib.h>
- int catstring(char *buf1, char *buf2, int len1, int len2)
- {
- char mybuf[256];
- if((len1 + len2) > 256)
- {
- printf("超出mybuf容纳范围!/n");
- return -1;
- }
- memcpy(mybuf, buf1, len1);
- memcpy(mybuf + len1, buf2, len2);
- printf("复制%d+%d=%d个字节到mybuf!/n",len1,len2,len1+len2);
- return 0;
- }
- int main(int argc, char *argv[])
- {
- catstring(argv[1],argv[2],atoi(argv[3]),atoi(argv[4]));
- return 0;
- }
该例子看起来无懈可击,并且进行了len1和len2相加之后的检查,输入:
显示:
输入:
显示:
这看起来正常,但是如果输入:
2147483647的16进制为0x7FFFFFFF。该运行结果为:
程序崩溃。根本不会显示:"超出mybuf容纳范围!"。是什么原因?请读者自己分析。
整数溢出是非常危险的,部分原因是因为它在发生后不可能被发现,也就是说,一个整数溢出发生了,应用程序并不知道它的计算是错误的。因此应用程序在假定它是正确的情况下,会继续运行下去。在安全的系统中,这种结果具有巨大的危害,有时甚至能够造成系统崩溃。
解决整数溢出的方案,主要是编程之前必须进行详细的预测,多考虑一些问题, 在编程时将各种问题考虑到并且进行相应的处理。如:
充分考虑各种数据的取值范围,使用合适的数据类型;
尽量不要在不同范围的数据类型之间进行赋值;等等。
- 整数溢出
- 整数溢出
- 整数溢出
- 整数溢出
- 整数溢出
- 整数溢出
- 整数溢出
- 整数溢出
- 整数溢出
- 整数溢出,如何判断整数溢出
- 整数溢出,如何判断整数溢出
- 整数溢出,如何判断整数溢出
- 整数溢出,如何判断整数溢出
- 整数溢出的问题
- 浅析整数溢出
- 整数加减溢出
- 整数溢出漏洞攻击
- JAVA 整数溢出问题
- 亚夏汽车首发申请通过 成第2家IPO经销商_135
- 人民币利率互换小幅上行,通胀不乐观致紧缩预期趋浓_183
- WIN下如何在PHP扩展里打开openssl支持?
- 人民币升值出口成本反降_114
- 今年内地首富“A股造” 梁稳根500亿成第一富豪_190
- 整数溢出
- 薰衣草,凋落的季节!
- 麦子成熟时 我来到这个世界
- 变量的存储与作用域
- 代码库的魅力
- 断线风筝
- 月光如水水如天
- 指针的一些规则
- 最美人间四月天