理解缓冲区溢出

这几天一直在做csapp里面的3.38,他是让你自己实现一个缓冲区溢出程序.代码如下:

[code]/* Bomb program that is solved using a buffer overflow attack */

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

/* Like gets, except that characters are typed as pairs of hex digits.
Nondigit characters are ignored. Stops when encounters newline */
char *getxs(char *dest)
{
int c;
int even = 1; /* Have read even number of digits */
int otherd = 0; /* Other hex digit of pair */
char *sp = dest;
while ((c = getchar()) != EOF && c != '\n') {
if (isxdigit(c)) {
int val;
if ('0' <= c && c <= '9')
val = c - '0';
else if ('A' <= c && c <= 'F')
val = c - 'A' + 10;
else
val = c - 'a' + 10;
if (even) {
otherd = val;
even = 0;
} else {
*sp++ = otherd * 16 + val;
even = 1;
}
}
}
*sp++ = '\0';
return dest;
}

/* $begin getbuf-c */
int getbuf()
{
char buf[12];
getxs(buf);
return 1;
}

void test()
{
int val;
printf("Type Hex string:");
val = getbuf();
printf("getbuf returned 0x%x\n", val);
}
/* $end getbuf-c */

int main()
{

int buf[16];
/* This little hack is an attempt to get the stack to be in a
stable position
*/
int offset = (((int) buf) & 0xFFF);
int *space = (int *) alloca(offset);
*space = 0; /* So that don't get complaint of unused variable */
test();
return 0;
}[/code]
这边有几个概念要说一下:
1 在一个函数的栈桢里面,有两个指针,一个是栈指针(存储在%esp),一个是桢指针(存储在%ebp),其中栈指针指向的是栈顶,也就是说它会不断变化,而桢指针指向的是栈低,他是固定的。而栈指针和桢指针之间方得就是,不能存储在寄存器中的一些局部变量.

2所有的寄存器被所有的函数所共享,因此这里IA32对寄存器去有了一个规定,那就是:

%eax,%edx,%ecx是调用者寄存器,%ebx,%edi,%esi是被调用者寄存器,举个例子,假设方法p调用q,则q可以随便覆盖%eax,%edx,%ecx中的而不会破坏p的使用,但是如果他要使用%ebx,%edi,%esi中的值,则必须先将这些寄存器进栈,谈后才能操作.

更详细的有关过程调用的介绍,可以自己去看csapp的第三章.

题目是让你输入一个16进制字符串,从而能使 getbuf返回0xdeadbeef.这个要实现的话,之要实现下面几个目标就可以了:
覆盖函数的返回地址(也就是test调用getbuf时压入栈的地址)。使这个地址指向你自己的执行代码,而你自己的执行代码所要实现的就是就是改变函数的返回值,修改完返回值之后,你要将返回你本该返回的地址,(这边就是函数test的地址)。

这边先看一下,函数调用结束时的汇编代码:

[code]leave
ret[/code]

leave指令相当于[code]
movl %ebp,%esp
popl %ebp[/code]
也就是说当函数要返回时,会先将栈指针指向桢指针所指的内容,也就是说删除掉了局部变量之类的东西,这时再弹出%ebp,这时的栈顶,也就是调用这个函数的函数地址了,而ret指令则就是弹出这个地址,然后跳转到这个地址。

知道了这个,我们就很容易构造我们的目标程序了,我们的目标程序也就是先将0xdeadbeef付给%eax,然后再将函数test的地址入栈,最后ret.这边假设我们的test的地址是$0x048503bf.
[code]mov $0xdeadbeef ,%eax
push $0x048503bf
ret[/code]

然后我们将这段代码先编译,然后反汇编,就能得到我们的目标代码.

所以,我们这边的构造的字符串的结构式这样的:
(目标代码)(当test调用getbuf时的%ebp的内容)(我们的目标代码的地址,也就是我们构造的这个字符串buf的地址,也就是%ebp的地址减去12)

这边为什么没有给出完整的程序呢,这是因为,在linux2.6下他对栈有保护,它使用一种叫做ExecShield的技术,简而言之就是他会使对堆栈内的代码不可执行,并且每次执行的时候会随即的改变堆栈的地址.因此如果我们要尝试缓冲区溢出的话,使用gdb调试,他会报一个segmentation fault的错误..

有关ExecShield的详细信息,可以看这个:

http://www.linuxgoo.com/bbs/archiver/58645.html
虽然没有实践成功,可是自己的理解应该是没有什么错误的.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值