BUUCTF [GUET-CTF2019]number_game

开始逆向!

把文件拖入到ExeinfoPE里面,发现无壳,用IDA打开。

现在你进入了IDA,按shift+F12进入字串列表

you用错了,句子罚写十遍!

一眼就看见了flag{ 。双击‘flag{’字串。Ctrl+x找到引用后按F5查看函数伪代码。

答案很明显了,这个v5就是flag。由于本人习惯从近到远,因此我选择先看flag所在这一块的if里的函数sub_400917。双击进入。

sub_400917

三层循环,看起来好像有点麻烦……硬看想看出来点啥还是有难度的,不如代数看看吧。为了方便,我把unk_601060简写为unk。

首先让i,j为0,k从1到4,两个if对应的分别是

unk+0==unk+1,2,3,4

unk+0==unk+5,10,15,20

后面同理。五个一组不能相等……该不会是5x5的数独吧?

点开unk看看,果然是25个,不过有些空位。估计会有用,先给他填上。

挺好写的,答案是0421421430。肯定有用,先记下来。

然后我们回到main函数,看看if上面的sub_40881

sub_40881

有点像是输入。双击这些byte_6010系列,你发现对应的就是上面unk_601060空缺的部分。看样子v7里面存的就是刚才数独的答案。其实这个时候你就可以去试试了,程序要在linux里面打开,输入后会发现是错的。

继续看上面的sub_400807。

sub_400807

完了,这回是真蒙了。好多都是用的动调,翻了好久才找到答案……原来这是一棵树的中序遍历……哪怕现在看我也看不出来……

不过动调确实爽。这个函数全是递归和赋值,并没有修改值的过程。那么我只要动调查v7的排序和输入排序的关系也能明白这个函数到底在干什么。

这里的a1(对应main里面的v4)明显是个数组,另外假如a1真就单纯是个数的话这个函数就是个死循环……

v4给v7提供值,现在疑惑就在v4身上了。看看400807上面的sub_400758。

sub_400758

值得一提的是,第八行if判断中在v5的值上面按‘r’键会直接转换成ascii码对应的字符,按‘h’键会在十进制与十六进制之间转换。

嗯……递归……2a+1和2a+2……

懂的也都明白是啥了。这其实就是在生成一棵树,方法就是横着排。如果你学过大小根堆的话印象应该会更深一点。

这里放一下小根堆的定义(来源百度百科)。小根堆的特点是任意根都不大于它的子树。

如果有一个关键字的集合K={k0,k1,k2, ..., kn-1}, 把所有元素按完全二叉树的顺序存储方式存放在一个一维数组中,并且满足ki <= k2i+1 且 ki <= k2i+2 (i = 0, 1, ..., (n-2)/2 向上取整)则称这个集合为小根堆。

最上面的if看不看其实也就那样了。大概率就是个输入判断。

整个流程还是比较清楚的:你输入v5,v4是用v5生成的一个奇怪东西(其实是树,假设没看出来),它经过某种排列(其实是中序遍历)后存到了v7里面,v7就是5x5数独的答案。

如果你成功的看出来他是棵树并且用的是中序遍历,那么恭喜你这道题解完了。这种方式形成的树结构固定,只要往里面代数就可以了。

这里给出flag

 flag{1134240024}

假如你没看出来,那么就要用到“逃课”方法:动态调试了。

动态调试

准备工作

首先你需要一个linux系统。没有的话优先推荐kali,里面有好多打misc能用到的软件。linux的具体安装方法网上有很多就不说了。然后启动,先把要调试的程序放里面。

值得一提的是,虚拟机和大部分安卓模拟器不共存。如果像我一样贪玩的话可以选择国际版的蓝叠模拟器。

之后,找到你ida的文件夹,打开里面的dbgsrv,把linux_server64拖到你的linux里面。

打开终端,输入

ip a

找到inet后面对应的ip地址,192.168开头的。

再输入

./linux_server64

来启动ida的服务端。如果成功会显示“Listening on 0.0.0.0:23946...”

它会默认占用23946这个端口。如果这个端口被使用会提示别的消息。一般来说重启一下虚拟机就好

打开ida中的调试器->选择调试器->remote linux debugger

注意,本人用的ida是中文破解版的,如果你的是全英的话可以参考我图中的位置。

从左往右第六个

之后调试器->进程选项

启动进程(绿三角)往下第二个就是

你会打开这么一个窗口。在主机名称里输入你刚才得到的192开头的ip,然后确定即可。

然后点击上方的绿三角启动就可以啦!

因为我这里犯懒没调文件位置,所以会有file missing这种提示。反正他也能找到

点击使用发现就可以了。

启动之后

你的ida就会进入一片蓝状态。虽然说变蓝了,除了多了几个窗口外和正常状态下并没有什么区别。

和刚才一样,shift+F12打开字串,或者左上角的跳转->跳转到函数(对应快捷键ctrl+p)找到main函数,按f5转化成伪代码

之后在伪代码那里右键->同步到->RIP,IDA View-RIP

其实不勾选也可以,不过这样会更方便一点。这回在伪代码中选中的行会变成绿色,同时在旁边的IDA View-RIP中也会以绿色提示伪代码选中行所对应的位置。

这时我们就要把前面没有分析的输入检测函数sub_4006D6分析一下了。

sub_4006D6

函数很简单,要求输入是十位并且在0-4之内(ascii码上‘/’的下一位就是'0')

这个时候问题就来了。前面已经知道程序操作是对数字进行某种排列,十位0-4之间肯定有重复数字,如果想要试出来对应位置就需要不止一次输入。我想输入0-9,该怎么办呢?

想解决很简单,要么让他返回1,要么就让他不跳转。

开始调试

如果选择返回1会稍微有点麻烦。在sub_4006D6里选择return 0,View-RIP中就会显示对应的部分

图中绿色部分就是

在mov eax,0那里右键->keypatch->patcher(对应快捷键ctrl+alt+k)

在Assembly里把0改成1 点patch就行了。

在我这里他会连着显示,第二个点取消即可

需要注意的是这里要修改两次,因为有两个判断(十位和0-4)。第一次修改后对应的代码会发生变化,按照上面的操作再进行一次就可以了。

第二种方法就是直接修改跳转。在main中找到第一个if对应的位置

右侧蓝字是因为我刚才修改过,忽略即可

按照上面的方法,把jz改成jnz就可以了。

汇编语言中jz表示为0跳转,jnz表示不为0跳转

后面同理,找到第二个if的位置并也把jz修改成jnz。

再往后划一点,插入一个断点以防止程序结束自动退出。我把断点插到了“}”的位置

右键->插入断点(对应快捷键F2)或者点那个小蓝点都可以。插入成功该行会变红。

图片中的jz没修改,应该是jnz哦

这回就万事大吉了。在linux的终端中输入数字0123456789即可。

先不用管linux输出的是什么,直接去ida看v7是什么。双击v7。

这时ida界面还是蓝色的,如果变白说明程序退出,你肯定是落了哪里

这样他的顺序就出来了。按照这个顺序对前面数独答案处理一下就可以了。

 flag{1134240024}

说点别的

说实话这道题用的东西确实不少,当时懒嘛遇到调试的题就想着先多练练静态的吧,结果连着好几道都是动调的题。这回了解了之后感觉原来真的没什么。

找的东西确实好多啊。又是动调方法又是查询ip又是各种在wp上看其他佬是怎么修改函数的……于是我就写详细了一点,别人看着也方便。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值