-3

今天是个好日子!

我试图了解缓冲区溢出是如何工作的。现在,我正在确定函数返回地址的地址,我应该更改它以执行缓冲区溢出攻击。我根据我在互联网上阅读的示例编写了一个简单的程序。这个程序所做的是它创建一个整数指针来存储函数的返回地址在堆栈中的地址。为此,(假设我了解函数/程序变量如何在堆栈中组织),我将 8 添加到缓冲区变量的地址并将其设置为 ret 的值。我在这里没有做任何会改变包含在 func 的返回地址位置中的地址的事情。

更新:我对程序进行了一些修改,所以它打印了 func 的参数 a 的堆栈地址。正如你所看到的,a 和 buffer 之间的距离大约是 8 个字节,所以这可能意味着,根据堆栈布局,保存的 FP 和旧的 EIP(func 返回地址)介于两者之间。我对吗?

这是程序:

void func( int a){
    char buffer[3];

    int *ret;

    ret = buffer + 11; // this is the configuratio which made the whole program works... This now points to the address containing func's return address

    printf (" address of a is %d\n", &a);

    printf ("address of buffer is %x\n", buffer);

    printf ("address of ret is %x\n", ret);

    printf ("value of ret is %x\n", (*ret));

}

void main(){
    int num;

    num = 0;

    func(num);

    num = 1;

    printf("Num now is %d", num);
}

执行时程序的输出:

替代文字 http://img20.imageshack.us/img20/2034/72783404.png

如您所见,我正在打印变量缓冲区和 ret 的地址。我添加了一条额外的语句,打印 ret 变量的值(假设 func 返回地址的位置,所以这应该打印下一条指令的地址,该指令将在 func 从执行返回后执行)。

这是转储,它显示了 func 返回后要执行的指令的假定地址。(绿色下划线)如您所见,该值与变量 ret 中包含的打印值完全不同。

替代文字 http://img717.imageshack.us/img717/8273/assemblycodecopy.png

我的问题是,为什么它们不同?(当然假设我所做的都是正确的)。否则,我做错了什么?我对程序运行时堆栈的理解是错误的吗?请帮助我理解这一点。我的项目将于下周到期,但我几乎没有碰过它。如果我要求很高,我很抱歉,我非常需要你的帮助。

4

3 回答 3

1

对于以下程序

int main(int argc, char **argv) {
   int v[2];

   return 0;
}

堆栈布局基本上如下:

 
       -------------   
           精氨酸
       -------------
         …………
       -------------   
0x1010 参数 0
       -------------
0x100C 恢复地址
       ==============
0x1008 旧 fp
       -------------
0x1004 v[1]
       -------------
0x1000 v[0]
       -------------

您可以使用 v + 3 找到 main 的返回地址。

假设放置在堆栈左侧的地址, v 的地址为 0x1000 ,返回地址的地址为 (v + 3 => 0x1000 + 4 * 3 = 0x100C)

于 2010-06-18T07:04:16.947 回答
1

首先,请注意缓冲区的地址是奇数0xbffffd51,然后将其加 8 得到0xbffffd59. 如果堆栈上的返回地址未与四字节地址对齐,我会感到非常惊讶。

根据编译器的不同,堆栈帧的确切布局方式可能会有所不同(例如,即使buffer在源代码中的第一个,编译器也可以将ret堆栈放在更高的位置),因此您可能需要试验您的值。我会做几件事:

  1. 将缓冲区更改为 4 个字节。
  2. 尝试不同的偏移量。我有一种感觉,您可能需要查找 12 个字节甚至 16 个字节才能找到您的返回地址。
于 2010-06-18T06:31:53.560 回答
0

当然,你不能修改原来的num,除非你把指针传给它;所以总的来说,num 首先是 0,然后是 1,并且它永远不会被 func 真正修改。func 中( &a) 的地址是参数的本地副本(按值)的地址,在大多数情况下可能是堆栈上的地址。ret 指向什么?你有一个 3 字符的缓冲区,并且你得到了超出它的地址;根据局部变量在内存中的“组织”方式,您现在必须将其视为指向垃圾的指针,即使您可能指向“有趣”的东西。所以你不能 100% 确定它确实指向返回地址。您假设以下内容:

0 4 个字节(对于 char,假设 4bytes 对齐)
4 4 个字节(不管是什么,也许是参数)
8 4字节(返回地址)

这取决于。这取决于架构;这取决于编译器如何“翻译”函数的代码。让我们想象一下 x86。以下是一种合理的 func 方式

功能:
  推送 ebp ;保存一些注册表...
  推动 eax ; 或与pusha?
  mov ebp, esp
  推 0 ; 对于字符 a[3]
  mov eax, ebp
  添加 eax, 4 ; -4 + 8
  推动 eax ; 对于 int *ret
  ; -4(ebp) 给出一个
  ; -8(ebp) 给出 int *ret
  ; 所以 ebp-4 是指向 a 的指针,我们
  ; 加8,得到ebp+4,这点
  ; 保存 ebp... 缺少 ret ptr
  ; (其他代码...)
  mov esp, ebp
  流行音乐; 还是和popa一起?
  流行音乐
  ret

如果保存的注册表更多怎么办?如果交换 char a[4] 和 int *ret 的顺序怎么办?你怎么能知道?您不能假设任何事情,除非您自己直接在 asm 中编写代码,在这种情况下,您可以准确控制正在发生的事情。否则,一个工作的 C 代码来做你想做的事情会偶然工作......

于 2010-06-18T09:31:26.807 回答