2021 第十四届全国大学生信息安全竞赛初赛两道逆向解题报告(writeup)

Posted by HX on 2021-05-16 | 👓

又只做了两道水题,太久没打了,啥都不会了qaq

0x0. glass - Reverse

拿到一个 apk 文件,先反编译。

经过checkFlag函数检查,对就输出right!这个函数是 native 函数,在 libnative-lib.so 文件里,用 7z 提取出来拖进 IDA。

checkFlag函数就是这样,v3是用户输入,检查长度为 39,然后传入了sub_1088。进去sub_FFCsub_1088可以认出是 RC4 加密,前者初始化 S 盒,后者加密:

密钥就是checkFlag函数里面那串12345678。RC4 结束后再给sub_10D4处理,进里面看下。

分上下两个循环处理,第一个循环以三个字符为一组(组密码),加密产生新串,第一、三字符异或得到新串第一字符,三字符一起异或得到新串第二字符,第二、三字符异或得到新串第三字符,如下(新串第二个字符少画了个异或符号):

第二个循环就是每个字符自己一组异或(流密码),解密直接和加密算法相同。第一个循环的解密需要改一下代码。以下是这整个函数的解密代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int v5;
char v6, v7, v8;
int a4 = 8;
int a2 = 39;
char a3[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
char result[39] = {0xa3, 0x1a, 0xe3, 0x69, 0x2f, 0xbb, 0x1a, 0x84, 0x65, 0xc2, 0xad, 0xad,
0x9e, 0x96, 0x5, 0x2, 0x1f, 0x8e, 0x36, 0x4f, 0xe1, 0xeb, 0xaf, 0xf0, 0xea, 0xc4,
0xa8, 0x2d, 0x42, 0xc7, 0x6e, 0x3f, 0xb0, 0xd3, 0xcc, 0x78, 0xf9, 0x98, 0x3f};
char *addr = result;
for ( int j = 0; j < a2; j += a4 )
{
for ( int k = 0; (a4 & ~(a4 >> 31)) != k && j + k < a2; ++k )
*(char *)(addr + k) ^= *(char *)(a3 + k);
addr = addr + a4;
}
addr = result;
for ( int i = 0; i < a2; i += 3 )
{
v5 = (int)addr + i;
v6 = *(char *)(addr + i + 2);
v7 = *(char *)(addr + i + 1);
v8 = *(char *)(addr + i);
*(char *)(addr + i) = (v6 ^ v7) & 0xff;
*(char *)(v5 + 1) = (v8 ^ v7) & 0xff;
*(char *)(v5 + 2) = (v8 ^ v6 ^ v7) & 0xff;
}
for (int i = 0; i < a2; i++)
printf("%x ", result[i] & 0xff);
return 0;
}

result数组是从checkFlag函数最后memcmp的参数提取出来的。

运行得到:

f8 ba 6a 97 47 ca e8 91 c5 7 6e f7 92 b 39 92 14 a8 af 7e aa 50 45 8d 6d 2d b6 86 6e 9f 86 5e df b3 1e 52 a6 62 6a

拿去 RC4 解密一下,密钥 12345678,就得到 flag:

\[ CISCN\{6654d84617f627c88846c172e0f4d46c\} \]

0x1. baby_bc - Reverse

拿到一个 .bc 后缀文件,搜索一下,是 LLVM bitcode,用llvm-dis工具反汇编成 LLVM IR,然后再用 GitHub 上面一个叫llvm2c的工具转成 C 代码,丢进 IDE 里改一改就可以编译出可执行文件。(5/17 更新:看了其他师傅 writeup,.bc 文件可以用llc直接生成目标文件再用gcc链接,这里画蛇添足了)再拖进 IDA 分析。

主函数先检查输入长度为 25,然后检查每个字符必须是数字 0-5。

进入fill_number看下。

里面基本都是这个结构,就是按照输入在填充map这个矩阵,输入 25 字符对应矩阵的五行五列,map里面本身在第三行第三列和第四行第四列填好了数字,输入的对应位置必须是 0,否则检查不通过。

最后进入docheck看下。

里面分三部分,第一部分检查矩阵的每行每列没有重复数字,第二部分和第三部分对行列上填写的数字做了一些大小的约束。实际上这个程序就类似在解数独,只是加了一些约束条件。

根据条件解数独,得出结果如上,所以最后正确的输入就是 1425353142350212150442315,flag 就是 CISCN{这串字符的MD5}。 \[ CISCN\{8a04b4597ad08b83211d3adfa1f61431\} \]