一、存储分配,局部内存变量,堆栈和函数调用
1、首先写一个简单的C字符串拷贝程序
//test.c #include <stdio.h> #include <stdlib.h> #include <string.h> void overflow(void) { char buf[10]; strcpy(buf,"aaaaaaaaaa"); }//end overflow int main(void) { overflow(); return 0; }//end main |
2、按F11进入"Step into"调试模式,其实只需要留意对我们研究和学习有用的汇编程序段,如下:
1: #include <stdio.h> 2: #include <stdlib.h> 3: #include <string.h> 4: 5: void overflow(void) 6: { 00401020 55 push ebp 00401021 8B EC mov ebp,esp 00401023 83 EC 4C sub esp,4Ch 00401026 53 push ebx 00401027 56 push esi 00401028 57 push edi 00401029 8D 7D B4 lea edi,[ebp-4Ch] 0040102C B9 13 00 00 00 mov ecx,13h 00401031 B8 CC CC CC CC mov eax,0CCCCCCCCh 00401036 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 eax 00401041 E8 6A 00 00 00 call strcpy (004010b0) 00401046 83 C4 08 add esp,8 9: 10: }//end overflow 00401049 5F pop edi 0040104A 5E pop esi 0040104B 5B pop ebx 0040104C 83 C4 4C add esp,4Ch 0040104F 3B EC cmp ebp,esp 00401051 E8 4A 01 00 00 call __chkesp (004011a0) 00401056 8B E5 mov esp,ebp 00401058 5D pop ebp 00401059 C3 ret 11: 12: int main(void) 13: { 00401070 55 push ebp 00401071 8B EC mov ebp,esp 00401073 83 EC 40 sub esp,40h 00401076 53 push ebx 00401077 56 push esi 00401078 57 push edi 00401079 8D 7D C0 lea edi,[ebp-40h] 0040107C B9 10 00 00 00 mov ecx,10h 00401081 B8 CC CC CC CC mov eax,0CCCCCCCCh 00401086 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,eax 16: }//end main 0040108F 5F pop edi 00401090 5E pop esi 00401091 5B pop ebx 00401092 83 C4 40 add esp,40h 00401095 3B EC cmp ebp,esp 00401097 E8 04 01 00 00 call __chkesp (004011a0) 0040109C 8B E5 mov esp,ebp 0040109E 5D pop ebp 0040109F C3 ret |
3、返回VStudio IDE,在调用overflow函数处设置断点,再次选择"Run"菜单项,这时程序在调用overflow前停止。(下面的学习你需要不断地翻看上面的Asm程序段)现在看一下在调用overflow之前的几个需要注意的参数,把它们加入"Watch"窗口。
esp 0x0012ff34(注意:这些值在不同的机器上运行时可能会不一样) ebp 0x0012ff80 buf 变量尚未分配 overflow 0x00401020 main 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 ebx 00401027 56 push esi 00401028 57 push edi 00401029 8D 7D B4 lea edi,[ebp-4Ch] 0040102C B9 13 00 00 00 mov ecx,13h 00401031 B8 CC CC CC CC mov eax,0CCCCCCCCh 00401036 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 _main push eax call _ExitProcess . ---------------------------------- //void overflow(void) push ebp . . . call _strcpy . . . ret ---------------------------------- //int main(void) push ebp . . . call _overflow . . . ret ---------------------------------- |
overflow中的ret让程序回到main,而main中的ret是为了回到入口点那段程序,以返回操作系统。
小结:
在这一部分里我们学习到了一些为理解缓冲区溢出打基础的东西,如局部内存变量是如何分配的,它于堆栈的关系以及函数调用、函数返回地址与堆栈的关系,把这些东西搞懂了以后我们可以进行一些简单的应用,出于学习原理的目的,接下来我们将用缓冲溢出来实现一个命令控制台窗口(cmd.exe)。
分页: [1] [2]
TAG: WINDOWS 缓冲区 原理 技术