美文网首页程序员C语言C语言学习
C语言中的一个大恶魔之溢出问题,“野兽出没”,小心小心再小心

C语言中的一个大恶魔之溢出问题,“野兽出没”,小心小心再小心

作者: C语言基础 | 来源:发表于2018-07-25 16:02 被阅读37次

整型溢出有点老生常谈了,但似乎没有引起多少人的重视。

C/C++学习资料。C/C++深度学习 :QQ群:747821062

整型溢出会有可能导致缓冲区溢出,缓冲区溢出会导致各种黑客攻击,比如最近OpenSSL的heartbleed事件,就是一个buffer overread的事件。在这里写下这篇文章,希望大家都了解一下整型溢出,编译器的行为,以及如何防范,以写出更安全的代码。

什么是整型溢出

C语言的整型问题相信大家并不陌生了。对于整型溢出,分为无符号整型溢出和有符号整型溢出。

对于unsigned整型溢出,C的规范是有定义的——“溢出后的数会以2^(8*sizeof(type))作模运算”,也就是说,如果一个unsigned char(1字符,8bits)溢出了,会把溢出的值与256求模。例如:

unsigned char x = 0xff;

printf("%d\n", ++x);

上面的代码会输出:0 (因为0xff + 1是256,与2^8求模后就是0)

对于signed整型的溢出,C的规范定义是“undefined behavior”,也就是说,编译器爱怎么实现就怎么实现。对于大多数编译器来说,算得啥就是啥。比如:

signed char x =0x7f; //注:0xff就是-1了,因为最高位是1也就是负数了

printf("%d\n", ++x);

上面的代码会输出:-128,因为0x7f + 0x01得到0x80,也就是二进制的1000 0000,符号位为1,负数,后面为全0,就是负的最小数,即-128。

另外,千万别以为signed整型溢出就是负数,这个是不定的。比如:

signed char x = 0x7f;

signed char y = 0x05;

signed char r = x * y;

printf("%d\n", r);

上面的代码会输出:123

相信对于这些大家不会陌生了。

整型溢出的危害

下面说一下,整型溢出的危害。

示例一:整形溢出导致死循环

short len = 0;

while(len< MAX_LEN) {

len += readFromInput(fd, buf);

buf += len;

}

C/C++学习资料。C/C++深度学习 :QQ群:747821062

上面这段代码可能是很多程序员都喜欢写的代码(我在很多代码里看到过多次),其中的MAX_LEN 可能会是个比较大的整型,比如32767,我们知道short是16bits,取值范围是-32768 到 32767 之间。但是,上面的while循环代码有可能会造成整型溢出,而len又是个有符号的整型,所以可能会成负数,导致不断地死循环。

示例二:整形转型时的溢出

int copy_something(char *buf, int len)

{

#define MAX_LEN 256

char mybuf[MAX_LEN];

if(len > MAX_LEN){ // <---- [1]

return -1;

}

return memcpy(mybuf, buf, len);

}

上面这个例子中,还是[1]处的if语句,看上去没有会问题,但是len是个signed int,而memcpy则需一个size_t的len,也就是一个unsigned 类型。于是,len会被提升为unsigned,此时,如果我们给len传一个负数,会通过了if的检查,但在memcpy里会被提升为一个正数,于是我们的mybuf就是overflow了。这个会导致mybuf缓冲区后面的数据被重写。

示例三:分配内存

关于整数溢出导致堆溢出的很典型的例子是,OpenSSH Challenge-Response SKEY/BSD_AUTH 远程缓冲区溢出漏洞。下面这段有问题的代码摘自OpenSSH的代码中的auth2-chall.c中的input_userauth_info_response() 函数:

nresp = packet_get_int();

if (nresp > 0) {

response = xmalloc(nresp*sizeof(char*));

for (i = 0; i < nresp; i++)

response[i] = packet_get_string(NULL);

}

上面这个代码中,nresp是size_t类型(size_t一般就是unsigned int/long int),这个示例是一个解数据包的示例,一般来说,数据包中都会有一个len,然后后面是data。如果我们精心准备一个len,比如:1073741825(在32位系统上,指针占4个字节,unsigned int的最大值是0xffffffff,我们只要提供0xffffffff/4 的值——0x40000000,这里我们设置了0x4000000 + 1), nresp就会读到这个值,然后nresp*sizeof(char*)就成了 1073741825 * 4,于是溢出,结果成为了 0x100000004,然后求模,得到4。于是,malloc(4),于是后面的for循环1073741825 次,就可以干环事了(经过0x40000001的循环,用户的数据早已覆盖了xmalloc原先分配的4字节的空间以及后面的数据,包括程序代码,函数指针,于是就可以改写程序逻辑。关于更多的东西,你可以看一下这篇文章《Survey of Protections from Buffer-Overflow Attacks》)。

示例四:缓冲区溢出导致安全问题

C/C++学习资料。C/C++深度学习 :QQ群:747821062

int func(char *buf1, unsigned int len1,

char *buf2, unsigned int len2 )

{

char mybuf[256];

if((len1 + len2) > 256){ //<--- [1]

return -1;

}

memcpy(mybuf, buf1, len1);

memcpy(mybuf + len1, buf2, len2);

do_some_stuff(mybuf);

return 0;

}

上面这个例子本来是想把buf1和buf2的内容copy到mybuf里,其中怕len1 + len2超过256 还做了判断,但是,如果len1+len2溢出了,根据unsigned的特性,其会与2^32求模,所以,基本上来说,上面代码中的[1]处有可能为假的。(注:通常来说,在这种情况下,如果你开启-O代码优化选项,那个if语句块就全部被和谐掉了——被编译器给删除了)比如,你可以测试一下 len1=0x104, len2 = 0xfffffffc 的情况。

示例五:size_t 的溢出

for (int i= strlen(s)-1; i>=0; i--) { ... }

for (int i=v.size()-1; i>=0; i--) { ... }

上面这两个示例是我们经常用的从尾部遍历一个数组的for循环。第一个是字符串,第二个是C++中的vector容器。strlen()和vector::size()返回的都是 size_t,size_t在32位系统下就是一个unsigned int。你想想,如果strlen(s)和v.size() 都是0呢?这个循环会成为个什么情况?于是strlen(s) – 1 和 v.size() – 1 都不会成为 -1,而是成为了 (unsigned int)(-1),一个正的最大数。导致你的程序越界访问。

这样的例子有很多很多,这些整型溢出的问题如果在关键的地方,尤其是在搭配有用户输入的地方,如果被黑客利用了,就会导致很严重的安全问题。

C语言中的一个大恶魔—— Undefined! 这里都是“野兽出没”的地方,你一定要小心小心再小心

其它

C/C++学习资料。C/C++深度学习 :QQ群:747821062

对于C++来说,你应该使用STL中的numeric_limits::max() 来检查溢出。

可见,写一个安全的代码并不容易,尤其对于C/C++来说。对于黑客来说,他们只需要搜一下开源软件中代码有memcpy/strcpy之类的地方,然后看一看其周边的代码,是否可以通过用户的输入来影响,如果有的话,你就惨了。

最后, 不好意思,这篇文章可能罗嗦了一些,大家见谅。C/C++深度学习 私信我 “代码” 获取资料。

相关文章

  • C语言中的一个大恶魔之溢出问题,“野兽出没”,小心小心再小心

    整型溢出有点老生常谈了,但似乎没有引起多少人的重视。 整型溢出会有可能导致缓冲区溢出,缓冲区溢出会导致各种黑客攻击...

  • 小心,小心,再小心

    人在家中坐,祸从天上来。 昨天收到一个朋友发来的一个公众号二维码。说是让帮忙关注一下。 我以为是她自己的公众号,就...

  • 小心小心再小心

    开车的朋友一定要注意安全,一定要看清楚交通的标志和标线,严守交通规则,防患于未然。 今天开车去办事情,到了地点,却...

  • 小心再小心

    今天遇到两个上门推销净水器的人,被姨夫赶走后,我细思极恐,这年头卖东西的人也太会耍油头了。 先是套近乎,因为我和表...

  • 开车,小心小心再小心

    2021年3月9日 星期二 晚上下班,开车回家。红灯停,绿灯行。 我在一个十字路口停车等红灯,我占的左拐道直行,绿...

  • 租车要小心小心再小心

    现在出门在外,很多人喜欢租个车自由行,小张也不例外,手机点一点,很方便就租了一辆车。结果天有不测风云,人有旦...

  • 小心,小我出没

    分身术第一课觉察你的语言 我做不到,我没有办法。 都怪XXX,要不是他的话我怎么会被骂,又不关我的事。 反正这样了...

  • 小心,有神出没

    2017年是个好运年 江湖中发现了一个叫铁哥的汉子 他把一群奇怪的人聚在一起 说啊教啊练啊 练啊教啊说啊 一个个都...

  • 小心!猴出没

    小心!猴出没 这是我游黄山记忆最深的惊魂一刻。 那一年趁着春节后的旅游淡季,我们一家决定去爬黄山。到达山脚时已接近...

  • 蛇出没 小心

    文.图/英歌h 晚饭后刚走出大门,雨点滴滴答答,王兄说:“我们回去在小区走走吧!” 他把我往通向围墙的一条路引,我...

网友评论

    本文标题:C语言中的一个大恶魔之溢出问题,“野兽出没”,小心小心再小心

    本文链接:https://www.haomeiwen.com/subject/zmopmftx.html