CISCN2024 Qual writeup

简单题是很水,没做出来的题也很让人崩溃。

asm_re

读ARM64汇编,不用F5,纯靠基本功。(初赛rev签到题该有的样子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdint.h>
#include <stdio.h>

uint8_t secret[] = {0xD7,0x1F,0,0,0xB7,0x21,0,0,0x47,0x1E,0,0,0x27,0x20,0,0,0xE7,0x26,0,0,0xD7,0x10,0,0,0x27,0x11,0,0,7,0x20,0,0,0xC7,0x11,0,0,0x47,0x1E,0,0,0x17,0x10,0,0,0x17,0x10,0,0,0xF7,0x11,0,0,7,0x20,0,0,0x37,0x10,0,0,7,0x11,0,0,0x17,0x1F,0,0,0xD7,0x10,0,0,0x17,0x10,0,0,0x17,0x10,0,0,0x67,0x1F,0,0,0x17,0x10,0,0,0xC7,0x11,0,0,0xC7,0x11,0,0,0x17,0x10,0,0,0xD7,0x1F,0,0,0x17,0x1F,0,0,7,0x11,0,0,0x47,0xF,0,0,0x27,0x11,0,0,0x37,0x10,0,0,0x47,0x1E,0,0,0x37,0x10,0,0,0xD7,0x1F,0,0,7,0x11,0,0,0xD7,0x1F,0,0,7,0x11,0,0,0x87,0x27,0,0,};

int main()
{
uint32_t *ss = secret;
char c;

for (int i = 0; i < 38; i++)
{
c = (((ss[i] - 0x1e) ^ 0x4d) - 0x14) / 0x50;
printf("%c", c);
}
return 0;
}

android_re

先用Frida拿到Key和IV,然后解密即可。

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
37
38
39
40
41
42
43
44
45
46
47
import frida

ss = """
Java.perform(function () {
console.log("Java.perform is called"); // 打印日志
var jniClass = Java.use("com.example.re11113.jni"); // 获取类引用
var getkey = jniClass.getkey;
getkey.implementation = function () { // 替换函数的实现
send("getkey begin");
var res = this.getkey();
send("key: [" + res + "]");
send("getkey end");
return res;
};

var getiv = jniClass.getiv;
getiv.implementation = function () { // 替换函数的实现
send("getiv begin");
var res = this.getiv();
send("iv: [" + res + "]");
send("getiv end");
return res;
};
});

"""
device = frida.get_usb_device()
pid = device.spawn(["com.example.re11113"])
session = device.attach(pid)
script = session.create_script(ss)

def on_message(msg, _data):
if msg["type"] == "send":
payload = msg["payload"]
print(f"[*] {payload}")
else:
print(msg)


script.on("message", on_message)
script.load()
input("==========")
print("ok")

device.resume(pid)

input("==========")

输出:

1
2
key: A8UdWaeq
iv: Wf3DLups

进行解密:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
public class SymmetricEncryUtils {

//AES ,DES
// 算法/模式/补码方式
//与给定的密钥内容相关联的 密钥算法的名称
// private static String ALGRITHM = "AES/CBC/PKCS5PADDING";
private static String ALGRITHM = "DES/CBC/PKCS5Padding";


// private static String TRANSFORMATION = "AES";
private static String TRANSFORMATION = "AES";


public static byte[] generateRandomString() {
Random random = new Random();
//AES秘钥规定是16位秘钥
//DES 8bytes
byte[] rand = new byte[8];
random.nextBytes(rand);
return rand;
}

private static final byte[] ALIAS;
private static final byte[] IV;

static {
try {
ALIAS = "A8UdWaeqA8UdWaeq".getBytes("UTF-8");
IV = "Wf3DLups".getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}


public static String encrypt(String content) {
try {
// 创建秘钥
SecretKeySpec keySpec = new SecretKeySpec(ALIAS, ALGRITHM);

// 创建密码器
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
// 初始化加密器
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
// 加密
return Base64.encodeToString(cipher.doFinal(content.getBytes("UTF-8")), Base64.NO_WRAP);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return "";
}

public static String decrypt(String content) {
try {
byte[] keyBytes = ALIAS;
Arrays.copyOf(keyBytes, 8);
// 创建秘钥
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, ALGRITHM);
IvParameterSpec iv = new IvParameterSpec(IV);

// 创建密码器
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
// 初始化解密器
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
// 解密
return new String(cipher.doFinal(Base64.decode(content, Base64.NO_WRAP)), StandardCharsets.UTF_8);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
}
return "";
}
}

gdb_debug

跟GDB没关系,也不用debug。直接爆破16个seed。

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

void do_dec(int seed)
{
srand(seed);
char secret[] = "congratulationstoyoucongratulationstoy";
uint8_t keys[] = {
0xBF, 0xD7, 0x2E, 0xDA, 0xEE, 0xA8, 0x1A, 0x10, 0x83, 0x73,
0xAC, 0xF1, 0x06, 0xBE, 0xAD, 0x88, 0x04, 0xD7, 0x12, 0xFE,
0xB5, 0xE2, 0x61, 0xB7, 0x3D, 0x07, 0x4A, 0xE8, 0x96, 0xA2,
0x9D, 0x4D, 0xBC, 0x81, 0x8C, 0xE9, 0x88, 0x78, 0x00, 0x00};
char data[40];
char data2[40];
memset(data, 0, sizeof(data));
for (int i = 0; i < 38; i++)
{
data[i] = secret[i] ^ keys[i];
}
uint8_t finalXorKeys[40];
for (int i = 0; i < 38; i++)
finalXorKeys[i] = rand() & 0xff;

char SBox[40];
for (int i = 0; i < 38; i++)
SBox[i] = i;

for (int i = 37; i; i--)
{
int j = rand() % (i + 1);
int tmp = SBox[i];
SBox[i] = SBox[j];
SBox[j] = tmp;
}

for (int i = 0; i < 38; i++)
{
data[i] ^= rand();
}

for (int i = 0; i < 38; i++)
{
data2[SBox[i]] = data[i];
}

for (int i = 0; i < 38; i++)
{
data2[i] ^= finalXorKeys[i];
}

if (data2[0] == 'f' && data2[1] == 'l' && data2[2] == 'a' && data2[3] == 'g' && data2[4] == '{' && data2[37] == '}')
{
for (int i = 0; i < 38; i++)
{
printf("%c", data2[i]);
}
printf("\n");
exit(0);
}
}

int main()
{
for (int i = 0; i < 0x100000000; i += 0x10000000)
{
do_dec(i);
}
}

whereThel1b

有符号,静态就可以差不多看懂。不过远程调试attach python然后给函数下断点也很方便。
相关结构体可以靠官方文档和猜。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]

b = []
import base64
my_enc = [53, 102, 39, 25, 108, 82, 103, 41, 103, 100, 51, 12, 51, 113, 123, 69, 19, 37, 50, 115, 25, 70, 98, 121, 57, 91, 9, 14, 121, 35, 2, 50, 62, 26, 53, 122, 126, 91, 27, 97, 42, 59, 17, 66, 101, 16, 74, 69, 110, 127, 8, 57, 45, 77, 0, 85, 97, 57, 4, 73, 94, 48, 51, 46, 44, 86, 77, 52, 50, 100, 28, 1, 61, 2, 22, 104, 23, 104, 47, 7, 115, 72, 27, 65, 3, 56, 78, 112, 44, 31, 90, 118, 63, 69, 32, 45, 96, 92, 76, 64, 41, 22, 49, 86, 84, 113, 0, 77, 3, 88, 0, 66, 49, 33, 3, 114, 63, 26, 0, 35]
my_enc = encry

import random
random.seed(0)

for i in range(len(my_enc)):
a = random.randint(0, len(my_enc))
# print(a, end=' ')
b += [(a ^ my_enc[i])]

# print()
tmp = (''.join(map(chr, b)))
print(base64.b64decode(tmp.encode()))