首页
社区
课程
招聘
[原创]strpcy_s安全函数不“安全”的说明
发表于: 2014-6-12 11:02 12127

[原创]strpcy_s安全函数不“安全”的说明

2014-6-12 11:02
12127
特此一贴,免得大家走弯路,如果有不正确,希望大家指正,并归纳,还大家一个真相,一个明白,给被误导者一个指引

在这贴子中,讨论带_s函数的安全性
http://bbs.pediy.com/showthread.php?p=1292546&posted=1#post1292546
发现有太多的人喜欢使用带_s的函数,为什么用他呢,因为msdn上面说:
Copy a string. These are versions of strcpy, wcscpy, _mbscpy with security enhancements as described in Security Enhancements in the CRT.

大概意思是讲,他们是安全函数,但是,他们真的安全吗?我觉得是安全,的确安全,但是对于经常混看雪的人来说,对vc库函数没有深入了解的人来说,我觉得他们并不是完全"安全"
当然,我说的不完全"安全",这里的“安全”,不是指溢出一类的安全,因为单就溢出层面来讲,他是绝对安全,真的绝对安全,
为什么我说他不“安全”呢,我是从编程角度出发,使用一个能让程序崩溃的函数,能叫安全函数吗?有可能导致整栋大楼垮塌的部件,能叫安全部件吗?大楼里面放一堆炸弹,黑客来了就炸,可以保证黑客进不来,但是不能保证大楼垮塌,或者被炸个洞,黑客更容易进来,从这点来说不带_s的函数,相比带_s的函数来说,从概率上来讲,不带_s的函数,不一定会让程序崩溃,而带_s的函数,在溢出情景下,一定会崩溃。就防入侵角度来讲,带_s的函数相当于炸弹,这个威力以及伤害力度,得由设计 人员来掌控,没有掌控好,后果很严重,最严重就是大楼垮塌,服务崩溃。

在那帖子中,我说得很明白了,可以还有人说,书上是这么讲的,好像有道理 ?
真的有道理吗?
对于strcmp_s函数我没有测试,只测试了strpcy_s

上传附加 ,大家调试一下,就知道了,

总结:
在非完全精通strpcy_s函数的程序手中,这个函数并非像msdn或者书中讲的那么“安全”,也不清楚为什么要弄这么个叫”安全函数“的”非安全“函数,国内的书,大部分是跟风,大部分写书的,仅仅是专业写书的,很少一部分是专业做程序的,很难得一种能追根究底的书,当然看雪上面的书不错,集无数大牛精华于一体,大部分是通过验证的。

代码贴上来

// strcpy_test.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "stdlib.h"
#include "windows.h"
#include "strsafe.h"
#include "string.h"

//测试时,请分别编译为debug和release两个版本测试


//1.测试strcpy,异常
void test_strcpy()
{

	char buffer[5];//缓冲区只有5字节,最多保存个字符

	strcpy( buffer,"123456789" );//堆栈溢出

	printf("%s\n",buffer);//必须要这一行,否则release下面这个函数会被优化掉
}

//2.测试strcpy_s,异常
void test_strcpy_s()
{

	char buffer[5];//缓冲区只有5字节,最多保存个字符

	strcpy_s( buffer,sizeof(buffer),"123456789" );//堆栈溢出

	printf("%s\n",buffer);//必须要这一行,否则release下面这个函数会被优化掉
}

//2.测试strcpy_s,不异常
void test_lstrcpyn()
{

	char buffer[5];//缓冲区只有5字节,最多保存个字符

	lstrcpynA( buffer,"123456789",sizeof(buffer) );//堆栈溢出

	printf("%s\n",buffer);//必须要这一行,否则release下面这个函数会被优化掉
}

int _tmain(int argc, _TCHAR* argv[])
{
	//test_strcpy();
	test_strcpy_s();
	test_lstrcpyn();
	return 0;
}



vs2008 sp1编译
strcpy_test.rar

[课程]Android-CTF解题方法汇总!

上传的附件:
收藏
免费 3
支持
分享
最新回复 (25)
雪    币: 2664
活跃值: (3401)
能力值: ( LV13,RANK:1760 )
在线值:
发帖
回帖
粉丝
2
Good job...
2014-6-12 11:05
0
雪    币: 185
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
使用新的增强安全的CRT函数有什么好处呢?简单地说,新的函数加强了对参数合法性的检查以及缓冲区边界的检查,如果发现错误,会返回errno或抛出异常。老版本的这些CRT函数则没有那么严格的检查与校验,如果错误地传输了参数或者缓冲区溢出,那么错误并不能被立刻发现,对于定位程序错误也带来更大困难。

简单的说,安全函数并不是说会帮你做截断的工作,strcpy 对应的截断版本是strncpy 。

strcpy_s 的作用是让错误直接暴露出来,也就是超出范围直接就挂掉, 防止缓冲区溢出利用。

在debug版本中加了断言, 可以方便的在调试阶段就暴露问题。
2014-6-12 11:08
0
雪    币: 138
活跃值: (460)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
安全函数会重新设置SEH ,导致外部无法捕获~ 直接 “崩~”弹出来给用户~ 一点都不友好;报错地址还不好定位。 还不如不用安全函数 生成minidump 呢;
2014-6-12 11:24
0
雪    币: 185
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
对~~ 这函数是个坑,直接用strncpy来得好。
2014-6-12 11:26
0
雪    币: 239
活跃值: (190)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
6
试试就知道哪不对了,如果应用到项目中,就知道后果了,它会带来前所未有点麻烦。
2014-6-12 11:38
0
雪    币: 60
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
memcpy最安全。
2014-6-12 12:38
0
雪    币: 49
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
再回复你下吧
错误越早暴露越好,暴露的越明显越好.
_s安全函数它不是让你的代码可以无异常的运行,而是让错误立即暴露,让你修改程序.它在万一你忘了检查或者截断数据的情况下,告诉你"黑,这里有问题".通过迫使你修改程序,从根本过上提高安全性,而不是默默代替你处理一切,毕竟_s函数不会知道这里的数据是否可以截断.
当然,这里有个假设,你会在程序发布前做充分的测试,这样才能暴露问题.
然后,万一你还是搞出了漏洞,入侵者来了,程序会奔溃,你觉的不安全了,
哪你和被入侵者成功得手相比呢?安全函数保证系统是安全的,数据是安全的,服务器不会被做成肉鸡,这有什么不好?
安全函数不是"智能保安",是"最终保险",在所有拦截都失效时的和敌人同归于尽的最后手段.
使用它,但不要依赖它.
2014-6-13 10:19
0
雪    币: 63
活跃值: (17)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
9
错误发现越早越好解决,*_s函数作用就在于此。当字符串越界时,不带s的函数可能不会崩溃,但是堆写坏了,堆写坏了,你的大楼一样会垮,而且估计垮的时候,还顺带把你一起杀死。另外strcmp是没有_s的版本,因为不需要。
2014-6-15 18:56
0
雪    币: 239
活跃值: (190)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
10
你们真的在使用strcpy_s吗?不怕程序挂?难道你们的程序都不是服务器程序?挂了意味着什么,大家都应该知道的吧
2014-6-15 20:25
0
雪    币: 63
活跃值: (17)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
11
服务程序要7*24小时运行的,一般必须会有重新拉起机制,和使用_s函数不矛盾。
2014-6-15 20:35
0
雪    币: 485
活跃值: (78)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
12
lstrcpyn的内部实现大概如下

一个for 循环COPY 字符串,当发现BUFFER的SIZE比SRC的字符小的时候,它会把BUFFER的最后一个字节强制填充为\0.以此保证BUFFER内容不被利用。

这函数完全可以自己写。

至于strcpy_s这个在DEBUG版本下的实现是先COPY,当发现BUFFER的SIZE比SRC的字符小的时候,此时函数内部会把BUFFER的第一个字符填充为\0,然后再写入0xFEFFFFFF,此时这个BUFFER完就被改写了,然后它再调用CRT的一个函数弹一个警告(也就是我们常说的断言),这个机制实现,很明显是为了防止 程序被栈溢出而被别人利用。而它的RELEASE版本下,当问题发生时,它只是把BUFFER的第一个字节填充为\0,然后再调用CRT的另一个函数(该函数是检察参数是否有效的),然后在这个函数调用完之后程序立即死亡。

从上面可以知道无论是DEBUG还是RELEASE其最主要的目的是为了防止 程序栈溢出而被利用。也就是LS上很多朋友说的 使问题提前暴露。而lstrcpyn完全就不会有这个特性。

如果让我建议的话我还是建议使用strcpy_s,这样问题才能暴露出来,程序的健壮性才能够得到保证。

PS:一些题外话,带S这种函数应该是以前M$被溢出怕了才出现的,所以说带S的安全也只是针对这种情况下才说的。

就LZ前面说的一大堆假设,服务器程序崩溃了,确实会引起很大的麻烦,不过这些和使用这几个函数确实没多大关系,现在还有人写的服务程序没有对应的容错机制吗?
2014-6-18 10:46
0
雪    币: 962
活跃值: (1696)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
服务程序数据被覆盖了更可怕。。。比如交易程序。。100覆盖成100W 。。。防溢出检查出错都是直接退出进程的。。
2014-6-18 16:21
0
雪    币: 261
活跃值: (51)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
要捕获异常的话,用_set_invalid_parameter_handler就可以。
我认为他并不是重置了SEH,而是在捕获函数里直接abort了
2014-6-19 13:02
0
雪    币: 138
活跃值: (460)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
15
这种方式的捕获,你也不方便定位问题函数具体行号;没有minidump

哥们是不是还有啥没说完~ 求指教!
2014-6-19 13:46
0
雪    币: 7068
活跃值: (3517)
能力值: ( LV12,RANK:340 )
在线值:
发帖
回帖
粉丝
16
个人认为:
安全函数就是在溢出的时候,弹出异常,而避免溢出后的问题。
好处是能及时发现漏洞,坏处是程序直接崩了。
如果仅仅为了不溢出,可以自己比较缓冲区大小,符合条件在调用"非安全"的函数。
2014-6-19 13:49
0
雪    币: 49
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
就是用了安全函数还是要自己判断缓冲区大小的,然后给出合适的处理,安全函数是在你万一忘记了检测,或者检测不得法的时候,给你最后的保障.
2014-6-30 09:10
0
雪    币: 90
活跃值: (80)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
18
都溢出了,你还希望他不要挂,我只能说你没调试过大型程序的bug吧
程序稳定是你逻辑上确保的,你自己代码写的不好程序老挂为什么要怪到这个函数头上?
2014-7-3 11:35
0
雪    币: 261
活跃值: (51)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
唔抱歉现在才看到……

void _invalid_parameter(
   const wchar_t * expression,
   const wchar_t * function,
   const wchar_t * file,
   int line,
   uintptr_t pReserved
);
以上这些参数在debug模式下都是有效的,当然minidump就没有了。。。
我试了一下hook abort 让他直接返回的话是可以正常运行下去的,逆了下abort没怎么看懂……我渣
2014-7-30 09:12
0
雪    币: 239
活跃值: (190)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
20
为毛不明白呢,难道是我理解错了?
比如聊天程序,你得限制聊天字串大小吧,或者用户名,你得限制长度吧,
客户端可以作限制,但是黑客可以你的协议,不经客户端直接发送到服务器,服务器你可以作检查,检查了,如果字符串过长,您直接丢弃,还是给后面截断了,再复制(strcpy_s)?不是多此一举吗?lstrcpyn可以复制时截断,不是很好吗?也不会溢出,
这不能严格叫溢出,叫截断溢出部分,溢出部分给他截断而已,
难道你收到一个比你规定长度的字符串,服务器就直接挂掉么?那还有什么稳定性而言,我是没有调试过大型 项目bug,但是我的程序,不会出现因为复制字符串而溢出的bug,我的团队,我也不允许他们这么干,对复制字符串的函数,我有严格规定的,能用哪些,不能用哪些,应该怎么使用等等,
而且我的项目,都得做溢出测试,绝对不允许程序掉挂的,即使我的服务器逻辑有异常,但是不能挂掉,一但挂掉,会影响其它用户业务,还有影响 其它服务器,
比如im服务,一但im服务器挂掉了,大量用户会重新连接,这样,即使im聊天服务器重新启动,因为大量用户同时连接,会再次请求其它服务,会给其它服务器,以及im服务器本身,带来大流量冲击,这种 冲击很麻烦,可能导致其它服务再次挂掉,就像骨牌一样,
这是我新身体会过的,以前我也使用strcpy_s,因为有一个地方,没有提前截断超长字符串,从而导致整个程序挂掉,大量用户挂线,然后断时间内,全部重新连接,相当 于一次CC攻击,然后等级服务器,以及头像 服务器,半小时内无响应,数据库服务器,一样受影响 。
然后IDC还发信息来说,服务器受攻击 ,要我处理,不处理给我停服务器。

您觉得这样的事,很好玩吗?

还有,你的服务器程序,难道允许 随时挂掉?然后再开一个监控进程,来重启 服务进程吗?
因为我的容灾处理,做得不好,所以,我不允许 服务器挂,即使逻辑出问题,
您有什么好的,方便的容灾处理方案,方便的话,请告诉我一下好吗?
这样,我就可以允许 我的服务器程序,随时挂掉了。
2014-7-30 10:06
0
雪    币: 90
活跃值: (80)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
21
渗透的时候最喜欢这种有异常也不会挂掉的了,被拿了shell用户信息全泄露你觉得更好看来
2014-7-30 16:15
0
雪    币: 239
活跃值: (190)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
22
lstrcpyn不会被渗透,如果lstrcpyn被渗透了,strcpy_s还能跑掉吗?不可能的
两者函数,复制字符串功能相同 ,只是一个挂掉,一个不挂而已.
2014-7-31 16:08
0
雪    币: 49
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
楼主还没搞明白,啥叫"最后保障",你自己做了严格的检查,那当然好,但万一你忘了呢?也许楼主水平高不会忘,但还有很多水平不那么高的人,他们会忘记检查.而安全函数的作用就是在万一忘记的情况下,防止损害扩大.
2014-7-31 16:21
0
雪    币: 239
活跃值: (190)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
24
lstrcpyn不需要你检查缓冲区长度,strcpy_s,既需要提供缓冲区长度,又需要你提前检查长度
2014-7-31 21:23
0
雪    币: 239
活跃值: (190)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
25
话说,lstrcpyn不会溢出,strcpy-s也不会溢出,但前者不会挂,后者会挂,用哪个,自己选吧
2014-7-31 21:32
0
游客
登录 | 注册 方可回帖
返回
//