今天在学习溢出时写了个简单程序,源代码如下。
1#include <stdio.h>
2#include <stdlib.h>
3
4int foo_normal(char*);
5int foo_abnormal(char*);
6
7int main()
8{
9 int i;
10 char szTmp[] = {
11 0x31, 0x31, 0x31, 0x31,
12 0x32, 0x32, 0x32, 0x32,
13 0x33, 0x33, 0x33, 0x33,
14 0x00, 0x40, 0x13, 0x45,
15
16 0x31, 0x31, 0x31, 0x31,
17 0x32, 0x32, 0x32, 0x32,
18 0x33, 0x33, 0x33, 0x33,
19 0x4e, 0x13, 0x40, 0x00
20 };
21
22 i = 0;
23
24 if (i == 0)
25 foo_normal(szTmp);
26 else
27 foo_abnormal(szTmp);
28
29 return(0);
30}
31
32
33int foo_normal(char* _sz)
34{
35 char buffer[5];
36 memcpy(buffer, _sz, sizeof(char)*32);
37 printf("Application terminates normally.\n");
38 return(0);
39}
40int foo_abnormal(char* _sz)
41{
42 char buffer[5];
43 memcpy(buffer, _sz, sizeof(char)*3);
44 printf("You should have never seen this.\n");
45 return (0);
46}
打算通过foo_normal中的memcpy函数覆盖栈中的EIP,改为调用foo_abnormal处的语句,来达到溢出攻击的目的。按照正常情况看,在foo_normal中,栈里有4字节的CS和4字节的EIP,然后是5字节的字符串数组——对齐后是8字节,还有4字节的EBP。所以当往buffer中复制12字节数据就可以覆盖掉EIP而达到溢出的目的。但实际上使用32字节的数据覆盖buffer及其后的数据,才把EIP给照顾到。使用OllyDBG跟了一下:
1 004013F2 /$ 55 push ebp
2 004013F3 |. 89E5 mov ebp, esp
3 004013F5 |. 83EC 28 sub esp, 28
4 004013F8 |. C74424 08 050>mov dword ptr [esp+8], 5 ; ||
5 00401400 |. 8B45 08 mov eax, dword ptr [ebp+8] ; ||
6 00401403 |. 894424 04 mov dword ptr [esp+4], eax ; ||
7 00401407 |. 8D45 E8 lea eax, dword ptr [ebp-18] ; ||
8 0040140A |. 890424 mov dword ptr [esp], eax ; ||
9 0040140D |. E8 FE050000 call <jmp.&msvcrt.memcpy> ; |\memcpy
10 00401412 |. C70424 D01340>mov dword ptr [esp], 004013D0 ; |ASCII "Application terminates normally.",LF
11 00401419 |. E8 EA050000 call <jmp.&msvcrt.printf> ; \printf
12 0040141E |. B8 00000000 mov eax, 0
13 00401423 |. C9 leave
14 00401424 \. C3 retn
发现在进入函数的时候申请了28个字节的空间——除去12字节给memcpy的参数,比预想的多了6字节。看来使用的3.3.1版本的gcc是16字节对齐的。
……让我抓狂了一个小时。