第2章 字符串

  • 格式:pdf
  • 大小:583.41 KB
  • 文档页数:12

1第2章字符串梁彬中国人民大学信息学院计算机系V 1.0

程序设计安全Secure Programming

2背景

•大部分终端用户与软件系统交互由各种字符串构成,而且还将越来越多(Web、XML…):

–命令行参数–环境变量–控制台输入–……•大量的软件漏洞源自字符串相关的问题:–字符串表示–字符串管理–字符串操作

3内容•字符串特征•常见字符串处理错误•进程内存组织•栈粉碎•代码注入•return-into-libc•缓解策略•小结42.1 字符串特征•字符串并不是C/C++语言的内建类型。C语言中,字符串由一个连续的字符序列组成,以一个空字符(null)标识结束。通常,用一个指向其的指针对字符串进行操作:

•C语言中支持以下字符串形式:–单字节字符串,1个字节表示1个字符char–多字节字符串,1个或多个字节表示1个字符char–宽字符字符串,多个字节(2、4)表示1个字符wchar_t

52.1 字符串特征•单字节字符串和多字节字符串都被描述为字节空字符终结的字节串NTBS(null-terminated byte string)–字符串长度为空字符前的字节个数•宽字符串是一个以空宽字符为终结符的宽字符序列–字符串长度为空宽字符前的宽字符个数62.1 字符串特征•C++中常使用标准模板类std::basic_string代表一个字符序列,支持序列操作和字符串操作•由字符类型进行初始化:–stringÅÆbasic_string–wstringÅÆbasic_string

•与NTBS相比,basic_string类较不容易导致安全漏洞,但在C/C++编程中不可避免地会使用到NTBS2

7作业•了解单字节字符串、宽字符串和多字节字符串等C/C++语言字符串表示方法,总结其原理、存储、操作、应用等特征。82.2 常见字符串处理错误•对于NBTS形式的字符串编程,很容易导致错误,最常见的有:–未限界字符串拷贝–off-by-one错误–Null-Termination错误–……

92.2.1 未限界字符串拷贝•未限界字符串拷贝(unbounded string copy)是从一个未限界的源拷贝数据至一个定长的数组。例如:1. intmain(void) {2. char Password[80];3. puts("Enter8 character password:");4. gets(Password);...5. }102.2.1 未限界字符串拷贝•函数strcpy和strcat执行字符串拷贝时不检查界限,非常容易导致错误

1. intmain(intargc, char *argv[]) {2. char name[2048];3. strcpy(name, argv[1]);4. strcat(name, " = ");5. strcat(name, argv[2]);...6. }

112.2.1 未限界字符串拷贝•一个简单的解决方案是测试输入长度并动态分配适当的内存1. intmain(intargc, char *argv[]) {2. char *buff = (char *)malloc(strlen(argv[1])+1);3. if (buff != NULL) {4. strcpy(buff, argv[1]);5. printf("argv[1] = %s.\n", buff);6. }7. else {/* Couldn't get the memory -recover */8. }9. return 0;10. }122.2.1 未限界字符串拷贝•C++中的cin输入也为一个未限界的源:1. #include 2. intmain(void) {3. char buf[12];

4. cin>> buf;5. cout<< "echo: " << buf<< endl;6. }3

132.2.1 未限界字符串拷贝•对于C++中的cin输入可设定最大输入长度1. #include 2. intmain(void) {3. char buf[12];4. cin.width(12);5. cin>> buf;6. cout<< "echo: " << buf<< endl;7. }142.2.2 off-by-one错误•off-by-one与未限界字符串拷贝类似,都涉及到越界写,主要是确定长度时考虑不当

1. intmain(intargc, char* argv[]) {2. char source[10];3. strcpy(source, "0123456789");4. char *dest= (char *)malloc(strlen(source));5. for (inti=1; i <= 11; i++) {6. dest[i] = source[i];7. }8. dest[i] = '\0';9. printf("dest= %s", dest);10. }

以上代码有些什么错误?

152.2.3 Null-Termination错误•Null-Termination错误涉及到不正确地处理空终结符,导致越界写1. intmain(intargc, char* argv[]) {2. char a[16];3. char b[16];4. char c[32];5. strcpy(a, "0123456789abcdef");6. strcpy(b, "0123456789abcdef");7. strcpy(c, a);8. strcat(c, b);9. printf("a= %s\n", a);10. return 0;11. }以上代码有些什么错误?162.2.4 非函数字符串操作错误•即使不使用危险的字符串操作函数,直接编码进行字符串操作,也会引入错误

1. intmain(intargc, char *argv[]) {2. inti = 0;3. char buff[128];4. char *arg1 = argv[1];

5. while (arg1[i] != '\0' ) {6. buff[i] = arg1[i];7. i++;8. }9. buff[i] = '\0';

10. printf("buff= %s\n", buff);11. }

以上代码有些什么错误?

172.2.5 字符串漏洞示例1. boolIsPasswordOkay(void) {2. char Password[12];3. gets(Password);4. if (!strcmp(Password, "goodpass"))5. return(true);6. else return(false);7. }8. void main(void) {9. boolPwStatus;10. puts("Enterpassword:");11. PwStatus= IsPasswordOkay();12. if (PwStatus== false) {13. puts("Accessdenied");14. exit(-1);15. }16. else puts("Accessgranted");17. }182.2.5 字符串漏洞示例•对于以上程序,当用户输入超过11个字符时,将会发生缓冲区溢出(Buffer Overflow)4

192.2.5 字符串漏洞示例•C/C++程序在向特定数据结构分配内存空间边界之外写入数据时,会发生缓冲区溢出,原因:–C/C++中将字符串定义为空字符为结尾的字符数组–未进行隐式的边界检查–某些标准的字符串函数调用未进行强制性的边界检查202.3 进程内存组织•各平台上进程内存组织示意:

212.3.1 栈管理•程序的栈(stack)用于存储以下信息保证程序的正常执行:–调用函数的返回地址–函数参数–局部变量•栈帧(frame):进程中每个函数被调用时在栈中压入的一段数据。在Intel架构下,当前帧的地址被存储在ebp寄存器中。在调用一个函数时,调用函数的帧地址也需要入栈222.3.1 栈管理

232.3.1 栈管理void foo(int, char *); //function prototypeintmain(intargc, char *argv[]) {intMyInt=1; // stack variable located at ebp-8char *MyStrPtr="MyString"; // stack varat ebp-4...foo(MyInt,MyStrPtr); // call foofunction//调用foo反汇编:1. moveax, [ebp-4]2. push eax3. movecx, [ebp-8]4. push ecx5. call foo6. add esp, 8...}242.3.1 栈管理•函数入口反汇编:void foo(inti, char *name) {char LocalChar[24];intLocalInt;1. push ebp2. movebp, esp3. sub esp, 28...

•函数返回反汇编:return;1. movesp, ebp2. pop ebp3. ret}