2022 D3CTF
**D3MUG**
这是一道安卓unity逆向,与Mono打包出来不同的是,这道题用的IL2CPP方式出包,所以不能直接从包中得到Assembly-CSharp.dll,需要借助与IL2CppDumper工具来得到Assembly-CSharp.dll。
取出包内的global-metadata.dat和libil2cpp.so放入在IL2CppDumper中创建的input文件夹中,并且再创建一个output的文件夹
在命令行中执行
..\Il2CppDumper.exe libil2cpp.so global-metadata.dat ..\output
然后在output中查看结果
接下来用ida加载libil2cpp.so,并且导入符号,这里要有耐心,加载libil2cpp.so就需要很久的时间,利用这段时间可以去喝口水,上个厕所然后回来继续。等加载完候再开始导入符号,不然会产生不必要的错误,在加载符号时ida会无响应,注意不是ida卡死了,在window在判断一个程序无响应的机制是看这个窗口是否还在成功的处理分配给它的消息。这里的ida无响应但是其实它是在工作的,再去喝口水休息一下,刷一刷bilibili等待ida处理结束。
具体的导入符号的步骤就是利用IL2CppDumper文件夹里面给的ida插件就行
选择script.json
导入头文件
漫长的等待后,字符串定位法
推测框中部分是一个加载d3mug.so的函数,类似于windowsAPI的loadlibiry。
所以这函数调用了d3mug的update函数,通过函数名推测应该是起到刷新作用的函数
接着查看与这个名字相似的函数
这个函数与点击相关,进去看看
同样调用了update函数来更新点击后的结果吧。调用undate函数的时候传入了一个大概是时间的东西。
跟这这个点击的函数不断上交叉引用,找到一个死循环,这应该就是这个游戏的主逻辑
对这个NoteObject函数的分析得出,传入update的时间是通过下面这个东西得出来的
交叉引用它
这个函数名叫,”加载节拍地图“,跟过去分析函数,对一些函数进行分析不难发现如图的函数与字符串
这个字符来自timepoint,和hitpoint
这里没有具体数据,使用AsetStudioGUI来获取这个程序的资源
一共三张地图三组数据,每一个数据的前一位为点击的位置,后一位为点击的时间,使用frida hook来获取flag,目前本人水平有限还没学
D3arm
给了一个bin文件,逆向这种文件的详细链接(https://bbs.pediy.com/thread-249844.htm),下面初略的理解一下它讲的内容,直接拖入ida
在进入界面别着急点,先选择arm小端(双击才是选择)。
它的栈什么的位置刚进去还不知道,直接点ok进入。
alt+g 填入不同的数字可以弄不同的位数,在最开始的两个位置按d寻找基址
这两个一个是(上面)栈基址,一个(下面)程序载入基址
看到这两个数直接猜测栈基址为20000000,程序基址为8000000
这个主要是猜测,如果不成功就换
然后删除文件,重新开始填入数据
然后全选shift+end(笔记本上的按键)+end 按c
有函数了,大成功
然后直接通过字符串定位到main函数
这里在进入main函数的时候,ida分析的不太好,需要进入一些函数,然后退出来按f5刷新函数的参数。图中的函数地址就是,真正执行的函数,我理解为就像windows创建的一个线程一样,把这个函数的地址传入一个调用的函数,这个调用的函数就会创建一个线程把这段代码跑起来。
进入这个真正的主函数
没有伪代码可以看了,由于本人水平有限,不会arm汇编,所以把它优化不成伪代码。我们直接看汇编,只看一些函数调用还是没有问题的。
通过对于里面仅有的两个函数的探索,在红框的函数里面有一个死循环,我见过一个是一个游戏消息的循环。
第一个函数
通过字符串,猜测它是记录分数的字符串
第二个函数
猜测是开始函数
最后一个函数
判断是否结束,输出是否是正确的,然后输出flag,这里感觉红框的部分为输出的flag,所以交叉引用看看处理的过程。
很明显加密函数
整个表达式,就一个数不知道,交叉引用查看,这个数怎么来的。
只有两处在进行写入,第一处将这个数初始化,第二处将它与index向关联
所以解题脚本就出来了
a =[0x00000020, 0x0000006D, 0x00000050, 0x00000030, 0x00000038, 0x00000048, 0x00000020, 0x0000006C, 0x00000007, 0x0000007D, 0x0000006C, 0x00000055, 0x0000007D, 0x00000068, 0x00000003, 0x00000027, 0x00000066, 0x00000000, 0x00000022, 0x00000069, 0x00000002, 0x0000007D, 0x0000006D, 0x0000000B, 0x00000077, 0x0000003B, 0x00000002, 0x00000027, 0x0000003B, 0x00000050, 0x00000073, 0x00000038, 0x00000004, 0x00000071, 0x0000003B, 0x00000050, 0x0000007D, 0x0000006A, 0x00000052, 0x00000075, 0x0000006D, 0x0000004E]
for i in range(42):
print(chr((0x335E44 >> (8 * (i % 3))) & 0xff ^ a[i]), end="")
#d3ctf{d2492f960c83f719383e1cec7f75ec94a13}
D3WOW
天堂之门反调试,可以看我的之前的博客有过介绍(http://s0rry.cn/index.php/archives/13/),一种在32位进程运行64位代码的技术。我们直接把64位部分的代码dump下来放入ida分析就行,ida dump脚本如下:
static main()
{
auto i,fp;
fp = fopen("dump","wb");
auto start = 0x0040122C;
auto size = 0x00402038;
for(i=start;i<size;i++)
{
fputc(Byte(i),fp);
}
}
这是一道迷宫题(大概吧),先对32位的输入函数进行分析:
这是一个应该是在走迷宫,走过的路径上下通过2和8标记,上为2下为8,没有方向。同理左右通过1和4标记。
对64位部分分析
第一部分
对边界再次进行判定,并且规定每个方块要经过两次,到这个方块算一次离开这个方块算一次,这算是一种检验。
第二部分
对于三个点进行了限制,在这三个点上必须转向。
第三部分
对于10个点进行了限制,这里一连串的条件,有点离谱。要理解ida的伪代码,我看syc的ida弄出来的伪代码
还很奇怪,为什么我的ida弄不出这种类型,看了一下汇编发现源代码还真与上面的一样在前一部分条件就会有一个退出判断。
但是这里ida表示的意思也与上面的代码的意思是一样的,但是对于分析来说是十分困难的。所以要注意理解ida弄出来的if()里面一连串的”||“和“&&”,“||”相当于再并列一个if,举个例子:
if(a||b||c){
...
}
等于
if(a){
...
}
if(b){
...
}
if(c){
...
}
“&&”相当于if里面嵌套一个if,比如:
if(a&&b&&c){
...
}
等于
if(a){
if(b){
if(c){
...
}
}
}
理解了这个就很好判断了,适当的把这些条件分开就能推测出这些点的约束条件。
必须直线通过这些点,并且在通过这些点必须有转角。
最后一部分
没有其他特殊点了,我们直接大胆猜测这部分的功能就是对所有的限定点进行判断,看看是否完全经过。
官方wp给出的图就很好理解了,走完就是flag。
d3ctf{22441442223133324424441111133333}
d3thon
通过wp知道是vm,打开查看代码,ba里面是解析代码的,而bcdoe.lbc里面是vm代码。
ida看了一下analize的代码太复杂啦,更着syc的wp学直接猜指令
kZslMZYnvPBwgdCz print
oGwDokoxZgoeViFcAF 赋值(如果赋值为KezJKhCxGRZnfLCGT, 则为输入
RDDDZUiIKbxCubJEN jmp
todeVDuRkYSIITaT 转为2进制
uPapnsSbmeJLjin 转为10进制
kuhisCvwaXWfqCs ~
IEKMEDdrPpzpdKy add
OcKUQCYqhwHXfAgGZH xor
FLNPsiCIvICFtzpUAR sub
OuGFUKNGxNLeHOudCK cmp
对于这个指令的推测,我的看法是把vm常用的几个指令与它匹配,如果合理就是这个指令
对整个代码分析一下,把非01的字符转为ord值, 再转为2进制, 2进制字符串串联起来, 转为10进制, 最后进行运算,最后与一个数-194952731925593882593246917508862867371733438849523064153861650948471779982880938比较
把okokokok部分代码提取出来
由于这些运算符号都是可逆的所以直接反着用,就逆回去了
res=-194952731925593882593246917508862867371733438849523064153861650948471779982880938
with open("/home/kali/Desktop/re/d3thon/okokokok.txt","r") as f:
opcodes=f.read().split(",")
for i in opcodes[::-1]:
i=i.replace("\'","")
if "IEKMEDdrPpzpdKy:flag:" in i:
res -= int(i.split(":")[-1])
elif "FLNPsiCIvICFtzpUAR:flag:" in i:
res += int(i.split(":")[-1])
elif "OcKUQCYqhwHXfAgGZH:flag:"in i:
res ^= int(i.split(":")[-1])
elif "kuhisCvwaXWfqCs:flag" in i:
res=~res
else:
raise SyntaxError
print(hex(res))
#0x3437323961346136626264643464373863393465363232393235376166333565
#将16进制转换为字符串4729a4a6bbdd4d78c94e6229257af35e
总结
还有一个题没复现(绝对不是我想偷懒),我题目附件丢了,懒得网上去找了。总的来说这一次收获很大,再一次拓宽了我的知识面,学会了对于完全陌生的东西逆向,都是靠着符号名字来逆向的,比如说一个变量名,一个函数名,一段字符串什么的。类似于通过只言片语来了解整个函数的功能,由于以前都是直接看整个代码逻辑,对于这种大范围的逆向去详细的弄是很不适应的,所以说代码是符号是十分重要的,要学会看函数名,变量名快速定位重要函数。