Strsafe.h:更安全的C语言字符串处理函数
原文出处:Strsafe.h: Safer String Handling in C 作者:Michael Howard 编译:王凌峰
在微软公司举行的Microsoft Windows Security Push 活动期间,一批测试者、程序管理经理和普通程序员共同决定要为 C 语言量身定制一套具有较高安全性的字符串处理函数,并且希望这些函数能被 Windows 程序员和微软公司内部的程序员所采用。
简单说来,现有的 C 语言运行时函数实在难以在当今充斥着恶意攻击企图的大环境下立足。这些函数要么在返回值和参数上缺乏一致性,要么隐含着所谓的“截断误差”(truncation errors) 错误,要么无法提供足够强大的功能。坦言之,调用这些函数的代码太容易产生“内存溢出”问题了。
我们发现,面向 C++ 程序员的类足以应付各种安全处理字符串的编程需要;他们能够选择 MFC 的Cstring 类、ATL 的CComBSTR 类 或者STL 的string 类,等等。然而,经典的 C 语言程序仍然普遍地存在,何况许多人正在把 C++ 当作 “改良的 C 语言” 来用,却把丰富的 C++ 类束之高阁。
其实只需要添加一行代码,你就能在 C 语言代码中调用安全性良好的 strsafe 系列函数了,详细请参阅:
《Using the Strsafe.h Functions》 这些新函数包含在一个头文件和一个函数库(可选)中,而后两者能在新版的 Platform SDK 中找到。对,就这么简单:
#include "strsafe.h"
还等什么呢!
再强调一次,对 strsafe 函数库的引用是可选的。
为了实现 strsafe 系列函数的目标,你的代码必须满足下列条件:
- 始终以 NULL 字符结束字符串。
- 始终检测目标缓冲区的长度。
- 始终用 HRESULT 语句产生统一的返回值。
- 兼顾 32 位与 64 位两种运行环境。
- 具有灵活性。
我们觉得,缺乏统一性是导致现有许多 C 语言字符串处理函数容易产生安全漏洞的根本原因,而 strsafe 系列函数所带来的高度统一性恰恰是解决此问题的一剂良药。然而,strsafe 也不是万能药。单纯依靠 strsafe 系列函数并不能保证代码的安全性和坚固性——你还必须开动你的大脑才行——然而这样对解决问题还是大有帮助的!
下面给出一段采用经典 C 语言运行时间函数的代码:
void UnsafeFunc(LPTSTR szPath,DWORD cchPath) {
TCHAR szCWD[MAX_PATH];
GetCurrentDirectory(ARRAYSIZE(szCWD), szCWD);
strncpy(szPath, szCWD, cchPath);
strncat(szPath, TEXT("\"), cchPath);
strncat(szPath, TEXT("desktop.ini"),cchPath);
}
以上代码中的 bug 随处可见 —— 它没有检查任何一个返回值,而且在对 strncat 函数的调用中也没有正确地使用 cchPath (因为MAX_PATH 中保存的是目标缓冲区内剩余空间的长度,而不是目标缓冲区的总长度)。于是,“内存溢出” 问题将会快找上门来。然而,象这样的代码片段早已泛滥成灾了。如果改用 strsafe 系列函数,那么以上代码应该变成:
bool SaferFunc(LPTSTR szPath,DWORD cchPath) {
TCHAR szCWD[MAX_PATH];
if (GetCurrentDirectory(ARRAYSIZE(szCWD), szCWD) &&
SUCCEEDED(StringCchCopy(szPath, cchPath, szCWD)) &&
SUCCEEDED(StringCchCat(szPath, cchPath, TEXT("\"))) &&
SUCCEEDED(StringCchCat(szPath, cchPath, TEXT("desktop.ini")))) {
return true;
}
return false;
}
这段代码不但检查了每一个返回值,还保证了适时传入同一目标缓冲区的总长度。你还可以采用 Ex 版本的 strsafe 系列函数来实现更加高级的功能,比如:
- 获取目标缓冲区的当前指针。
- 获取目标缓冲区的剩余空间长度。
- 以某个特定字符填充空闲缓冲区。
- 一旦字符串处理函数失败,就把用特定值填充字符串。
- 一旦字符串处理函数失败,就把目标缓冲区设成 NULL 。
如此改进后的代码性能又如何呢?告诉你一个好消息:它与原先的代码在性能上几乎没有差别。我曾在自己的 1.8 GHz 电脑上测试过混用经典 C 语言中各种字符串连接函数的代码、混用 strsafe 系列中各种字符串连接函数的代码和混用 Ex 版本 strsafe 系列中各种字符串连接函数的代码。它们各自独立运行一百万次(没错,就是 10,000,000 次)所消耗的时间分别为:
- 经典 C 语言 —— 7.3 秒
- Strsafe 系列—— 8.3 秒
- Strsafe 系列 (Ex 版) —— 11.1 秒
在测试中,调用 Ex 版本的 strsafe 系列函数的程序会在调用失败时把缓冲区设为 NULL ,并以 0xFE 作为填充字节,代码如下:
DWORD dwFlags = STRSAFE_NULL_ON_FAILURE | STRSAFE_FILL_BYTE(0xFE);
其中设置填充字节的代码耗时较多。事实上,如果这里仅仅把缓冲区设置为 NULL 的话,则采用 Ex 版本的 strsafe 系列函数的代码将会与采用普通的 strsafe 系列函数的代码耗时相同。
由此可见,以上三种方案的性能差异极小。我相信你也不会经常在一个程序中数百万次地反复执行包含大量字符串处理函数的代码吧!
还有一点值得引起注意:当你引用 strsafe 系列函数时,原有的 C 语言字符串处理函数都将被自动进行 #undef 处理。这也没问题,因为调试过程中的出错信息将会告诉你哪些函数已经被相应的 strsafe 系列函数取代了。好了,请放心地使用 strsafe.h 吧!更多相关信息请参阅 《Using the Strsafe.h Functions》。
Strsafe.h:更安全的C语言字符串处理函数的更多相关文章
- 转:C语言字符串操作函数 - strcpy、strcmp、strcat、反转、回文
转自:C语言字符串操作函数 - strcpy.strcmp.strcat.反转.回文 C++常用库函数atoi,itoa,strcpy,strcmp的实现 作者:jcsu C语言字符串操作函数 1. ...
- C语言字符串操作函数 - strcpy、strcmp、strcat、反转、回文
原文:http://www.cnblogs.com/JCSU/articles/1305401.html C语言字符串操作函数 1. 字符串反转 - strRev2. 字符串复制 - strcpy3. ...
- (C/C++学习)13.C语言字符串处理函数(一)
说明:字符串处理的函数很多,本文将例举经常遇到的一些函数加以说明. 一.字符串的输入输出 头文件:<stdio.h> 1.利用标准输出函数 printf() 来输出,将格式设置为 s% . ...
- C语言字符串操作函数整理
整理C语言字符串相关的函数,以程序方式验证,以注释方式做说明. #include<stdio.h> #include<string.h> #include<stdlib. ...
- C语言字符串处理函数
函数名: strcpy 功 能: 拷贝一个字符串到另一个 用 法: char *stpcpy(char *destin, char *source); 程序例: #include < ...
- C语言字符串操作函数
1.函数名: stpcpy 功 能: 拷贝一个字符串到另一个 用 法: char *stpcpy(char *destin, char *source); 程序例: #include < ...
- C语言 字符串处理函数 转自 http://blog.chinaunix.net/uid-25885064-id-3175049.html
C字符串处理函数 2012-04-13 18:14:16 分类: C/C++ void *memccpy (void *dest, const void *src, int c, size_t n) ...
- C++语言字符串处理函数
C++语言提供了比C语言更丰富的字符串处理功能.它可以在字符串上经行输入,输出,合并,修改,比较,转换,复制,搜索等操作.使用这些现成的功能可以大大减少我们的编程的负担. 输入和输出的字符串函数,如p ...
- C语言字符串操作函数总结
转载来源:https://blog.csdn.net/qq_33757398/article/details/81212618 字符串相关操作头文件:string.h 1.strcpy函数 原型:st ...
随机推荐
- 【转】WinForm窗体显示和窗体间传值
以前对WinForm窗体显示和窗体间传值了解不是很清楚 最近做了一些WinForm项目,把用到的相关知识整理如下 A.WinForm中窗体显示 显示窗体可以有以下2种方法: Form.ShowDial ...
- linux ssh 免密码登录的配置过程
# ssh-keygen -t rsa -C "自定义描述" -f ~/.ssh/自定义生成的rsa文件 # cd ./.ssh # touch config # 粘贴 Host ...
- Uboot流程分析
1. uboot的配置分析 1).配置入口分析 首先分析配置: 从make mx6dl_sabresd_android_config可知配置项,搜索Makefile: mx6solo_sabresd_ ...
- BeanPostProcessor接口
BeanPostProcessor接口及回调方法图 1.InstantiationAwareBeanPostProcessor:实例化Bean后置处理器(继承BeanPostProcessor) po ...
- WCF中的AddressHeader作用
客户端发送请求给服务端,服务端根据请求消息把消息转发给对应的终结点.这里面有个消息筛选机制,如果请求消息中带有地址报头相关信息,则会用地址报头匹配当前的所有终结点.所以默认情况下客户端和服务端的地址报 ...
- nodejs记录1——async函数
其实手动配置babel环境并不难,记录下步骤: 1.首先npm init创建一个nodejs项目 2.全局安装babel-cli处理工具:npm i babel-cli -g 3.cd到项目下安装ba ...
- 撩课-Web大前端每天5道面试题-Day15
1.请描述一下 cookies,sessionStorage 和 localStorage 的区别? cookie是网站为了标示用户身份而储存在用户本地终端(Client Side)上的数据(通常经过 ...
- eclipse中实现文本换行
Eclipse 使用系统内置的“ Text Editor ”做为文本编辑器,这个文本编辑器有一个问题,就是文本无法换行. 扩展插件 WordWrap 可以实现文本换行 安装方法: ...
- MyBatis 事务源码分析
先来看看在JAVA事务的相关技术,在JAVA中有两类事务,JDBC事务和JTA事务,如果是JDBC类型的事务,则是由Connection类来控制的.如果创建一个Connection对象时,没有显示调用 ...
- ps入门教程:photoshop工作界面
请大家安装好PS(这不是废话嘛……),然后将PS的界面熟悉一下,消除对PS的惧怕心理~~学会新建文件和保存文件,学会设置参考线. 安装完毕后,打开PS,就进入了PS的操作界面,我们来看一下[图1.1] ...