攻防世界 reverse-box 题目
原题来自 mma-ctf-2nd-2016: reverse-box
Description:
$ ./reverse_box ${FLAG}
95eeaf95ef94234999582f722f492f72b19a7aaf72e6e776b57aee722fe77ab5ad9aaeb156729676ae7a236d99b1df4a
题目flag格式:TWCTF{…}
main函数如下:
运行程序时输入的参数,会和一个数值相加,转化成十六进制,然后打印出来,
我们简单运行下如下:
另外汇编是:
这个位置,是一个查表的过程,函数sub_804858D
是一个建立表的过程,
重点在于:
后部分都是设置建立表,但是这个生成表的种子是随机的,也就是我们的表是不固定的。
题目就是对输入进行查表,然后打印出来,并且给出了正确flag查表对应的值,但是这个表是随机生成的,我们需要得到正确的一个表。
方案应该是只能爆破,并且rand()
函数调用的返回值,限制为int8
即0xff大小,最大为0xff,爆破次数不算太大,直接爆破出来正确的表,就结束了。
这时候我们面对一个问题,这个怎么方便的进行爆破?
下面我们写一下关于爆破的方法,
其中wp有的直接爆破出正确的,有的直接爆破出所有的再交python得到最后工作,这都是差不多的, 我们只讨论爆破的实现。
下面是我搜到一些wp中的解法:
其中大部分方法都可以看到链接。一个ida python的手段是我们的重点, 也恰好是在wp没有看到的一种方案。
相关的文章是这个, 比较详细的介绍了相关的函数。
脚本:
import idaapi
import idc
for i in range(1, 256):
RunTo(0x080485b1)
GetDebuggerEvent(WFNE_SUSP, -1)
print i
SetRegValue(i, "eax")
RunTo(0x08048704)
GetDebuggerEvent(WFNE_SUSP, -1)
bool = GetRegValue("eax")
RunTo(0x08048746)
GetDebuggerEvent(WFNE_SUSP, -1)
SetRegValue(0x080486D4, 'eip')
if bool == 0x95:
print i
break
RunTo(0x080485b1)
GetDebuggerEvent(WFNE_SUSP, -1)
print i
SetRegValue(i, "eax")
RunTo(0x08048704)
GetDebuggerEvent(WFNE_SUSP, -1)
stack = GetRegValue("esp")
arr_addr = stack + 0x1c
arr = []
for i in range(0xff):
arr.append(Byte(arr_addr + i))
s = '95eeaf95ef94234999582f722f492f72b19a7aaf72e6e776b57aee722fe77ab5ad9aaeb156729676ae7a236d99b1df4a'
a = []
for i in range(0,len(s), 2):
a.append(arr.index(int(s[i:i+2], 16)))
print ''.join(map(chr, a))
注意idc定义的的函数可以直接在ida-python中调用。
注意一点:关于
GetDebuggerEvent()
这个代码看起来很无用,但是不加会报错,
因为python脚本和调试器会异步执行,我们要获取寄存器的话应该要等待,直接过去是会报错,显示寄存器名错误,可以通过加入
GetDebuggerEvent(WFNE_SUSP, -1)
来解决这个问题,注意每次要读取修改数据啥的要加一个这个就可以了。
设置好debug options, 直接运行脚本,ida会自动开始动调,
然后运行脚本得到flag,
注意两点,
一个是对于这种要求调用时要参数的程序,我们在ida debug options中设置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AZwpjfBD-1586228644870)(image-20200405222503984.png)]
这样调试的时候,对应的参数就是"T"
这个是linux 下wine运行的ida因此为127.0.0.1
我们的脚本的循环中每次都是改掉eip调整程序运行的。ida-python 不能够自动循环启动多次调试,但是对于一般题目修改eip已经足够我们的使用。
在所有的wp解法中, 我认为最简洁和具有通用性的应该是ida-python的解法和gdb-python的解法,
这里简单写一下相关的使用。
gdb - python, 有一个官方文档, 然后个人觉得使用起来略显复杂,主要写下idapython
然后在动调主要使用到的函数:
AddBpt(long Address)
: 在指定的地址设置断点
GetRegValue(string Register)
:获取一个寄存器的名称
SetRegValue(long Value, string Register)
设置寄存器的值
RunTo(long Address)
: 运行到指定的地址,然后停下。
GetDebuggerEvnt(WFNE_SUSP, -1)
:应该是获取调试某一过程中信息,一般就在RunTo()
后和操作寄存器前使用,解决脚本和调试异步而导致的寄存器读取报错的问题
一般使用的:
RunTo(xxxx)
GetDebuggerEvent(WFNE_SUSP, -1)
SetRegValue(xxx, 'xxx')
xxx = GetRegValue("xxx")
RunTo(xxx)
GetDebuggerEvent(WFNE_SUSP, -1)
stack = GetRegValue("esp")
这样,运行到某个位置,获取esp/rsp的值,为栈顶,通过偏移可以获取相对应的栈内地址,然后使用Byte()
/ Dword()
来获取相关数据,如果需要的话我们还可以通过PatchByte(long Addr, byte)
来进行数据的修改,