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》

原文地址:https://www.cnblogs.com/tewuapple/p/8436374.html

时间: 2024-08-02 11:01:59

Strsafe.h:更安全的C语言字符串处理函数的相关文章

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

转自:C语言字符串操作函数 - strcpy.strcmp.strcat.反转.回文 作者:jcsu C语言字符串操作函数 1. 字符串反转 - strRev2. 字符串复制 - strcpy3. 字符串转化为整数 - atoi4. 字符串求长 - strlen5. 字符串连接 - strcat6. 字符串比较 - strcmp7. 计算字符串中的元音字符个数8. 判断一个字符串是否是回文1. 写一个函数实现字符串反转 版本1 - while版 void strRev(char *s){    

C语言字符串处理函数

函数名: strcpy 功  能: 拷贝一个字符串到另一个 用  法: char *stpcpy(char *destin, char *source); 程序例: #include <stdio.h> #include <string.h> int main(void) {    char string[10];    char *str1 = "abcdefghi";    stpcpy(string, str1);    printf("%sn&

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

#include<stdio.h> #include<string.h> #include<stdlib.h> int main() {     char *str1="hello world!";     char *str2="HELLO WORLD!";     int len=strlen(str1);//求的字符串长度,不包括'\0'在内     printf("len=%d\n",len);    

c语言字符串分割函数(strtok())

头文件:#include <string.h> 定义函数:char * strtok(char *s, const char *delim); 函数说明:strtok()用来将字符串分割成一个个片段.参数s 指向欲分割的字符串,参数delim 则为分割字符串,当strtok()在参数s 的字符串中发现到参数delim 的分割字符时则会将该字符改为\0 字符.在第一次调用时,strtok()必需给予参数s 字符串,往后的调用则将参数s 设置成NULL.每次调用成功则返回下一个分割后的字符串指针.

C语言字符串操作函数实现

1.字符串反转 – strRev void strRev(char *str) { assert(NULL != str);   int length=strlen(str); char *end=str+length-1; while(end > str) { *str=(*str)^(*end); *end=(*str)^(*end); *str=(*str)^(*end); end--; str++; } } 2.字符串复制 – strcpy char *strcpy(char *strD

C 语言字符串处理函数实现合集

其中包括:strcpy(char *, char *),strncpy();strcat();strncat();strcmp();strncmp();strchr();strrchr();strlen(); strdup();strspn();strpbrk();strtok(); memset();memcpy();memmove();memcmp();memscan();strstr(); _strupr_();_strlen_();_strsplit_();_hex2dec_();_st

C语言-字符串操作函数

gets(char buffer[]) 从标准输入读取一行, 并去掉换行符, 在字符串末尾增加 '\0' 字符, 写入到缓冲区 成功则返回 buffer 的地址, 出错或者遇到文件结尾则返回空指针, 使用 stdio 的  NULL 表示 fgets(char buffer[], int num, FILE * f) 从指定文件 f 中读取 num - 1 个字符, 去掉换行符, 并在末尾添加 '\0' 字符, 写入到 buffer scanf(const char * format [, ar

C语言字符串操作函数

来源:http://www.cnblogs.com/JCSU/articles/1305401.html 1. 字符串反转 - strRev2. 字符串复制 - strcpy3. 字符串转化为整数 - atoi4. 字符串求长 - strlen 5. 字符串连接 - strcat6. 字符串比较 - strcmp 7. 计算字符串中的元音字符个数 8. 判断一个字符串是否是回文 1. 写一个函数实现字符串反转 版本1 - while版 void strRev(char *s) {     cha

R语言字符串处理函数之——字符串的大小写转换

toupper 转化为大写字母 toupper(x=c("apple", "I like your style")) [1] "APPLE" "I LIKE YOUR STYLE" tolower 转化为小写字母 tolower(x="APPLE") [1] "apple"