在这里仅给出我个人解出的题目的WriteUp.
Customize Virtual Machine:
查看main:
1 | __int64 __fastcall main(int a1, char **a2, char **a3) |
注意到是输入的字节SMC解密代码,之后有一个426字节的vm加载密文并进行解密,由于flag是我们的输入所以我们其实不需要关注后面的vm部分,仅仅需要注意50个funcs的解密,而funcs的每一块都由输入的一个字节进行异或,观察这些funcs:
1 | .text:00005555555551F0 unk_5555555551F0 db 0EEh ; DATA XREF: .data:funcs↓o |
发现其中绝大多数都有多个连续的三个相同字节,且值恰好在给定的flag字符集范围内,猜测是同一个dword的三个连续高位0字节,带入输入测试发现确实如此,50个块中有45块均可被这样解密,剩余的5块发现其中两个的值大于0x7F,说明其在被异或之前应该是负数(0xFFFFFF??),还有两个为b+1,b,b,推测是大于1个字节,第二位的b被覆盖,而倒数第四个函数块没有找到类似的结构,经过推测猜测首字节应该为异或(0x83),经过测试发现确实如此,提取得到输入c9z2cn9jmvkh30aqjwrb3urxtkp10q8b0vr_9dbfrocalkn1v5
flag{c9z2cn9jmvkh30aqjwrb3urxtkp10q8b0vr_9dbfrocalkn1v5}.
drillbeam:
查看java层:
1 | package com.primite.drillbeam; |
输入值只能是0~f,在java层拿到密文8c1ee8d42e211d6b649e0b9c33bdc836fc912709
,进入so层分析,查看所有函数,找到一个最关键的:
1 | void *__fastcall XXTEA(const void *src_1, size_t n, __int128 *a3, _QWORD *a4) |
这是一个典型的有长度填充的XXTEA加密,查看密钥和delta值,发现delta值在这个函数中被初始化:
1 | void sub_217C() |
Key从外界传入:
1 | void __fastcall brf3(__int64 a1, __int64 a2, __int64 a3) |
其中xorf1将其第二个参数解密得到密钥114514,在后续的XXTEA中填充为16位,为了确认题目采取的加密形式,编写frida脚本hook Java层:
1 | Java.perform(function () { |
经过观察比对发现,虽然要求输入的是0~f的值,猜测按两个字符为一个字节进行解析,但是实际测试发现密文的长度与字节数量并不成4的比例,反而与字符数量成4的比例,结合so层的scanf %02x没有任何xref猜测实际上传入的还是字符串,在加密时还会按ASCII值进行解析,编写同构加解密测试脚本发现结果并不相同,又因为密钥是从函数外界传入的参数,而delta值是全局dword,猜测可能暗改了delta值,编写脚本进行爆破:
1 | def b2dle(byte_array): |
发现delta值被改为了0x7c1ca759
,测试加密结果与程序相同,但是在解密时发现解出来的东西一团乱麻,不符合之前输入的格式,仔细观察整个程序的所有函数,发现还有sysconf获取系统状态,popen管道创建与pclose管道关闭,mmap内存映射等,猜测还含有检测系统状态的反hook与反调试等,由于实在是不易分析,尝试从密文直接爆破flag,由于有填充,最后一个dword一定小于256,所以解密结果的最后6字符一定为000000,编写脚本爆破全部4字节:
1 |
|
筛选输出,发现其中有一个解:Delta: 0x7c1ca806 | Hex: 6165313466623332396265353138626310000000符合结果,flag{ae14fb329be518bc}.