原文出处: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语言字符串处理函数的更多相关文章

  1. 转:C语言字符串操作函数 - strcpy、strcmp、strcat、反转、回文

    转自:C语言字符串操作函数 - strcpy.strcmp.strcat.反转.回文 C++常用库函数atoi,itoa,strcpy,strcmp的实现 作者:jcsu C语言字符串操作函数 1. ...

  2. C语言字符串操作函数 - strcpy、strcmp、strcat、反转、回文

    原文:http://www.cnblogs.com/JCSU/articles/1305401.html C语言字符串操作函数 1. 字符串反转 - strRev2. 字符串复制 - strcpy3. ...

  3. (C/C++学习)13.C语言字符串处理函数(一)

    说明:字符串处理的函数很多,本文将例举经常遇到的一些函数加以说明. 一.字符串的输入输出 头文件:<stdio.h> 1.利用标准输出函数 printf() 来输出,将格式设置为 s% . ...

  4. C语言字符串操作函数整理

    整理C语言字符串相关的函数,以程序方式验证,以注释方式做说明. #include<stdio.h> #include<string.h> #include<stdlib. ...

  5. C语言字符串处理函数

    函数名: strcpy  功  能: 拷贝一个字符串到另一个  用  法: char *stpcpy(char *destin, char *source);  程序例:  #include < ...

  6. C语言字符串操作函数

    1.函数名: stpcpy  功  能: 拷贝一个字符串到另一个  用  法: char *stpcpy(char *destin, char *source);  程序例: #include < ...

  7. 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) ...

  8. C++语言字符串处理函数

    C++语言提供了比C语言更丰富的字符串处理功能.它可以在字符串上经行输入,输出,合并,修改,比较,转换,复制,搜索等操作.使用这些现成的功能可以大大减少我们的编程的负担. 输入和输出的字符串函数,如p ...

  9. C语言字符串操作函数总结

    转载来源:https://blog.csdn.net/qq_33757398/article/details/81212618 字符串相关操作头文件:string.h 1.strcpy函数 原型:st ...

随机推荐

  1. Java责任链模式

    责任链模式 顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链.这种模式给予请求的类型,对请求的发送者和接收者进行解耦.这种类型的设计 ...

  2. js禁止微信浏览器下拉显示黑底查看网址,不影响内部Scroll

    开发项目跑在微信浏览器经常会遇到一个问题,微信浏览器下拉的时候会出现自带的黑色底色(显示网址)如下图: 网上好多js禁止操作的做法禁止了内部Scroll,导致页面不能滚动,上拉加载失效,例如这种做法: ...

  3. JAVA练手--链表

    package tet; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; impo ...

  4. IOS学习资源汇总

    昨天夜里在简书看到关于ios学习资源总结的文章,在这分享给大家. http://www.jianshu.com/p/b7c4a787a597?utm_campaign=hugo&utm_med ...

  5. 方法返回多个值参数Out使用的方法

    string str; Console.WriteLine("请输入用户名"); string user = Console.ReadLine().ToString(); Cons ...

  6. C# AJAXform上传图片

     前台: @{    Layout = null;} <!DOCTYPE html> <html><head>    <meta name="vie ...

  7. JS 教程

    1简介 1.JavaScript 是脚本语言 1.1轻量级的编程语言 1.2可插入 HTML 页面的编程代码 1.3可由所有的现代浏览器执行 2.您将学到什么 2.1JavaScript:写入 HTM ...

  8. C# 文件上传 制作水印

    其实C#的文件上传是非常简单的 前台代码 <asp:FileUpload ID="FileUpload1" accept=".jpg,.png,.jpeg" ...

  9. [javaSE] 数据结构(栈)

    栈(stack)是一种线性存储结构,有以下特点: 1.栈中数据是按照先进后出的方式进出栈的 2.向栈中添加删除元素时,只能从栈顶进行操作 使用数组实现栈 定义一个类ArrayStack 实现入栈方法p ...

  10. C++ 语法积累20161015

    1.break 作用:用于终止当前循环(跳出循环体). 遇到最多的应该是在双层循环体中的使用: (1)在内循环体中,遇到break,则直接跳出内循环体,再次执行外循环体. (2) 在外循环体中,bre ...