C缓冲区溢出 - 要输入的字节数

时间:2017-11-15 00:13:54

标签: c security buffer overflow

我做了一个简单的C程序来尝试理解缓冲区溢出。我试图溢出输入缓冲区,以便标志变为true,程序输出"到达这里"。该程序在这里(假设你有密码.txt,其中包含):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv){
  int flag = 0;

  char pwd[5];
  char input[5];

  FILE *f = fopen("password.txt", "r");

  fscanf(f, "%s", pwd);
  fclose(f);

  strcpy(input, argv[1]);

  if(strcmp(input, pwd)){
    printf("wrong password!\n");
  }
  else{
    flag = 1;
    printf("correct password!\n");
  }
  if(flag){
    printf("you got into the secret place\n");
  }
  return 0;
}

所以在堆栈上我们有这样的东西我想:

[high addresses]
flag   ---> 4 bytes
pwd    ---> 8 bytes
input  ---> 8 bytes
[low addresses]

所以我想我只需要给程序只有17个字节来覆盖flag变量。所以我给了它 aaaaaaaaaaaaaaaaaa ,但那并没有奏效。我不得不给它 a 23次,所以23个字节。为什么17个字节不够?

2 个答案:

答案 0 :(得分:2)

尝试此操作时可能会遇到多个问题。

  • 堆栈上局部变量的顺序不一定与代码中的顺序相同。编译器可以随意重新排列它们。

  • 编译器经常将数据与边界对齐以加快执行速度。这意味着不同局部变量之间的空间可能比您想象的要大得多。看到堆栈变量在8或16字节边界上对齐的情况并不罕见,即使它的内容要小得多。

您可以使用objdump -D对程序进行反汇编,也可以使用gdb进行调试。这将使您更好地了解程序中的堆栈对齐。

答案 1 :(得分:2)

免责声明:我使用Ubuntu 14.04和gcc版本4.8.4,并且遵守了你的 因此代码gcc -m32 -g -ansi -pedantic -Wall temp.c -o temp。不同 编译器或gcc的不同选项很可能会给出 不同的结果。

我还稍微修改了你的代码,以便更容易找到东西,

  1. 我将第6行更改为int flag = 0x41414141;
  2. 我将第25行更改为if(flag==1){
  3. 编译后,我在GDB下运行可执行文件,并设置了断点 主要的。然后拆卸主要(在设置拆卸味道后 英特尔),我们得到:

    (gdb) disass
    Dump of assembler code for function main:
       0x0804857d <+0>: push   ebp
       0x0804857e <+1>: mov    ebp,esp
       0x08048580 <+3>: and    esp,0xfffffff0
       0x08048583 <+6>: sub    esp,0x30
       0x08048586 <+9>: mov    eax,DWORD PTR [ebp+0xc]
       0x08048589 <+12>:    mov    DWORD PTR [esp+0xc],eax
    => 0x0804858d <+16>:    mov    eax,gs:0x14
       0x08048593 <+22>:    mov    DWORD PTR [esp+0x2c],eax
       0x08048597 <+26>:    xor    eax,eax
       0x08048599 <+28>:    mov    DWORD PTR [esp+0x18],0x41414141
    

    前四行是main的序言。重要的是。{ notice是我们正在设置的行sub esp,0x30 堆栈框架的功能。如您所见,我们减去了48个字节 来自esp。 (实际上更多一点,因为我们首先对齐堆栈帧 到16字节的边界。)

    现在,我们可以通过查看值来查看堆栈帧的位置 对于ESP和EBP:

    (gdb) info registers esp
     esp            0xffffd110  0xffffd110
    
    (gdb) info registers ebp
    ebp            0xffffd148   0xffffd148
    

    我们可以找到堆栈框架中的位置;

    (gdb) print &pwd
    $3 = (char (*)[5]) 0xffffd132
    
    (gdb) print &flag
    $4 = (int *) 0xffffd128
    
    (gdb) print &input
    $5 = (char (*)[5]) 0xffffd137
    
    (gdb) print &f
    $6 = (FILE **) 0xffffd12c
    

    由此我们可以推断出我们的堆栈布局。这个记忆图像是 在程序读取命令行之后运行 参数是字符串BBBBB(回想一下B的ASCII码是0x42,所以很容易看到一个0x42字节的序列)

    (gdb) x/56xb $esp
    0xffffd110: 0x37    0xd1    0xff    0xff    
    0xffffd114: 0xbf    0xd3    0xff    0xff
    0xffffd118: 0x32    0xd1    0xff    0xff    
    0xffffd11c: 0xe4    0xd1    0xff    0xff
    0xffffd120: 0x02    0x00    0x00    0x00    
    0xffffd124: 0xe4    0xd1    0xff    0xff
    0xffffd128: 0x41    0x41    0x41    0x41         (flag) 
    0xffffd12c: 0x08    0xb0    0x04    0x08         (f)
    0xffffd130: 0xc4    0xf3    
    0xffffd132: 0x68    0x65    0x79    0x00    0xff (pwd buffer)   
    0xffffd137: 0x42    0x42    0x42    0x42    0x42 (input buffer) 
    0xffffd13c: 0x00    0xd5    0x61    0x5d
    0xffffd140: 0x60    0x86    0x04    0x08    
    0xffffd144: 0x00    0x00    0x00    0x00
    

    另请注意,如果我将命令行参数设置为BBBBBBBB,我们就会这样做 对于我们的堆栈框架的内容

    (gdb) x/56xb $esp
    0xffffd110: 0x37    0xd1    0xff    0xff    0xbc    0xd3    0xff    0xff
    0xffffd118: 0x32    0xd1    0xff    0xff    0xe4    0xd1    0xff    0xff
    0xffffd120: 0x02    0x00    0x00    0x00    0xe4    0xd1    0xff    0xff
    0xffffd128: 0x41    0x41    0x41    0x41    0x08    0xb0    0x04    0x08
    0xffffd130: 0xc4    0xf3    0x68    0x65    0x79    0x00    0xff    0x42
    0xffffd138: 0x42    0x42    0x42    0x42    0x42    0x42    0x42    0x00
    0xffffd140: 0x60    0x86    0x04    0x08    0x00    0x00    0x00    0x00
    

    请注意,flag变量的内容保持不变,但是现在溢出的输入缓冲区的内容会移动到堆栈的顶部。回想一下,在x86中,堆栈向下增长(较低的内存地址)。此外,因为缓冲区向上增长是我们可以使用缓冲区溢出来过度写入堆栈中存储的EIP的原因。

    所以在我的系统上,我不相信可以用用户输入覆盖flag变量。您的系统可能会在堆栈上以不同的方式进行布局(您需要执行类似的练习来验证这一点)。

    另请注意,堆栈中变量的位置与源文件中声明的顺序无关。