知识大全 缓冲区溢出还是问题吗

Posted 字符

篇首语:读书志在圣贤,为官心存君国。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 缓冲区溢出还是问题吗相关的知识,希望对你有一定的参考价值。

缓冲区溢出还是问题吗?C++/CLI安全编码  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

C++/CLI是对C++的一个扩展 其对所有类型 包括标准C++类 都添加了对属性 事件 垃圾回收 及泛型的支持   Visual C++ 扩展了对使用C++/CLI(通用语言基础结构)开发运行于带有垃圾回收的虚拟机上的控件及应用程序的支持 而C++/CLI是对C++编程语言的一个扩展 其对所有类型 包括标准C++类 都添加了如属性 事件 垃圾回收 及泛型等特性   Visual C++ 支持 NET Framework通用语言运行时库(CLR) 其是垃圾回收虚拟机Microsoft的实现 Visual C++ 对 NET编程的C++语法支持是从Visual C++ NET 中引入的托管扩展C++演化而来的 托管扩展C++仍然被支持 但在倾向于新语法的情况下已不赞成使用 Visual C++ 同时也对本地编程添加了新的特性 包括 位处理器架构支持 及提高了安全性的新库函数   在本文中 将主要讲解在以最小代价把现有老系统移植到使用CLR的新环境中来时 所要面临的问题 目的是为了确定这些程序是否仍然易受折磨C/C++程序多年的缓冲区溢出的影响   例 会要求用户输入用户名及密码 除去用户名之外 程序只接受 NCC 为有效的密码 如果用户输入了错误的密码 程序将退出 (这个程序只是作为C++/CLI代码的漏洞测试 而不是演示如何处理密码 ) 例 #include <stdlib h> #include <stdio h> #include <windows h> char buff[ ]; struct user char *name; size_t len; int uid; ; bool checkpassword()  char password[ ];  puts( Enter character password: );  gets(password);  if (strcmp(password NCC ) == )   return true;    else   return false;   int main(int argc char *argv[])  struct user *userP = (struct user *) xcdcdcdcd;  size_t userNameLen = xdeadbeef;  userP = (struct user *)malloc(sizeof(user));  puts( Enter user name: );  gets(buff);  if (!checkpassword())   userNameLen = strlen(buff) + ;   userP >len = userNameLen;   userP >name = (char *)malloc(userNameLen);   strcpy(userP >name buff); // log failed login attempt   exit( );  

  程序从 行的main()开始执行 在 及 行使用了一对puts()和gets()来提示输入用户名 导致了一个从标准输入到缓冲区字符数组(声明在第 行)的不受控制的字符串复制 程序中的这两处地方都有可能会导致一个缓冲区溢出的漏洞 checkpassword()函数由main()中的 行调用 并在 及 行中提示用户输入密码 这也是使用了一对puts()/gets() 对gets()的第二次调用也会导致一个定义在堆栈上的密码字符数组缓冲区溢出   程序使用Microsoft Visual C++ 编译 并关闭了缓冲区安全检查选项(/GS ) 打开了托管扩展(/clr) 默认情况下 缓冲区安全检查是打开的 把它关闭并不是个好做法(如本例所示) 而/clr选项可允许由托管及非托管代码生成混合的程序集

  程序生成过程中产生的几个警告信息都可以忽略掉 例如 warning C : gets was declared deprecated 和 warning C : strcpy was declared deprecated 编译器推荐使用gets_s()来代替gets() 用strcpy_s()来代替strcpy() 如果完全使用这些替代函数 那么就可消除缓冲区溢出潜在的可能性 然而 这些只是警告信息 可以忽略甚至关闭 忽略这些警告信息是符合用最小的代价移植现有老系统这个前提的   当使用托管扩展时 编译器会为main()及checkpassword()函数生成Microsoft媒介语言(MSIL或称为通用媒介语言CIL) CIL字节码会被打包进一个可执行文件 在调用即时编译器(JIT)将其翻译为本地程序集指令后 接着把控制权交给main()   程序运行时 提示用户输入用户名 Enter user name:rcs  接着程序要求用户输入密码 其被读入到声明在 行上的 个字符数组这个变量中 在插 中 如果在密码从标准输入读取之前 查看堆栈上的数组地址起始处的数据(本例中为 x DF D ) 将会看到分配给密码的存储空间(以黑体字标出)及堆栈上的返回地址(以红色字标出) 返回地址在此为小尾字节序(Little Endian)   代码段 堆栈上数组地址起始处的数据 DF D f d a b e y cT DF E f d f f a a e y:N DF F a b f f d da c fc f d +/ yx DF f d H ` @ PST   倘若输入了更多的字符 以致密码字符数组存储空间无法容纳 一个攻击者就可以溢出此缓冲区 并以shellcode(可为任意的代码)地址覆蓋掉返回地址 出于演示的目的 在此假定shellcode已被注入 且定位于 x 为执行此代码 攻击者只需把下列字符串作为密码输入 Enter character password: |@  这个输入的字符串被复制到密码字符数组 溢出了此缓冲区并覆蓋相应的内存包括返回地址 字符串中的三个字符 |@覆蓋了返回地址的前三个字节 而返回地址的最后一个字节被一个由gets()函数产生的null结尾字符所覆蓋 注意 如果这个null不在最后一个字节上 那么不可能复制整个字符串 因为gets()函数会把这个null字符解释为字符串的结尾 那为什么要以上这三个字符呢?因为 这些字符的十六进制形式提供了内存中表示地址所需的值 的ASCII十六进制码为 x | 为 x 而 @ 为 x 如果把这三个字符以顺序 | @ 连接起来 就可将shellcode( x )地址的小尾字节序表示形式写入到内存中 最后一个null字节 由字符串的null字符提供 (见代码段 )  代码段 DF D DF E a e @ y:N DF F a b f f d da c fc f d +/ yx DF f d H ` @ PST   当checkpassword()函数返回时 控制权就传到shellcode而不是main()函数中的原始返回地址上   为了简化这个攻击过程 在此 关闭了缓冲区安全检查选项/GS 如果这个选项没有关闭 编译器将会在声明在堆栈上的任何数组(缓冲区)之后插入一个 密探 实际上为一个Cookie 见图 图 基于 密探 的缓冲区溢出保护

  如果要使用那些不受控制的字符串复制操作 如gets()或strcpy() 来覆蓋掉由 密探 保护的返回地址(EIP) 基指针(EBP) 或堆栈上的其他值 一个攻击者将首先要覆蓋掉这个 密探 如果 密探 被修改了 当函数返回时 将会产生一个错误 导致攻击失败 除非是为了进行 拒绝服务攻击 通过暴力枚举猜测这个值 或其他方法 还是有可能挫败这个 密探 的 但是 进行一次成功攻击的难度增加了   打开/GS选项不会让程序对缓冲区溢出漏洞彻底免疫 堆栈中的缓冲区溢出仍会使程序崩溃 攻击者利用基于堆栈的溢出来执行任意代码的可能性 即使在打开/GS的情况下仍然存在 更重要的是 /GS选项不会检测堆中或数据段中的缓冲区溢出   为举例说明 例 使用Win GUI重写了前面那个示例程序 这个程序提供一个带有一些简单选项的菜单栏 File菜单下有两个菜单项 Login 和 Exit Login会用一个对话框来提示用户输入密码 一旦输入了密码 在用户点击 OK 按钮之后 将把输入的密码与之前记录的密码相比较   例

   #include stdafx h #include TestItDan h #include <stdlib h> #include <stdio h> #include <windows h> #define MAX_LOADSTRING struct user  wchar_t *name;  size_t len;  int uid; ; HINSTANCE hInst; TCHAR szTitle[MAX_LOADSTRING]; TCHAR szWindowClass[MAX_LOADSTRING]; TCHAR lpszUserName[ ] = L guest ; TCHAR lpszPassword[ ] = L abcde ; struct user *userP = (struct user *) xcdcdcdcdcdcdcdcd; size_t userNameLen = ; size_t userPasswordLen = xffffffff; int APIENTRY _tWinMain(HINSTANCE hInstance HINSTANCE hPrevInstance LPTSTR lpCmdLine int nCmdShow)  UNREFERENCED_PARAMETER(hPrevInstance);  UNREFERENCED_PARAMETER(lpCmdLine);  MSG msg;  HACCEL hAccelTable;  LoadString(hInstance IDS_APP_TITLE szTitle MAX_LOADSTRING);  LoadString(hInstance IDC_TESTITDAN szWindowClass MAX_LOADSTRING);  MyRegisterClass(hInstance);  userP = (struct user *)malloc(sizeof(user));  if (!InitInstance (hInstance nCmdShow))   return FALSE;    hAccelTable =LoadAccelerators(hInstance MAKEINTRESOURCE(IDC_TESTITDAN));  while (GetMessage(&msg NULL ))   if (!TranslateAccelerator(msg hwnd hAccelTable &msg))    TranslateMessage(&msg);    DispatchMessage(&msg);       return (int) msg wParam; INT_PTR CALLBACK GetPassword(HWND hDlg UINT message WPARAM wParam LPARAM lParam)  TCHAR lpszGuestPassword[ ] = L NCC ;  UNREFERENCED_PARAMETER(lParam);  switch (message)   case WM_INITDIALOG:    return (INT_PTR)TRUE;   case WM_MAND:    if (LOWORD(wParam) == IDOK)     EndDialog(hDlg LOWORD(wParam));     SendDlgItemMessage(hDlg     IDC_EDIT     EM_GETLINE     (WPARAM) // line     (LPARAM) lpszPassword     );     userP >len = userNameLen;     if (wcscmp(lpszPassword lpszGuestPassword) == )      return true;          else      MessageBox(hDlg       (LPCWSTR)L Invalid Password       (LPCWSTR)L Login Failed       MB_OK      );          return (INT_PTR)TRUE;        break;      return (INT_PTR)FALSE;  

  程序编译及测试的环境均与前例相同 除了在此使用了Unicode字符集及打开了缓冲区安全检查选项(/GS) 我们在此继续使用托管扩展(CLR)   这是一个非常简单的程序 尽管为了支持Windows GUI 它显得稍微有点长 在 至 行 有几个有意思的变量 lpszPassword是一个由 个宽字符( 字节)组成的已初始化的静态变量 紧跟其后的是userP指针及两个无符号整形 userNameLen和userPasswordLen 之后 userP在 行初始化 这些变量的地址如下

   &lpszPassword = x C&userP = x C&userNameLen = x &userPasswordLen = x

  userP的值为 x D userNameLen的值为 x userPasswordLen的值为 xffffffff 如果我们查看lpszPassword地址的起始处内存 可以非常清楚地看到这些变量的初始值(见插 )   代码段

   C C C d ff ff ff ff a C c

  此程序中的漏洞是在 至 行中对SendDlgItemMessage的调用 EM_GETLINE消息指定了从编辑控件IDC_EDIT 获取一行文本 编辑控件在Login对话框中 并把它复制到定长缓冲区lpszPassword中 这个缓冲区只能容纳 个Unicode字符及一个结尾的null 如果输入了多于 个字符 就会发生缓冲区溢出 在此假设输入了 个字符 第 及 个字符将会覆蓋掉userP 第 及 个字符将会覆蓋掉userNameLen 结尾的null将会覆蓋掉userPasswordLen

cha138/Article/program/net/201311/12555

相关参考

知识大全 巧解Tomcat中JVM内存溢出问题

巧解Tomcat中JVM内存溢出问题  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  你对Tomc

知识大全 java开发中常常遇到的内存溢出问题 OutOfMemory

java开发中常常遇到的内存溢出问题OutOfMemory  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一

在使用标准缓冲溶液进行定位调节时,应注意什么问题?

在使用标准缓冲溶液进行定位调节时,应注意什么问题?参考答案:1、要确定pH缓冲液的时效性(pH4几周后会发霉;pH7六周后.pH9四周后应重新配制)。2、选用一种与被测水样pH值相接近的缓冲液作为定位

在使用标准缓冲溶液进行定位调节时,应注意什么问题?

在使用标准缓冲溶液进行定位调节时,应注意什么问题?参考答案:1、要确定pH缓冲液的时效性(pH4几周后会发霉;pH7六周后.pH9四周后应重新配制)。2、选用一种与被测水样pH值相接近的缓冲液作为定位

知识大全 delphi 缓冲画图(内存画图)解决画图闪烁问题[1]

  很多朋友在做绘图程序的时候往往出现屏幕不停刷新产生闪烁的问题这里就告诉大家一个解决办法缓冲绘图如果有人是用取反画图解决这个问题那么在画直线的时候容易出现斑点效果不是很好如果是图片很大那么缓冲画图是

知识大全 delphi 缓冲画图(内存画图)解决画图闪烁问题[2]

  到了mfc里面由于有了封装所有的hdc被隐藏在对象中做为隐藏参数传递(就是DC类的this啦~~)所以我们的关键话题就转变为了怎样得到想要的DC类而已这个过程其实大同小异的在消息响应的过程中WM_

知识大全 双缓冲原理在awt和swing中实现消除闪烁的方法

   对于双缓冲的分析是在坦克大战游戏的设计时开始的由于当时忙于游戏的整体设计所以对这一个问题没有进行详细的研究现在就这个问题来谈谈自己的一些看法分析前提出几个问题&nb

知识大全 手机插上耳机后没反应,还是外放,有可能是耳机问题吗

手机插上耳机后没反应,还是外放,有可能是耳机问题吗?,求助.手机插上耳机没反应还是外放若插入耳机后耳机没有声音,建议您:1.调节耳机音量键+键。2.将耳机插入到其他设备,观察是否可以正常使用(排除是否

知识大全 问大家一个问题,工作面试,具体面试老板是看你什么,说话吗,还是什么,需要指教

问大家一个问题,工作面试,具体面试老板是看你什么,说话吗,还是什么,需要指教不同的老板要求都是不同的。不过,要时刻准备着奥,面试时要让自己看起来阳光、自信。说话时要条理清晰,逻辑严谨。语速不宜太快同时

知识大全 一旦朝韩开战中国和俄罗斯还会加入吗

一旦朝韩开战中国和俄罗斯还会加入吗这场战争利益不大吗?那怎么解释当年的抗美援朝,中国倾全国之力据敌于国外?自古有唇亡齿寒的说法,朝鲜再怎么拖中国的后腿,但怎么说还是中国对外的缓冲之地,否则,美国的势力