C 缓冲区溢出困境

C 缓冲区溢出困境,c,security,buffer-overflow,C,Security,Buffer Overflow,因此,我试图对这段代码执行缓冲区溢出,目的是将变量target更改为“Y”。问题是,我似乎无法让缓冲区溢出到足以生成运行时错误的程度。有人能帮我理解为什么吗 #include <stdio.h> #include <string.h> #include <stdlib.h> enum {SIZE = 50}; char target = 'Z'; char name[SIZE]; FILE *f; void read(char *s) { char

因此,我试图对这段代码执行缓冲区溢出,目的是将变量
target
更改为“Y”。问题是,我似乎无法让缓冲区溢出到足以生成运行时错误的程度。有人能帮我理解为什么吗

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

enum {SIZE = 50};

char target = 'Z';
char name[SIZE];
FILE *f;

void read(char *s) {
   char buffer[SIZE];
   int i = 0; 
   int c;

   for (;;) 
   {
      c = getchar();
      if ((c == EOF) || (c == '\n')) 
         break;
      buffer[i] = c;
      i++;
   }
   buffer[i] = '\0';

   for (i = 0; i < SIZE; i++) 
      s[i] = buffer[i];
}


int main(void) 
{
   read(name);

   if (strcmp(name, "ABCD") == 0) 
      target = 'Y';

   printf("%s\n", name);
   printf("%c\n", target);

   exit(0);
}
#包括
#包括
#包括
枚举{SIZE=50};
char target='Z';
字符名称[大小];
文件*f;
无效读取(字符*s){
字符缓冲区[大小];
int i=0;
INTC;
对于(;;)
{
c=getchar();
如果((c==EOF)| |(c=='\n'))
打破
缓冲区[i]=c;
i++;
}
缓冲区[i]='\0';
对于(i=0;i
如果您试图使用更改的回信地址执行此操作,则需要使用所需的特定地址覆盖它。在这种情况下,为了获得所需的结果,您可以使用代码
target='Y'的地址进行覆盖

根据堆栈的组织方式,您可以直接从
read()
中删除返回地址,该地址将返回到调用
main
的第一行之后。否则,您将有机会对
i
进行重击,这将允许您任意写入内存。在编写这些字节时需要小心,因为这会影响索引

我将假设这是在ARM上完成的,因为我更熟悉它们的堆栈布局和调用约定。那么,你的堆栈看起来是这样的:

STACKPTR-60: buffer[0]
 |   |   |
STACKPTR-11: buffer[49]
STACKPTR-10: padding
STACKPTR -9: padding
STACKPTR -8: i (bits 7..0)
 |   |   |
STACKPTR -5: i (bits 31..24)
STACKPTR -4: c (bits 7..0)
 |   |   |
STACKPTR -1: c (bits 31..24)
STACKPTR +0: link register (return address) (bits 7..0)
 |   |   |
STACKPTR +3: link register (return address) (bits 31..24)
因此,使用此堆栈布局,当您溢出
缓冲区时,您将首先填充两个pad字节。然后,填充
i
的最低有效字节。在这种情况下,您可以跳过巨大的溢出,只需修改索引以指向堆叠链接寄存器。您希望
i=60
开始写入回信地址,但写入后,回信地址将递增,因此您确实希望
i=59
,然后将递增到
i=60
。此时,您可以从最低有效到最高有效写入所需的返回地址,然后是
EOF
\n


需要注意的是这些都是非常特定于体系结构和编译器的。编译器设置将确定
int
s的大小,以及堆栈是否将被填充以对齐变量,以及变量在堆栈上的显示顺序。体系结构将决定堆栈帧的外观。

尝试此“非崩溃”版本的代码,它确实会覆盖
名称
数组的边界,以获得中等大小的输入字符串:

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

enum {SIZE = 50};

char target_1 = 'Z';
char name[SIZE] = "Help";
char target_2 = 'Z';

static void read(char *s)
{
   char buffer[2*SIZE];
   int i = 0; 
   int c;

   while ((c = getchar()) != EOF && c != '\n')
      buffer[i++] = c;
   buffer[i] = '\0';

   for (int j = 0; j < i; j++) 
      s[j] = buffer[j];
}


int main(void) 
{
   printf("Data-1: %c %s %c\n", target_1, name, target_2);
   read(name);
   printf("Data-2: %c %s %c\n", target_1, name, target_2);
   return(0);
}
您可以看到,
target_1
的内存地址高于
name
,并且它确实会被大小为52或更大的输入覆盖

当我说“非崩溃”时,我的意思是“它不会崩溃,因为输入行的长度足以溢出
名称
缓冲区,但不会长到溢出
缓冲区
缓冲区”(大约长度为50到100)。使用较长的输入字符串,程序会因分段错误而崩溃,但有趣的是,它在打印第二行输出后崩溃,而不是在从
read()
返回时崩溃


在Mac OS X 10.9.2 Mavericks和GCC 4.9.0上测试。

您尝试输入的内容是什么?非常长的字符串。数千个字符。它溢出了缓冲区,但没有导致运行时错误。我猜这意味着它没有覆盖任何返回值。您打算如何进行攻击-直接覆盖
目标
?您是否已尝试打印
缓冲区
目标
的地址,以便知道需要多少数据?您不能使用长字符串覆盖“target”,因为堆栈上没有分配“target”。@alexander请参阅您上面的注释。我正试图覆盖一个回信地址;最近的返回地址是read函数,我的字符串攻击无法到达它。我之前所做的这个问题的迭代在代码{target}中的相同位置有{I}。为什么我现在是一个局部变量这一事实使得这一点更加困难?使用
i
作为堆栈变量,您的索引在溢出时会被破坏。我在最初的回答中遗漏了这一点,所以我将更新它。使用ARM的ABI更新了一个示例。这似乎不起作用。在输入字符串的末尾,有一个0xffffffff字,如果被篡改,它将结束输入。你能想出一个解决方法吗?换句话说,i和buffer对于局部变量的顺序是错误的。
$ for len in 48 49 50 51 52 53 54 ; do echo $len; perl -e "print 'a' x $len" | ./bo; done
48
Data-1: Z Help Z
Data-2: Z aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Z
49
Data-1: Z Help Z
Data-2: Z aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Z
50
Data-1: Z Help Z
Data-2: Z aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaZ Z
51
Data-1: Z Help Z
Data-2: a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Z
52
Data-1: Z Help Z
Data-2: a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Z
53
Data-1: Z Help Z
Data-2: a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Z
54
Data-1: Z Help Z
Data-2: a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Z
$
140
Data-1: Z Help Z
Data-2: a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Z
Segmentation fault: 11