前言
- 错过了这场比赛,赛后补上。虽有WP可以参考,但是复现起来还是磕磕绊绊。我总不想去依赖wp,于是先自己去分析,一开始好多看不懂的代码,睡过去了,积极性受打击,遂搁置了一天。断断续续逆了3天,然后参考WP,终于弄懂了。
分析
- 程序无壳,用IDA分析。主要过程分为三步,每一步就是flag的一个组成部分。通过单步调试,可以知道flag的第一部分与第二部分用 @ 做分割,第二部分与第三部分用 # 做分割,并且除分隔符之外全是数字。
- 第一步:
- 将flag的第一部分转成数字,作为随机数种子,采用梅森旋转算法生成一个随机数序列,相关代码如下
- 这个函数中多次调用到一个我称之为StrToNumber的函数,其实就是将字符串转为数字形式。
- 梅森旋转算法:
- 生成的随机数要满足
- 将flag的第一部分转成数字,作为随机数种子,采用梅森旋转算法生成一个随机数序列,相关代码如下
((((((((v12 >> 11) ^ v12) & 0xFF3A58AD) << 7) ^ (v12 >> 11) ^ v12) & 0xFFFFDF8C) << 15) ^ ((((v12 >> 11) ^ v12) & 0xFF3A58AD) << 7) ^ (v12 >> 11) ^ v12 ^ (((((((((v12 >> 11) ^ v12) & 0xFF3A58AD) << 7) ^ (v12 >> 11) ^ v12) & 0xFFFFDF8C) << 15) ^ ((((v12 >> 11) ^ v12) & 0xFF3A58AD) << 7) ^ (v12 >> 11) ^ v12) >> 18)) == 0xD4CBCF03
-
-
所以可以通过爆破随机数种子来确定flag的第一部分。这里有两个方法可行:
1.从IDA中copy下来修改一下在IDE中爆破。
2.直接patch源程序,在源程序中进行爆破。 -
显然第二种方法比较好最省力,最不容易出错。因为不需要去考虑IDA的伪代码中函数的参数类型和返回值,这是最容易出错的地方。
-
我使用x64dbg找到对应的地方:
-
修改为
-
修改为
-
并在其下方下个断点,断下的时候观察一下寄存器rbx寄存器,就能得到flag1=78,然后记录最后得到的随机数为0x22,后面会用到。
-
- 第二步:
- 将flag的第二部分作为byte_140008530数组的索引,依次得到De1ta,反求得到flag2=20637。
- 第三步:
- 这是IDA识别不好的问题,曾困扰我多时。所以根据第一步得到的随机数0x22可以算出flag3=114。
- 故正确的flag为flag{78@20637#114}。
总结
- 遇到看不懂的函数就手动剥出来调试,便于理解。
- 逆向过程需要灵活机动,还要不厌其烦,必须有耐性。没有积极性真的很难坚持下去。
- 算法千千万,我们不可能都见过,都掌握,所以要善于利用百度谷歌,善于从常数中获得新发现。
- IDA的反编译结果存在很多误差,变量的类型通常都是错误的,需要手动调试多遍才能确定变量的类型和他们彼此间的联系,只要掌握了这些,后面的都不是大问题。