线程守护的题目
ctf_game wp
师傅给的任务,
这儿题目考察的是线程守护,
前面一个hgame week4 secret的题目为父子进程发送信号, 可以同时看看,
链接: https://pan.baidu.com/s/1NC6sjsELWTGHNCuWUflhqg 密码: csdr
分析函数
最开始看到.bak
的后缀,然后以为是个啥备份文件,然后拿来以后犹豫了下, 用file
指令查看了下,是个elf, 还是64位,
然后直接运行一下,一开始一个简单的介绍, 进去大概就是个抓娃娃机, 然后发现随机的动, 而且跳得地方看起来也没啥规律, 移动过去抓住, 然后等级高了以后发现动的几率增大了, 到后面还经常抓住了以后滑落下来,
那大概有个底, ida看一下, 文件还是比较大, 看起来不是很一般的题目,静态分析,再动调几次基本可以确定main函数和其中的调用的几个函数:
main函数
main函数前面是输出那段提示信息,然后进入这个大循环, 就是这个游戏的运行了, 显示不断循环获取输入,然后根据这些字符决定运行的函数,
但是这里注意到有一个v
是在提示中并没有出现的,后面的提示说debug模式,也就因此命名了下函数,运行输入v,发现总是显示不再debug模式。
然后后面是一次运行后的收尾部分。
首先是读取一下用户的生命值, 然后判定是否是生命值为0游戏结束了,
然后下面吧一个数据增大了,之后由这个数据和我们的等级决定是否进入某个函数,这个位置得动调一下, 确定这个函数:
然后会发现每次运行到这个函数的时候F的位置才会变化,这个函数将f位置随机变化,是rand_fun
,然后前面的数据, 决定了是否进入这个函数为rand_data
, 其他两个函数左右移动函数确定下来里面的F的位置F_position
和爪子位置position
两个量, 就还是比较简单的看明白,
cat函数
主要这个cat函数看起来比较复杂, 调试会看到其实左右的两个循环并不需要去管,我们看下真正判定是否抓住的位置只在中间的位置;
主要的三个判定都在这个位置, 然后我们可以将jnz
和jz
互换,
但是这个cat函数会出现掉落的情况,所以里面的判定, 中间那个判定的位置是时好时坏,
得到flag1
这个时候可以直接全nop掉,然后保存到源文件,直接去运行,直接s五次就可以拿到前一半flag,
但在这个想法之前,我尝试直接修改了usr_level
, 但是随即程序结束,然后提示有数据被修改了,所以nop掉以后直接s下去才行:
这里是第一半flag, 然后前面的数字不知道啥意思,下面提示是关于线程的,说在一个竞争中得到剩下的一半flag
线程
其实在分析时 就看到了许多奇怪的函数了结构的出现:
首先是提示中也出现的ThreadSanitizer
, 就是在多线程中为了保证数据访问时不会混乱,c++设置的工具,也是程序中随处可见的:
还有这个锁的结构就引起了我一点怀疑,这个是在多线程中读写数据为了保持数据不会在线程之间混乱, 进行原子操作设置的锁结构。
而提示也说到在一个race
, 想到了data race
,
data race
而这时候也是察觉到了多线程这个要素,但是比较无从下手,程序运行后输出提示,查询单线程, 进入游戏后,有五个线程:
这时候也是有点盲目了,还有个v的debug模式没使用过,应该会有一些问题,
在main函数时就是debug_data
在判定是否进入debug函数
然后在debug函数内部也是一直在判定debug_data
,
然后其中的add_life()
和level_up()
就是字面意思,直接加血和等级,但是里面也判断一首debug_data
,而debug_on()
就是直接复制debug_data = 1
这里注意一点,前面的读写数据都是在pthread_mutex_lock
和pthread_mutex_unlock
之间,但是这个debug_on()
,
却是这样的, 这个地方就是存在一个data race
,
然后就尝试调到这里,中间的确遇到一些艰难,里面的每次都要检测debug_data
是需要都patch掉,
然后调的时候有时候会进行报错,说是data race
, 大致如下:
然后下一次再次调试过去的时候发现给回显了一个flag地址:
但是我们没有断到这个位置,下次运行的时候地址已经没有了, 应该找到这个函数的位置, 下端,然后在这个提示的时候过去查看flag。
线程守护
查找字符串,找到了这个don't modify me
的位置, 一共在三个函数中,其中一个为检测debug_data
数据的:
而且三个函数都在这个函数中被调用,
可以看到是产生新线程, 然后这三个线程从这三个函数开始运行, 三个函数差不太多,就是在一个while()
里面, 如果数据改变啥的, 就会break
出来,
这是个多线程守护数据,
然后在去找下, 得到了这个:
一堆的这个ThreadSanitizer
, 其中比较引起注意的是这一句,然后找到这个函数, 下断, 调试的时候直接完成游戏,level=5 的时候就会运行到这里,这就是我们的: ‘find a race for the left flag’。
还有这仨位置:
头两个是关于第一次输出的flag, 第三个是我们的报错的时候出现的字符串,找到他们对应函数下断,
调试得到flag
注意当level为1的时候会进入报错,当level为2就可以看到他告诉说flag的位置,但是如果去的晚的话就是:
然后动调到修改debug_data
的位置,运行的时候会被拦截下来,
然后跟着动调, 慢慢的会出现那个指示flag的位置的地方,
然后在显示"flag at xxx"的时候过去, 就看到了另外一半flag:
合并起来就是我们的整个的flag