mrctf2020_shellcode
类型:pwn、shellcode
首先检查文件

大概了解情况之后,根据题目标题,我大概知道是shellcode,然后打开IDA查看一下代码长啥样。

一整个流程是这样的,然后F5也没有办法正常反编译成伪C。

整个使用流程大概如此。
再回到汇编,在输出之后可以让我读取值。因为call了read函数
1 | #include <unistd.h> |

这边题目给了不少备注,在程序的一开始用了一个buf什么-410h,这个就是rbp的缓冲区。然后在这个下面的时候edx400h的意思是给read函数提供了400h的缓冲区。也就是说我可以输入400的内容并且不会栈溢出。
通过lea操作符,rax获取了rbp+buf的地址,如果main函数栈从0开始,那么rax现在值就是栈中地址-410h。
通过read函数不难发现,读取值的void *buf是一个空类型指针,将rax的地址交给他开始读取数据。此时此刻,栈中的数据会被rsi寄存器(这边的寄存器被当作指针用,获取string数据)
在这里,程序要获取400h的值,那相当多了。获取这些数据要干嘛?接着往下找:

然后,通过一个jg进行跳转,我们先不管这个jump greater的条件。不难发现当判断正确的时候(也就是蓝色的线),会再一次把rbp+buf这个地址交给rax,此时此刻rax就是指向我们刚刚才输入进去的数据的。然后使用了call指令,也就会导致我们输入进去的数据在栈中会被执行。
我们再回来看这个比较到底是啥:首先mov了eax的值到rbp+ver_4的位置,然后将0和这个值进行比较,如果大于那么就跳转到蓝色部分,否则红色部分。
不过在read函数调用之前我们就发现,mov eax,0已经把eax寄存器中的值变成0。

在gdb调试后的时候非常神奇,我们的eax虽然被归0了,但是似乎使用完read函数之后,他就莫名其妙的得到了一个0xA的值(并非莫名其妙,最后会讲)。不管如何,他现在反正是大于0的。于是乎我们接下来输入的东西就会被正常执行了,可喜可贺。
1 | from pwn import* |
直接用自带工具构造一个shellcode做成payload,一把梭直接结束。

ls后flag在根目录下,直接cat,然后结束。
孩子们,我们是不是忘记那个奇怪的eax怎么获得0x0A的值了?
是的,现在我们来回顾一下这个傻逼read函数:
1 |
|
fd.、.buf.、.count分别对应edi,rsi和edx三个寄存器。那么ssize_t类型的返回值去哪里了?
我们做一个实验,其实有理论可以直接说,不过实验对于我而言印象会更深刻。

我们写一个带有返回值的函数,然后编译之后gdb调试一下:


当我们把add中的return给运行完的时候,返回值也就是10+20==30,30也就是0x1e的值出现在了rax寄存器上,也就是说,我这个函数运行完之后的返回值会返回到rax寄存器上。而_read函数的返回值是读取了多少数据他就返回多少,于是乎在我们运行完之后,rax的值变成了0x0A。
也就是说,返回值会默认给0x0A。所以我可以逆向一下,大概源程序的代码是这样的:
1 |
|
这个只是伪C代码,大概这样写不能还原题目。就这样吧…









