Chinaunix首页 | 论坛 | 博客
  • 博客访问: 14523973
  • 博文数量: 5645
  • 博客积分: 9880
  • 博客等级: 中将
  • 技术积分: 68081
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-28 13:35
文章分类

全部博文(5645)

文章存档

2008年(5645)

我的朋友

分类:

2008-04-28 21:06:32

下载本文示例代码
  面向初学者的,进行详细分析的缓冲溢出入门文章还是很少(我还没有看到),所以我下决心写了这篇文章,从C的局部变量分配以及它和堆栈的关系、返回地址和堆栈的关系、局部变量和返回地址以及堆栈的关系开始写起,并在讲述完原理后进行简单的应用,使理论和应用相结合,以给广大初学缓冲溢出的朋友一点小小的帮助,本文还是具有典型性的,通过本文的学习,可以让我们从一个普通的C程序员,了解到更加底层的技术,本文虽是面向初学者(指初学缓冲溢出,而不是初学C语言),作者假定你(读者)已经是一位熟练的C程序员,并且了解一些Asm编程技术。我也是刚学缓冲区溢出不久,这是我第一次写溢出技术,所以难免有错误的地方,还请大家指正,在ipxodi和袁哥的文章中我学到了很多东西,但ipxodi和袁哥和文章比较深比较专业,初学者学习起来有些困难,特别我又是非计算机专业的(我和绿盟的小四哥一样是电脑会计专业的,向小四哥学习,呵呵!).在这里把我学习时的一点理解,一点经验介绍给大家,希望对广大学习缓冲溢出的朋友有所帮助!   一、存储分配,局部内存变量,堆栈和函数调用  1、首先写一个简单的C字符串拷贝程序 //test.c#include #include #include void overflow(void){ char buf[10]; strcpy(buf,"aaaaaaaaaa");}//end overflowint main(void){ overflow(); return 0;}//end main  2、按F11进入"Step into"调试模式,其实只需要留意对我们研究和学习有用的汇编程序段,如下: 1: #include 2: #include 3: #include 4:5: void overflow(void)6: {00401020 55 push ebp00401021 8B EC mov ebp,esp00401023 83 EC 4C sub esp,4Ch00401026 53 push ebx00401027 56 push esi00401028 57 push edi00401029 8D 7D B4 lea edi,[ebp-4Ch]0040102C B9 13 00 00 00 mov ecx,13h00401031 B8 CC CC CC CC mov eax,0CCCCCCCCh00401036 F3 AB rep stos dword ptr [edi]7: char buf[10];8: strcpy(buf,"aaaaaaaaaa");00401038 68 1C F0 41 00 push offset string "aaaaaaaaaa" (0041f01c)0040103D 8D 45 F4 lea eax,[ebp-0Ch]00401040 50 push eax00401041 E8 6A 00 00 00 call strcpy (004010b0)00401046 83 C4 08 add esp,89:10: }//end overflow00401049 5F pop edi0040104A 5E pop esi0040104B 5B pop ebx0040104C 83 C4 4C add esp,4Ch0040104F 3B EC cmp ebp,esp00401051 E8 4A 01 00 00 call __chkesp (004011a0)00401056 8B E5 mov esp,ebp00401058 5D pop ebp00401059 C3 ret11:12: int main(void)13: {00401070 55 push ebp00401071 8B EC mov ebp,esp00401073 83 EC 40 sub esp,40h00401076 53 push ebx00401077 56 push esi00401078 57 push edi00401079 8D 7D C0 lea edi,[ebp-40h]0040107C B9 10 00 00 00 mov ecx,10h00401081 B8 CC CC CC CC mov eax,0CCCCCCCCh00401086 F3 AB rep stos dword ptr [edi]14: overflow();00401088 E8 7D FF FF FF call @ILT 5(overflow) (0040100a)15: return 0;0040108D 33 C0 xor eax,eax16: }//end main0040108F 5F pop edi00401090 5E pop esi00401091 5B pop ebx00401092 83 C4 40 add esp,40h00401095 3B EC cmp ebp,esp00401097 E8 04 01 00 00 call __chkesp (004011a0)0040109C 8B E5 mov esp,ebp0040109E 5D pop ebp0040109F C3 ret  3、返回VStudio IDE,在调用overflow函数处设置断点,再次选择"Run"菜单项,这时程序在调用overflow前停止。(下面的学习你需要不断地翻看上面的Asm程序段)现在看一下在调用overflow之前的几个需要注意的参数,把它们加入"Watch"窗口。 esp 0x0012ff34(注意:这些值在不同的机器上运行时可能会不一样)ebp 0x0012ff80buf 变量尚未分配overflow 0x00401020main 0x00401070  4、按F11跟踪进入overflow,让程序停在6:   现在再看一下几个主要参数:  esp=0x0012ff30,其它未变(指我们watch的几个标识符,这时eip一定是会变化的)很显然堆栈里压了一个dword(4字节)数据,看看它是什么,打开memory窗口,输入esp,右击窗口内容,选"Long Hex Format",当前的堆栈顶内容0x0040108d,现在请看一下call overflow的下一行,如果找不到请从头搜索"15:"字符串,看到了吗!压入的是call overflow的下一指令地址,也就是我们通常说的"函数返回地址".  再按F11(执行push ebp),再看一下几个主要参数  esp=0x0012ff2c,现在堆栈顶中是ebp的值0x0012ff80,  再按F11(执行下面的语句),程序将当前esp值保存在ebp中: mov ebp,esp  然后就开始分配局部变量了  sub esp,4ch;分配了76(0x4c)个字节这个地方我不太清楚为什么始终要保留64(0x40)个字节,其实只有12(0x0c)字节可用,随后的7句指令: 00401026 53 push ebx00401027 56 push esi00401028 57 push edi00401029 8D 7D B4 lea edi,[ebp-4Ch]0040102C B9 13 00 00 00 mov ecx,13h00401031 B8 CC CC CC CC mov eax,0CCCCCCCCh00401036 F3 AB rep stos dword ptr [edi]  将这76个字节以dword(4)为单位填充为0xcccccccc,共填充76/4=19(0x13)次让我们在执行完rep stos dword ptr [edi]时先停下来.在watch窗口里加入eip和一个表达式"ebp-0ch",会发现在"ebp-0ch"和buf的地址一样,这就是编译程序在堆栈中为我们分配的局部内存变量的起始地址(如果你懂编译原理,这里很容易理解),在memory窗口里输入ebp-0ch(变量起始地址),右击窗口选"Byte Format",可以看到里面有12个字节是被0xcc填充过的.  好!现在跟踪执行完call strcpy,再看看Memory窗口的内容,有11个字节被填充,前10个填充为0x61即ASCII字符'a',后一个字节为0这验证了C字符串操作函数总是产生一个空终止字符。  再往下看,右击选"Long Hex Format"看到它们分别是 0x0012ff80和0x0040108d,什么?有点熟?对啊!我也觉得有点面熟,为什么呢?请回头看一下第4小节的开始部分,找到答案了?对!是"老的ebp"和"函数返回地址",继续跟踪将执行以下几个动作,恢复主要寄存器内容,add esp 4ch销毁了局部内存变量恢复老的ebp(这时堆栈顶的内容为0x0040108d),再ret返回(其实ret相当于执行了一次"pop eip",但并没有这样的指令)执行完这条指令后eip的内容变为0x0040108d,这时已经回到了主函数中,在主函数中将执行几乎同样的动作,最后完成程序执行。  有人可能会问overflow需要回到main所以用了一个ret,可是main中的ret是做什么用的呢?其实初学者可能并不知道我们的C程序编译后程序的空间结构(简化后的)是这么一个样子的. ----------------------------------//程序入口点(Program Entry Point)...call _mainpush eaxcall _ExitProcess.---------------------------------- //void overflow(void)push ebp...call _strcpy...ret ---------------------------------- //int main(void)push ebp...call _overflow...ret ----------------------------------  overflow中的ret让程序回到main,而main中的ret是为了回到入口点那段程序,以返回操作系统。   小结:  在这一部分里我们学习到了一些为理解缓冲区溢出打基础的东西,如局部内存变量是如何分配的,它于堆栈的关系以及函数调用、函数返回地址与堆栈的关系,把这些东西搞懂了以后我们可以进行一些简单的应用,出于学习原理的目的,接下来我们将用缓冲溢出来实现一个命令控制台窗口(cmd.exe)。共2页。 1 2 :   面向初学者的,进行详细分析的缓冲溢出入门文章还是很少(我还没有看到),所以我下决心写了这篇文章,从C的局部变量分配以及它和堆栈的关系、返回地址和堆栈的关系、局部变量和返回地址以及堆栈的关系开始写起,并在讲述完原理后进行简单的应用,使理论和应用相结合,以给广大初学缓冲溢出的朋友一点小小的帮助,本文还是具有典型性的,通过本文的学习,可以让我们从一个普通的C程序员,了解到更加底层的技术,本文虽是面向初学者(指初学缓冲溢出,而不是初学C语言),作者假定你(读者)已经是一位熟练的C程序员,并且了解一些Asm编程技术。我也是刚学缓冲区溢出不久,这是我第一次写溢出技术,所以难免有错误的地方,还请大家指正,在ipxodi和袁哥的文章中我学到了很多东西,但ipxodi和袁哥和文章比较深比较专业,初学者学习起来有些困难,特别我又是非计算机专业的(我和绿盟的小四哥一样是电脑会计专业的,向小四哥学习,呵呵!).在这里把我学习时的一点理解,一点经验介绍给大家,希望对广大学习缓冲溢出的朋友有所帮助!   一、存储分配,局部内存变量,堆栈和函数调用  1、首先写一个简单的C字符串拷贝程序 //test.c#include #include #include void overflow(void){ char buf[10]; strcpy(buf,"aaaaaaaaaa");}//end overflowint main(void){ overflow(); return 0;}//end main  2、按F11进入"Step into"调试模式,其实只需要留意对我们研究和学习有用的汇编程序段,如下: 1: #include 2: #include 3: #include 4:5: void overflow(void)6: {00401020 55 push ebp00401021 8B EC mov ebp,esp00401023 83 EC 4C sub esp,4Ch00401026 53 push ebx00401027 56 push esi00401028 57 push edi00401029 8D 7D B4 lea edi,[ebp-4Ch]0040102C B9 13 00 00 00 mov ecx,13h00401031 B8 CC CC CC CC mov eax,0CCCCCCCCh00401036 F3 AB rep stos dword ptr [edi]7: char buf[10];8: strcpy(buf,"aaaaaaaaaa");00401038 68 1C F0 41 00 push offset string "aaaaaaaaaa" (0041f01c)0040103D 8D 45 F4 lea eax,[ebp-0Ch]00401040 50 push eax00401041 E8 6A 00 00 00 call strcpy (004010b0)00401046 83 C4 08 add esp,89:10: }//end overflow00401049 5F pop edi0040104A 5E pop esi0040104B 5B pop ebx0040104C 83 C4 4C add esp,4Ch0040104F 3B EC cmp ebp,esp00401051 E8 4A 01 00 00 call __chkesp (004011a0)00401056 8B E5 mov esp,ebp00401058 5D pop ebp00401059 C3 ret11:12: int main(void)13: {00401070 55 push ebp00401071 8B EC mov ebp,esp00401073 83 EC 40 sub esp,40h00401076 53 push ebx00401077 56 push esi00401078 57 push edi00401079 8D 7D C0 lea edi,[ebp-40h]0040107C B9 10 00 00 00 mov ecx,10h00401081 B8 CC CC CC CC mov eax,0CCCCCCCCh00401086 F3 AB rep stos dword ptr [edi]14: overflow();00401088 E8 7D FF FF FF call @ILT 5(overflow) (0040100a)15: return 0;0040108D 33 C0 xor eax,eax16: }//end main0040108F 5F pop edi00401090 5E pop esi00401091 5B pop ebx00401092 83 C4 40 add esp,40h00401095 3B EC cmp ebp,esp00401097 E8 04 01 00 00 call __chkesp (004011a0)0040109C 8B E5 mov esp,ebp0040109E 5D pop ebp0040109F C3 ret  3、返回VStudio IDE,在调用overflow函数处设置断点,再次选择"Run"菜单项,这时程序在调用overflow前停止。(下面的学习你需要不断地翻看上面的Asm程序段)现在看一下在调用overflow之前的几个需要注意的参数,把它们加入"Watch"窗口。 esp 0x0012ff34(注意:这些值在不同的机器上运行时可能会不一样)ebp 0x0012ff80buf 变量尚未分配overflow 0x00401020main 0x00401070  4、按F11跟踪进入overflow,让程序停在6:   现在再看一下几个主要参数:  esp=0x0012ff30,其它未变(指我们watch的几个标识符,这时eip一定是会变化的)很显然堆栈里压了一个dword(4字节)数据,看看它是什么,打开memory窗口,输入esp,右击窗口内容,选"Long Hex Format",当前的堆栈顶内容0x0040108d,现在请看一下call overflow的下一行,如果找不到请从头搜索"15:"字符串,看到了吗!压入的是call overflow的下一指令地址,也就是我们通常说的"函数返回地址".  再按F11(执行push ebp),再看一下几个主要参数  esp=0x0012ff2c,现在堆栈顶中是ebp的值0x0012ff80,  再按F11(执行下面的语句),程序将当前esp值保存在ebp中: mov ebp,esp  然后就开始分配局部变量了  sub esp,4ch;分配了76(0x4c)个字节这个地方我不太清楚为什么始终要保留64(0x40)个字节,其实只有12(0x0c)字节可用,随后的7句指令: 00401026 53 push ebx00401027 56 push esi00401028 57 push edi00401029 8D 7D B4 lea edi,[ebp-4Ch]0040102C B9 13 00 00 00 mov ecx,13h00401031 B8 CC CC CC CC mov eax,0CCCCCCCCh00401036 F3 AB rep stos dword ptr [edi]  将这76个字节以dword(4)为单位填充为0xcccccccc,共填充76/4=19(0x13)次让我们在执行完rep stos dword ptr [edi]时先停下来.在watch窗口里加入eip和一个表达式"ebp-0ch",会发现在"ebp-0ch"和buf的地址一样,这就是编译程序在堆栈中为我们分配的局部内存变量的起始地址(如果你懂编译原理,这里很容易理解),在memory窗口里输入ebp-0ch(变量起始地址),右击窗口选"Byte Format",可以看到里面有12个字节是被0xcc填充过的.  好!现在跟踪执行完call strcpy,再看看Memory窗口的内容,有11个字节被填充,前10个填充为0x61即ASCII字符'a',后一个字节为0这验证了C字符串操作函数总是产生一个空终止字符。  再往下看,右击选"Long Hex Format"看到它们分别是 0x0012ff80和0x0040108d,什么?有点熟?对啊!我也觉得有点面熟,为什么呢?请回头看一下第4小节的开始部分,找到答案了?对!是"老的ebp"和"函数返回地址",继续跟踪将执行以下几个动作,恢复主要寄存器内容,add esp 4ch销毁了局部内存变量恢复老的ebp(这时堆栈顶的内容为0x0040108d),再ret返回(其实ret相当于执行了一次"pop eip",但并没有这样的指令)执行完这条指令后eip的内容变为0x0040108d,这时已经回到了主函数中,在主函数中将执行几乎同样的动作,最后完成程序执行。  有人可能会问overflow需要回到main所以用了一个ret,可是main中的ret是做什么用的呢?其实初学者可能并不知道我们的C程序编译后程序的空间结构(简化后的)是这么一个样子的. ----------------------------------//程序入口点(Program Entry Point)...call _mainpush eaxcall _ExitProcess.---------------------------------- //void overflow(void)push ebp...call _strcpy...ret ---------------------------------- //int main(void)push ebp...call _overflow...ret ----------------------------------  overflow中的ret让程序回到main,而main中的ret是为了回到入口点那段程序,以返回操作系统。   小结:  在这一部分里我们学习到了一些为理解缓冲区溢出打基础的东西,如局部内存变量是如何分配的,它于堆栈的关系以及函数调用、函数返回地址与堆栈的关系,把这些东西搞懂了以后我们可以进行一些简单的应用,出于学习原理的目的,接下来我们将用缓冲溢出来实现一个命令控制台窗口(cmd.exe)。共2页。 1 2 : 下载本文示例代码


Windows 2000缓冲区溢出技术原理Windows 2000缓冲区溢出技术原理Windows 2000缓冲区溢出技术原理Windows 2000缓冲区溢出技术原理Windows 2000缓冲区溢出技术原理Windows 2000缓冲区溢出技术原理Windows 2000缓冲区溢出技术原理Windows 2000缓冲区溢出技术原理Windows 2000缓冲区溢出技术原理Windows 2000缓冲区溢出技术原理Windows 2000缓冲区溢出技术原理Windows 2000缓冲区溢出技术原理Windows 2000缓冲区溢出技术原理Windows 2000缓冲区溢出技术原理Windows 2000缓冲区溢出技术原理
阅读(183) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~