crewCTF 2025 Reverse-WASM Vault WriteUp

3.7k 词

unlock函数:

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
undefined4 export::unlock(void)

{
int iVar1;
undefined4 uVar2;

iVar1 = import::env::x(0xb4b);
if (iVar1 == 0) {
unnamed_function_15();
}
else {
unnamed_function_1();
}
iVar1 = import::env::x(0xb52);
if (iVar1 == 0) {
unnamed_function_2();
}
else {
unnamed_function_15();
}
iVar1 = import::env::x(0xb59);
if (iVar1 == 0) {
unnamed_function_21();
}
else {
unnamed_function_4();
}
iVar1 = import::env::x(0xb60);
if (iVar1 == 0) {
unnamed_function_4();
}
else {
unnamed_function_15();
}
iVar1 = import::env::x(0xb67);
if (iVar1 == 0) {
unnamed_function_16();
}
else {
unnamed_function_1();
}
iVar1 = import::env::x(0xb6e);
if (iVar1 == 0) {
unnamed_function_17();
}
else {
unnamed_function_4();
}
iVar1 = import::env::x(0xb75);
if (iVar1 == 0) {
uVar2 = 0xf00;
}
else {
uVar2 = 0x121a;
}
iVar1 = import::env::x(0xb7c);
if (iVar1 == 0) {
uVar2 = unnamed_function_9(uVar2);
}
else {
uVar2 = unnamed_function_5(uVar2);
}
iVar1 = import::env::x(0xb83);
if (iVar1 == 0) {
unnamed_function_19(uVar2);
}
else {
unnamed_function_20(uVar2);
}
iVar1 = import::env::x(0xb8a);
if (iVar1 == 0) {
unnamed_function_21();
}
else {
unnamed_function_1();
}
iVar1 = import::env::x(0xb91);
if (iVar1 == 0) {
uVar2 = unnamed_function_22();
}
else {
uVar2 = unlock();
}
return uVar2;
}

查看外层函数x的实现:

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
async function load() {
window.vault = await WebAssembly.instantiateStreaming(fetch("vault.wasm"), {
env: {
x(number) {
let result = 0;
while (number !== 0) {
result ^= number & 1;
number >>>= 1;
}
return result;
}
}
});
}

function unlock() {
const field = document.getElementById("vault");
const flag = field.value;

const flagEncoded = new TextEncoder().encode(flag);

if (flagEncoded.length >= 0x100) {
return false;
}

new Uint8Array(window.vault.instance.exports.memory.buffer).set(flagEncoded);
new Uint8Array(window.vault.instance.exports.memory.buffer).set([0], flagEncoded.length);

const result = window.vault.instance.exports.unlock() != 0;

if (result) {
field.classList.remove("is-danger");
field.classList.add("is-success");
} else {
field.classList.remove("is-success");
field.classList.add("is-danger");
}
}

load();

x是二进制位奇偶校验,为奇数时返回0,偶数时返回1

自动化脚本:

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
93
94
95
96
97
98
99
100
101
import re

def parity_check(number):
result = 0
while number > 0:
result ^= number & 1
number >>= 1
return result

def deobfuscate_wasm_code(code_string):
lines = code_string.strip().split('\n')
output_lines = []
i = 0
while i < len(lines):
line = lines[i]
trimmed_line = line.strip()
match = re.search(r'(.+?) = import::env::x\((0x[0-9a-f]+|[0-9]+)\);', trimmed_line)
if match:
number_str1 = match.group(2)
try:
number1 = int(number_str1, 16) if number_str1.startswith('0x') else int(number_str1, 10)
except ValueError:
output_lines.append(line)
i += 1
continue
if i + 1 < len(lines) and re.search(r'(.+?) = import::env::x\((0x[0-9a-f]+|[0-9]+)\);', lines[i+1].strip()):
if i + 7 < len(lines) and lines[i+2].strip().startswith("if") and lines[i+5].strip().startswith("else"):
match2 = re.search(r'(.+?) = import::env::x\((0x[0-9a-f]+|[0-9]+)\);', lines[i+1].strip())
number_str2 = match2.group(2)
number2 = int(number_str2, 16) if number_str2.startswith('0x') else int(number_str2, 10)
check_result1 = parity_check(number1)
check_result2 = parity_check(number2)
inner_expr_val = 0
is_not_equal_zero = "!= 0" in lines[i+3]
if (is_not_equal_zero and check_result1 != 0) or (not is_not_equal_zero and check_result1 == 0):
inner_expr_val = 1

if check_result2 == 0:
extracted_line = lines[i + 3]
else:
extracted_line = lines[i + 6]
def replace_expr(m):
return str(inner_expr_val)
extracted_line_fixed = re.sub(r'\([a-zA-Z0-9_]*? \?\?.*? (==|!=) 0\)', replace_expr, extracted_line)
extracted_line_fixed = re.sub(r'\([a-zA-Z0-9_]*? (==|!=) 0\)', replace_expr, extracted_line_fixed)
output_lines.append(extracted_line_fixed)
i += 8
continue
if i + 4 < len(lines) and lines[i+1].strip().startswith("if") and lines[i+4].strip().startswith("else"):
check_result = parity_check(number1)
if check_result == 0:
output_lines.append(lines[i + 2])
else:
output_lines.append(lines[i + 5])
i += 7
continue
if i + 2 < len(lines) and not lines[i+1].strip().startswith("if") and lines[i+2].strip().startswith("if"):
check_result = parity_check(number1)
if check_result == 0:
output_lines.append(lines[i + 3])
else:
output_lines.append(lines[i + 1])
i += 5
continue
if i + 3 < len(lines) and lines[i+1].strip().startswith("if"):
check_result = parity_check(number1)
if check_result == 0:
output_lines.append(lines[i + 2])
i += 4
continue
if i + 1 < len(lines) and re.search(r'\(.+? (==|!=) 0\);', lines[i+1].strip()):
check_result = parity_check(number1)
assignment_line = lines[i+1]
assignment_var = re.search(r'(.+?) =', assignment_line).group(1)
is_equal_zero = "==" in assignment_line
if (is_equal_zero and check_result == 0) or (not is_equal_zero and check_result != 0):
output_lines.append(f"{assignment_var.strip()} = 1;")
else:
output_lines.append(f"{assignment_var.strip()} = 0;")
i += 2
continue
output_lines.append(line)
i += 1
else:
output_lines.append(line)
i += 1
final_output = []
last_line_was_empty = False
for line in output_lines:
trimmed_line = line.strip()
if not trimmed_line and last_line_was_empty:
continue
final_output.append(line)
last_line_was_empty = not trimmed_line
return '\n'.join(final_output)

wasm_code = """
"""

deobfuscated_code = deobfuscate_wasm_code(wasm_code)
print(deobfuscated_code)

unlock:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
undefined4 export::unlock(void)
{
int iVar1;
undefined4 uVar2;

unnamed_function_1();
unnamed_function_2();
unnamed_function_4();
unnamed_function_15();
unnamed_function_16();
unnamed_function_17();
uVar2 = 0xf00;
uVar2 = unnamed_function_9(uVar2);
unnamed_function_19(uVar2);
unnamed_function_21();
uVar2 = unnamed_function_22();
return uVar2;
}

按顺序分析并处理所有的类似混淆:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void unnamed_function_1(void)
{
int iVar1;
uint uVar2;
undefined2 *puVar3;
uint uVar4;
uint uVar5;

uVar4 = 0;
do {
uVar2 = 0x200;
uVar5 = 2;
uVar5 = uVar5 * uVar4;
puVar3 = (undefined2 *)(uVar2 + uVar5);
*puVar3 = (short)uVar4;
uVar2 = 1;
uVar4 = uVar4 + uVar2;
uVar2 = 0x100;
} while (uVar4 != uVar2);
return;
}

在0x2000x3FF的位置填充word形式的0x000xFF

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
void unnamed_function_2(void)
{
byte bVar1;
int iVar2;
int iVar3;
uint uVar4;
char *pcVar5;
byte *pbVar6;
undefined4 uVar7;
char cVar8;
uint uVar9;
char *pcVar10;
char cVar11;

for (pbVar6 = (byte *)0x0; *pbVar6 != 0; pbVar6 = pbVar6 + iVar3) {
uVar4 = 0x201;
uVar9 = 2;
bVar1 = *pbVar6;
uVar9 = uVar9 * bVar1;
pcVar5 = (char *)(uVar4 + uVar9);
uVar4 = 0x201;
iVar3 = 2;
bVar1 = *pbVar6;
uVar9 = iVar3 * (uint)bVar1;
pcVar10 = (char *)(uVar4 + uVar9);
cVar8 = *pcVar10;
cVar11 = '\x01';
cVar8 = cVar8 + cVar11;
*pcVar5 = cVar8;
iVar3 = 1;
iVar3 = iVar3;
}
uVar7 = 0x201;
*(bool *)uVar7 = 1;
return;
}

从地址0x0开始遍历直到遇到空字符,统计每个字符的出现次数,在0x200的表中的奇数索引记录

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
void unnamed_function_4(void)
{
int iVar1;
int iVar2;
uint uVar3;
uint uVar4;
uint uVar5;
uint uVar6;
uint uVar7;
uint uVar8;
uint uVar9;

uVar3 = 0x200;
uVar4 = 0x400;
uVar5 = 1;
do {
uVar7 = 0;
do {
uVar6 = uVar3;
uVar8 = 2;
uVar9 = uVar7;
uVar8 = uVar8 * uVar9;
uVar6 = uVar6 + uVar8;
uVar8 = uVar4;
iVar1 = 2;
uVar9 = uVar7;
iVar1 = iVar1 * uVar9;
iVar1 = uVar8 + iVar1;
uVar8 = uVar5;
unnamed_function_3(uVar6,iVar1,uVar8);
uVar7 = uVar7;
iVar1 = 2;
uVar6 = uVar5;
iVar1 = iVar1 * uVar6;
iVar1 = iVar1;
uVar7 = uVar7 + iVar1;
uVar6 = uVar7;
uVar8 = 0x100;
} while (uVar6 != uVar8);
uVar7 = uVar3;
uVar3 = uVar4;
uVar4 = 2;
uVar5 = uVar5;
uVar5 = uVar4 * uVar5;
uVar6 = uVar5;
uVar8 = 0x100;
uVar4 = uVar7;
} while (uVar6 != uVar8);
return;
}

void unnamed_function_3(uint param1,uint param2,uint param3)
{
ushort uVar1;
bool bVar2;
int iVar3;
int iVar4;
int iVar5;
ushort *puVar6;
uint uVar7;
uint uVar8;
uint uVar9;
uint uVar10;
undefined2 *puVar11;
uint uVar12;
undefined2 *puVar13;
uint uVar14;
uint uVar15;
uint uVar16;

uVar12 = 0;
uVar8 = 0;
uVar7 = 0;
do {
uVar9 = uVar12;
uVar10 = param3;
if (uVar9 == uVar10) {
uVar7 = 0;
}
else {
uVar9 = uVar8;
uVar10 = param3;
if (uVar9 == uVar10) {
uVar7 = 1;
}
else {
uVar7 = param1;
iVar3 = 2;
uVar9 = uVar12;
uVar9 = iVar3 * uVar9;
puVar6 = (ushort *)(uVar7 + uVar9);
uVar1 = *puVar6;
uVar7 = param1;
iVar3 = 2;
uVar9 = param3;
uVar10 = uVar8;
iVar4 = uVar9 + uVar10;
iVar3 = iVar3 * iVar4;
iVar3 = iVar3;
if (uVar1 < *(ushort *)(uVar7 + iVar3)) {
uVar7 = 1;
}
else {
uVar7 = 0;
}
}
}
uVar9 = uVar7;
if (uVar9 == 0) {
uVar9 = param2;
iVar3 = 2;
uVar10 = uVar12;
uVar14 = uVar8;
iVar4 = uVar10 + uVar14;
iVar3 = iVar3 * iVar4;
iVar3 = iVar3;
uVar10 = param1;
uVar14 = 2;
uVar15 = param3;
uVar16 = uVar8;
uVar15 = uVar15 + uVar16;
uVar14 = uVar14 * uVar15;
puVar13 = (undefined2 *)(uVar10 + uVar14);
*(undefined2 *)(uVar9 + iVar3) = *puVar13;
uVar9 = uVar8;
tmp = 1;
uVar8 = uVar9 + tmp;
}
else {
uVar9 = param2;
iVar3 = 2;
uVar10 = uVar12;
uVar14 = uVar8;
iVar4 = uVar10 + uVar14;
uVar10 = iVar3 * iVar4;
puVar13 = (undefined2 *)(uVar9 + uVar10);
uVar9 = param1;
iVar3 = 2;
uVar10 = uVar12;
uVar10 = iVar3 * uVar10;
puVar11 = (undefined2 *)(uVar9 + uVar10);
*puVar13 = *puVar11;
uVar9 = uVar12;
uVar12 = 1;
uVar12 = uVar9 + uVar12;
}
uVar9 = param3;
uVar10 = uVar12;
uVar14 = param3;
uVar15 = uVar8;
bVar2 = uVar9 == uVar10 && uVar14 == uVar15;
} while (!bVar2);
return;
}

先把0x2000x3FF的内存块复制到0x4000x5FF,然后进行统计,将0255的出现情况按照出现频次从小到大排列,然后同频次内部再按照ascii排列,在0x2000x3FF执行全部256个ascii的排序,在0x4000x5FF只进行0x000x7F的排序,0x80~0xFF不动

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
void unnamed_function_15(void)
{
byte bVar1;
int iVar2;
int iVar3;
int iVar4;
int iVar5;
int iVar6;
int iVar7;
int iVar8;
int iVar9;
char *pcVar10;
byte *pbVar11;

iVar9 = 0;
iVar8 = 0;
iVar2 = 0xf00;
do {
iVar4 = 0x201;
iVar5 = 2;
iVar7 = iVar8;
iVar5 = iVar5 * iVar7;
pcVar10 = (char *)(iVar4 + iVar5);
tmp = 0;

if (tmp != (bool)*pcVar10) {
iVar3 = 0x1000;
iVar4 = 0x10;
iVar5 = 0x200;
iVar7 = 2;
iVar9 = iVar8;
iVar7 = iVar7 * iVar9;
pbVar11 = (byte *)(iVar5 + iVar7);
bVar1 = *pbVar11;
iVar4 = iVar4 * (uint)bVar1;
iVar4 = -iVar4;
iVar9 = iVar3 + iVar4;
iVar3 = iVar9;
iVar4 = 0x200;
iVar5 = 2;
iVar7 = iVar8;
iVar5 = iVar5 * iVar7;
pbVar11 = (byte *)(iVar4 + iVar5);
bVar1 = *pbVar11;
unnamed_function_6(iVar3,(uint)bVar1);
iVar3 = iVar9;
iVar4 = 0x201;
iVar5 = 2;
iVar7 = iVar8;
iVar5 = iVar5 * iVar7;
pbVar11 = (byte *)(iVar4 + iVar5);
bVar1 = *pbVar11;
unnamed_function_8(iVar3,(uint)bVar1);
iVar3 = iVar2;
iVar4 = iVar9;
unnamed_function_10(iVar3,iVar4);
iVar3 = iVar9;
unnamed_function_12(iVar3,0);
iVar2 = iVar9;
unnamed_function_14(iVar2,0);
iVar2 = iVar9;
}
iVar3 = iVar8;
iVar8 = iVar3 + 1;
iVar3 = iVar8;
iVar4 = 0x100;
} while (iVar3 != iVar4);
iVar8 = iVar2;
tmp = 0;
unnamed_function_10(iVar8,tmp);
return;
}

void unnamed_function_6(uint param1,uint param2)
{
int iVar1;
int iVar2;
uint uVar3;
undefined *puVar4;
undefined uVar5;

uVar3 = param1;
puVar4 = (undefined *)(uVar3 + 0);
uVar5 = (undefined)param2;
*puVar4 = uVar5;
return;
}

void unnamed_function_8(int param1,int param2)
{
int iVar1;
int iVar2;
int iVar3;
undefined2 *puVar4;
undefined2 uVar5;

iVar3 = param1;
iVar1 = 1;
puVar4 = (undefined2 *)(iVar3 + iVar1);
uVar5 = (undefined2)param2;
*puVar4 = uVar5;
return;
}

void unnamed_function_10(int param1,int param2)
{
int iVar1;
int iVar2;
int iVar3;
int *piVar4;

iVar3 = param1;
iVar1 = 4;
piVar4 = (int *)(iVar3 + iVar1);
*piVar4 = param2;
return;
}

void unnamed_function_12(int param1,int param2)
{
int iVar1;
int iVar2;
int iVar3;
int *piVar4;

iVar3 = param1;
iVar1 = 8;
piVar4 = (int *)(iVar3 + iVar1);
*piVar4 = param2;
return;
}

void unnamed_function_14(int param1,int param2)
{
int iVar1;
int iVar2;
int iVar3;

iVar3 = param1;
iVar1 = 0xc;
*(int *)(iVar3 + iVar1) = param2;
return;
}

6、8、10、12、14是内存写入函数,整体逻辑是从内存位置 0x200 + iVar8 * 2 读取当前字符的 ASCII 值,并计算目标地址0x1000 + 0x10 * bVar1,生成16字节结构体:

1
2
3
4
5
6
7
struct {
byte field_0; // 字符 i
byte field_1; // 字符 i 的出现次数
int field_4; // 一个地址指针,指向按出现频次排序的下个字符的结构体地址
int field_8; // 0
int field_c; // 0
} __attribute__((packed));

func16:

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
void unnamed_function_16(void)
{
int iVar1;
int iVar2;
int iVar3;
int iVar4;
undefined4 param2;
int iVar5;
int iVar6;
int iVar7;
uint uVar8;
uint uVar9;
undefined4 uVar10;

iVar7 = 0;
iVar5 = 0;
do {
uVar10 = 0xf00;
iVar1 = unnamed_function_9(uVar10);
iVar3 = iVar1;
iVar3 = unnamed_function_9(iVar3);
iVar2 = iVar3;
if (iVar2 == 0) {
return;
}
uVar10 = 0xf00;
iVar5 = iVar3;
param2 = unnamed_function_9(iVar5);
unnamed_function_10(uVar10,param2);
iVar5 = 0x2000;
iVar2 = 0x10;
iVar4 = iVar7;
iVar2 = iVar2 * iVar4;
iVar5 = iVar5 + iVar2;
iVar2 = iVar7;
iVar7 = iVar2 + (uint)1;
iVar2 = iVar5;
iVar4 = iVar1;
uVar8 = unnamed_function_7(iVar4);
iVar4 = iVar3;
uVar9 = unnamed_function_7(iVar4);
uVar8 = uVar8 + uVar9;
unnamed_function_8(iVar2,uVar8);
iVar2 = iVar5;
iVar4 = iVar1;
unnamed_function_12(iVar2,iVar4);
iVar2 = iVar5;
iVar4 = iVar3;
unnamed_function_14(iVar2,iVar4);
iVar2 = iVar1;
iVar1 = iVar5;
unnamed_function_10(iVar2,iVar1);
iVar1 = iVar3;
iVar2 = iVar5;
unnamed_function_10(iVar1,iVar2);
iVar1 = 0xf00;
while( true ) {
iVar2 = iVar1;
iVar3 = iVar2;
iVar3 = unnamed_function_9(iVar3);
iVar1 = iVar3;
if (iVar1 == 0) break;
iVar1 = iVar5;
uVar8 = unnamed_function_7(iVar1);
iVar1 = iVar3;
uVar9 = unnamed_function_7(iVar1);
if (uVar8 < uVar9) break;
iVar1 = iVar3;
}
iVar1 = iVar2;
iVar4 = iVar5;
unnamed_function_10(iVar1,iVar4);
iVar1 = iVar5;
unnamed_function_10(iVar1,iVar3);
} while( true );
}

uint unnamed_function_7(int param1)
{
int iVar1;
int iVar2;
ushort *puVar3;

iVar1 = 1;
puVar3 = (ushort *)(param1 + iVar1);
return (uint)*puVar3;
}

void unnamed_function_8(int param1,int param2)
{
int iVar1;
int iVar2;
int iVar3;
undefined2 *puVar4;
undefined2 uVar5;

iVar3 = param1;
iVar1 = 1;
puVar4 = (undefined2 *)(iVar3 + iVar1);
uVar5 = (undefined2)param2;
*puVar4 = uVar5;
return;
}

undefined4 unnamed_function_9(uint param1)
{
int iVar1;
undefined4 *puVar2;
uint uVar3;

uVar3 = 4;
puVar2 = (undefined4 *)(param1 + uVar3);
return *puVar2;
}

void unnamed_function_10(int param1,int param2)
{
int iVar1;
int iVar2;
int iVar3;
int *piVar4;

iVar3 = param1;
iVar1 = 4;
piVar4 = (int *)(iVar3 + iVar1);
*piVar4 = param2;
return;
}

void unnamed_function_12(int param1,int param2)
{
int iVar1;
int iVar2;
int iVar3;
int *piVar4;

iVar3 = param1;
iVar1 = 8;
piVar4 = (int *)(iVar3 + iVar1);
*piVar4 = param2;
return;
}

void unnamed_function_14(int param1,int param2)
{
int iVar1;
int iVar2;
int iVar3;

iVar3 = param1;
iVar1 = 0xc;
*(int *)(iVar3 + iVar1) = param2;
return;
}

是一个哈夫曼树构造过程,生成的结构体形如:

1
2
3
4
5
6
0x200 0x2020 0x1000 0x1410 
0x300 0x2040 0x1610 0x1420
0x400 0x2060 0x1620 0x2000
0x600 0x2080 0x1390 0x1430
0x600 0x2090 0x1630 0x2010
...

四个小端序dword构成一组(共计16字节),分别记录了:权重和、父节点、左节点、右节点

func17+unlock中的部分逻辑:

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
...
unnamed_function_17();
uVar2 = 0xf00;
uVar2 = unnamed_function_9(uVar2);
...

void unnamed_function_17(void)
{
int iVar1;
uint *puVar2;

puVar2 = (uint *)0x600;
*puVar2 = 0;
return;
}

undefined4 unnamed_function_9(uint param1)
{
int iVar1;
undefined4 *puVar2;
uint uVar3;

uVar3 = 4;
puVar2 = (undefined4 *)(param1 + uVar3);
return *puVar2;
}

在0x600处初始化并设置了一个计数器,然后开始处理哈夫曼树的指针:

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
void unnamed_function_19(uint param1)
{
bool bVar1;
int iVar2;
int iVar3;
int iVar4;
uint uVar5;
undefined4 uVar6;
uint uVar7;
uint uVar8;

uVar5 = 0;
iVar2 = unnamed_function_11(uVar5);
uVar5 = param1;
iVar3 = unnamed_function_13(uVar5);
bVar1 = iVar2 == 0 && iVar3 == 0;
if (bVar1) {
uVar5 = 0;
uVar5 = unnamed_function_5(uVar5);
unnamed_function_18((uint)0);
uVar7 = uVar5;
uVar7 = uVar7 >> (uint)0;
uVar8 = 1;
uVar7 = uVar7 & uVar8;
unnamed_function_18(uVar7);
uVar7 = uVar5;
iVar2 = 1;
uVar7 = uVar7 >> iVar2;
uVar8 = 1;
uVar7 = uVar7 & uVar8;
unnamed_function_18(uVar7);
uVar7 = uVar5;
iVar2 = 2;
uVar7 = uVar7 >> iVar2;
uVar7 = uVar7 & iVar2 == 0;
unnamed_function_18(uVar7);
uVar7 = uVar5;
iVar2 = 3;
uVar7 = uVar7 >> iVar2;
uVar8 = 1;
uVar7 = uVar7 & uVar8;
unnamed_function_18(uVar7);
uVar7 = uVar5;
iVar2 = 4;
uVar7 = uVar7 >> iVar2;
uVar8 = 1;
uVar7 = uVar7 & uVar8;
unnamed_function_18(uVar7);
uVar7 = uVar5;
iVar2 = 5;
uVar7 = uVar7 >> iVar2;
uVar7 = uVar7 & iVar2 != 0;
unnamed_function_18(uVar7);
uVar7 = uVar5;
iVar2 = 6;
uVar7 = uVar7 >> iVar2;
uVar7 = uVar7 & iVar2 != 0;
unnamed_function_18(uVar7);
param1 = uVar5;
iVar2 = 7;
uVar5 = param1 >> iVar2;
uVar7 = 1;
uVar5 = uVar5 & uVar7;
unnamed_function_18(uVar5);
}
else {
unnamed_function_18((uint)1);
uVar5 = param1;
uVar6 = unnamed_function_11(uVar5);
unnamed_function_19(uVar6);
uVar6 = unnamed_function_13(param1);
unnamed_function_19(uVar6);
}
return;
}

uint unnamed_function_5(uint param1)
{
int iVar1;
int iVar2;
byte *pbVar3;

pbVar3 = (byte *)(param1 + 0);
return (uint)*pbVar3;
}

undefined4 unnamed_function_11(int param1)
{
int iVar1;
int iVar2;
undefined4 *puVar3;

iVar1 = 8;
puVar3 = (undefined4 *)(param1 + iVar1);
return *puVar3;
}

undefined4 unnamed_function_13(int param1)
{
int iVar1;
int iVar2;

iVar1 = 0xc;
return *(undefined4 *)(param1 + iVar1);
}

void unnamed_function_18(uint param1)
{
int iVar1;
int iVar2;
uint *puVar3;
uint uVar4;
byte *pbVar5;
undefined *puVar6;
int *piVar7;
undefined uVar8;
uint uVar9;
uint uVar10;
uint uVar11;
uint uVar12;
uint uVar13;
uint uVar14;
uint uVar15;

puVar3 = (uint *)0x600;
uVar4 = *puVar3;
iVar1 = 0x604;
uVar9 = uVar4;
uVar11 = 8;
uVar9 = uVar9 / uVar11;
pbVar5 = (byte *)(iVar1 + uVar9);
uVar11 = (uint)*pbVar5;
uVar9 = param1;
uVar10 = 0xfe;
uVar12 = uVar11;
uVar13 = 7;
uVar14 = uVar4;
uVar15 = 7;
uVar14 = uVar14 & uVar15;
uVar13 = uVar13 - uVar14;
uVar12 = uVar12 >> (uVar13 & 0x1f);
uVar10 = uVar10 & uVar12;
uVar9 = uVar9 | uVar10;
iVar1 = 7;
uVar10 = uVar4;
uVar11 = 7;
uVar10 = uVar10 & uVar11;
uVar10 = iVar1 - uVar10;
uVar8 = (undefined)(uVar9 << (uVar10 & 0x1f));
uVar9 = 0x604;
uVar11 = uVar4;
uVar10 = 8;
uVar11 = uVar11 / uVar10;
puVar6 = (undefined *)(uVar9 + uVar11);
uVar8 = (undefined)uVar4;
*puVar6 = uVar8;
piVar7 = (int *)0x600;
param1 = uVar4;
iVar1 = 1;
iVar1 = param1 + iVar1;
*piVar7 = iVar1;
return;
}

实现了哈夫曼树的序列化,在0x600记录长度并在0x604开始的位置记录了序列化的字节,之后进入func21:

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
void unnamed_function_21(void)
{
byte bVar1;
int iVar2;
int iVar3;
byte *pbVar4;
undefined4 param1;
uint uVar5;

pbVar4 = (byte *)0x0;
while (*pbVar4 != 0) {
iVar3 = 0x1000;
uVar5 = 0x10;
bVar1 = *pbVar4;
uVar5 = uVar5 * bVar1;
uVar5 = -uVar5;
unnamed_function_20(iVar3 + uVar5);
iVar3 = 1;
pbVar4 = pbVar4 + iVar3;
}
param1 = 0x1000;
unnamed_function_20(param1);
return;
}

void unnamed_function_20(int param1)
{
int iVar1;
int iVar2;
int iVar3;
int iVar4;
undefined4 param1_00;

iVar2 = 0;
iVar2 = unnamed_function_9(iVar2);
iVar1 = iVar2;
if (iVar1 != 0) {
iVar1 = param1;
iVar3 = iVar2;
iVar3 = unnamed_function_11(iVar3);
if (iVar1 == iVar3) {
unnamed_function_20(iVar2);
unnamed_function_18((uint)0);
}
else {
param1 = iVar2;
unnamed_function_20(param1);
param1_00 = 1;
unnamed_function_18(param1_00);
}
return;
}
return;
}

undefined4 unnamed_function_9(uint param1)
{
int iVar1;
undefined4 *puVar2;
uint uVar3;

uVar3 = 4;
puVar2 = (undefined4 *)(param1 + uVar3);
return *puVar2;
}

undefined4 unnamed_function_11(int param1)
{
int iVar1;
int iVar2;
undefined4 *puVar3;

iVar1 = 8;
puVar3 = (undefined4 *)(param1 + iVar1);
return *puVar3;
}

是在刚才的bit基础上继续添加输入值被编码的bit,生成一个总的密文,func22自然就是比较:

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
undefined unnamed_function_22(void)
{
char cVar1;
int iVar2;
int iVar3;
undefined uVar4;
int *piVar5;
int iVar6;
char *pcVar7;
uint uVar8;
uint uVar9;
uint uVar10;
uint uVar11;

uVar8 = 0;
piVar5 = (int *)0x600;
iVar6 = *piVar5;
piVar5 = (int *)0xe00;
if (iVar6 == *piVar5) {
piVar5 = (int *)0x600;
iVar6 = *piVar5;
iVar2 = 7;
iVar2 = iVar2;
uVar10 = 8;
uVar10 = (uint)(iVar6 + iVar2) / uVar10;
while( true ) {
iVar2 = 0x604;
uVar9 = uVar8;
pcVar7 = (char *)(iVar2 + uVar9);
cVar1 = *pcVar7;
iVar2 = 0xe04;
uVar9 = uVar8;
uVar9 = -uVar9;
if (cVar1 != *(char *)(iVar2 + uVar9)) break;
uVar8 = uVar8;
iVar2 = 1;
iVar2 = -iVar2;
uVar8 = uVar8 + iVar2;
uVar9 = uVar8;
uVar11 = uVar10;
if (uVar9 == uVar11) {
uVar4 = 1;
return uVar4;
}
}
return 0;
}
return 0;
}

同构:

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
def b2nle(b, n):
return [int.from_bytes(b[i:i+n], byteorder='little', signed=False) for i in range(0, len(b), n)]

def ru16(data, offset):
return data[offset] | (data[offset + 1] << 8)

def ru32(data, offset):
return data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24)

def wu16(data, offset, value):
data[offset] = value & 0xFF
data[offset + 1] = (value >> 8) & 0xFF

def wu32(data, offset, value):
data[offset] = value & 0xFF
data[offset + 1] = (value >> 8) & 0xFF
data[offset + 2] = (value >> 16) & 0xFF
data[offset + 3] = (value >> 24) & 0xFF

def sort_table(data, start, length):
num_entries = length // 2
entries = []
for i in range(num_entries):
offset = start + i * 2
even_index = data[offset]
odd_index = data[offset + 1]
entries.append((even_index, odd_index))
entries.sort(key=lambda x: (x[1], x[0]))
for i, (even, odd) in enumerate(entries):
offset = start + i * 2
data[offset] = even
data[offset + 1] = odd

mem = bytearray(0x3000)
inp = bytearray(b"aABBbbCCC999cccdddd5555DDDD00000EEEEEeeeee")
for i in range(len(inp)): mem[i] = inp[i]
tgt = bytearray.fromhex("B5030000E8C6660CD7C1C7649D111CBE127558CA6E004E4C452DA446898CD56535BB9BC2CBEB3630B5902AAA3544D1DCBAB805615AFDF96B6FCB5B7E5ADABEF4B60FEB170545B047F34A17F37111DA5A2B86EA79EB1AA2EC17A10B83796DD4F3DF965B57417F4EE768E98F4841770E1B9F1AAD3EF8A489D3635240B8AEC6")
for i in range(len(tgt)): mem[i+0xE00] = tgt[i]

# --- func1 ---
for i in range(0x200, 0x400, 2): mem[i] = (i - 0x200) // 2

# --- func2 ---
for i in range(len(inp)): mem[0x201+2 * inp[i]] += 1
mem[0x201] = 1

# --- func4 & func3 ---
mem[0x400:0x600] = mem[0x200:0x400]
sort_table(mem, 0x200, 0x200)
sort_table(mem, 0x400, 0x100)

# --- func15 & func16 ---
active_nodes = []
base_addr = 0x1000
struct_size = 0x10
for i in range(0, 0x200, 2):
chrord = mem[0x200 + i]
freq = mem[0x201 + i]
if freq > 0:
addr = base_addr + struct_size * chrord
active_nodes.append((freq, addr))
tree_ptr = 0x2000
while len(active_nodes) > 1:
active_nodes.sort(key=lambda x: (x[0], x[1]))
node1_freq, node1_addr = active_nodes.pop(0)
node2_freq, node2_addr = active_nodes.pop(0)
parent_freq = node1_freq + node2_freq
parent_addr = tree_ptr
mem[parent_addr + 0] = 0
wu16(mem, parent_addr + 1, parent_freq)
wu32(mem, parent_addr + 8, node1_addr)
wu32(mem, parent_addr + 12, node2_addr)
wu32(mem, node1_addr + 4, parent_addr)
wu32(mem, node2_addr + 4, parent_addr)
active_nodes.append((parent_freq, parent_addr))
tree_ptr += struct_size
if active_nodes:
root_address = active_nodes[0][1]
wu32(mem, 0xf00, root_address)
# print(hex(b2nle(mem[0xF00:0xF04], 4)[0]))
# l = b2nle(mem[0x2000:0x2100], 4)
# for i in range(len(l)):
# print(hex(l[i]),end=" ")
# print()

# --- func19 ---
def write_bit(bit):
offset = ru32(mem, 0x600)
byte_idx = offset // 8
bit_idx = 7 - (offset % 8)
addr = 0x604 + byte_idx
if addr >= len(mem): mem.extend([0] * 0x100)
if bit: mem[addr] |= (1 << bit_idx)
else: mem[addr] &= ~(1 << bit_idx)
wu32(mem, 0x600, offset + 1)

def func19(node_addr):
left = ru32(mem, node_addr + 8)
right = ru32(mem, node_addr + 12)
if node_addr < 0x2000:
write_bit(0)
ch = (node_addr - 0x1000) >> 4
for i in range(8):
bit = (ch >> i) & 1
write_bit(bit)
else:
write_bit(1)
func19(left)
func19(right)
wu32(mem, 0x600, 0)
for i in range(0x200):
if 0x604 + i < len(mem):
mem[0x604 + i] = 0
root_addr = ru32(mem, 0xF00)
# print(f"Root address: {hex(root_addr)}")
func19(root_addr)

# --- func20 & func21 ---
def func20(leaf_addr):
parent = ru32(mem, leaf_addr + 4)
if parent == 0: return
left_child = ru32(mem, parent + 8)
func20(parent)
if leaf_addr == left_child: write_bit(0)
else: write_bit(1)

def func21():
addr = 0
while mem[addr] != 0:
ch = mem[addr]
leaf_addr = 0x1000 + (ch << 4)
func20(leaf_addr)
addr += 1
func20(0x1000)
func21()

final_bit_count = ru32(mem, 0x600)
final_byte_count = (final_bit_count + 7) // 8
# print(f"Total compressed: {final_bit_count} bits ({final_byte_count} bytes)")
print(f"Output (hex): {mem[0x600:0x604 + final_byte_count].hex()}")
print(f"Target (hex): {tgt.hex()}")

解密:

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
93
94
def ru32(data, offset):
return data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24)

def wu32(data, offset, value):
data[offset] = value & 0xFF
data[offset + 1] = (value >> 8) & 0xFF
data[offset + 2] = (value >> 16) & 0xFF
data[offset + 3] = (value >> 24) & 0xFF

mem = bytearray(0x3000)
tgt = bytearray.fromhex("B5030000E8C6660CD7C1C7649D111CBE127558CA6E004E4C452DA446898CD56535BB9BC2CBEB3630B5902AAA3544D1DCBAB805615AFDF96B6FCB5B7E5ADABEF4B60FEB170545B047F34A17F37111DA5A2B86EA79EB1AA2EC17A10B83796DD4F3DF965B57417F4EE768E98F4841770E1B9F1AAD3EF8A489D3635240B8AEC6")
for i in range(len(tgt)): mem[i+0xE00] = tgt[i]

def write_bit(bit):
offset = ru32(mem, 0x600)
byte_idx = offset // 8
bit_idx = 7 - (offset % 8)
addr = 0x604 + byte_idx
if addr >= len(mem): mem.extend([0] * 0x100)
if bit: mem[addr] |= (1 << bit_idx)
else: mem[addr] &= ~(1 << bit_idx)
wu32(mem, 0x600, offset + 1)

def func19(node_addr):
left = ru32(mem, node_addr + 8)
right = ru32(mem, node_addr + 12)
if node_addr < 0x2000:
write_bit(0)
ch = (node_addr - 0x1000) >> 4
for i in range(8):
bit = (ch >> i) & 1
write_bit(bit)
else:
write_bit(1)
func19(left)
func19(right)

def rebuild_tree_from_bits(bit_stream_data, bit_offset_ref):
byte_idx = bit_offset_ref[0] // 8
bit_in_byte_idx = 7 - (bit_offset_ref[0] % 8)
if byte_idx >= len(bit_stream_data):
return 0
bit = (bit_stream_data[byte_idx] >> bit_in_byte_idx) & 1
bit_offset_ref[0] += 1
if bit == 0:
char_code = 0
for i in range(8):
byte_idx = bit_offset_ref[0] // 8
bit_in_byte_idx = 7 - (bit_offset_ref[0] % 8)
bit = (bit_stream_data[byte_idx] >> bit_in_byte_idx) & 1
bit_offset_ref[0] += 1
char_code |= (bit << i)
return 0x1000 + (char_code << 4)
else:
parent_addr = tree_rebuild_ptr[0]
tree_rebuild_ptr[0] += 0x10
left_child_addr = rebuild_tree_from_bits(bit_stream_data, bit_offset_ref)
right_child_addr = rebuild_tree_from_bits(bit_stream_data, bit_offset_ref)
wu32(mem, parent_addr + 8, left_child_addr)
wu32(mem, parent_addr + 12, right_child_addr)
if left_child_addr != 0: wu32(mem, left_child_addr + 4, parent_addr)
if right_child_addr != 0: wu32(mem, right_child_addr + 4, parent_addr)
return parent_addr

def decode_data_from_bits(root_addr, bit_stream_data, bit_offset_ref, total_bits):
decoded_bytes = bytearray()
while bit_offset_ref[0] < total_bits:
current_node_addr = root_addr
while True:
byte_idx = bit_offset_ref[0] // 8
bit_in_byte_idx = 7 - (bit_offset_ref[0] % 8)
if byte_idx >= len(bit_stream_data): break
bit = (bit_stream_data[byte_idx] >> bit_in_byte_idx) & 1
bit_offset_ref[0] += 1
if bit == 0: current_node_addr = ru32(mem, current_node_addr + 8)
else: current_node_addr = ru32(mem, current_node_addr + 12)
if current_node_addr < 0x2000:
char_code = (current_node_addr - 0x1000) >> 4
decoded_bytes.append(char_code)
break
return decoded_bytes

total_bits = ru32(tgt, 0)
compressed_data = tgt[4:]
bit_offset_rebuild = [0]
tree_rebuild_ptr = [0x2000]
wu32(mem, 0x600, 0)
root_addr_for_func19 = ru32(mem, 0xF00)
func19(root_addr_for_func19)
tree_encoded_len = ru32(mem, 0x600)
tree_encoded_bytes = (tree_encoded_len + 7) // 8
rebuilt_root_addr = rebuild_tree_from_bits(compressed_data, bit_offset_rebuild)
decoded_output = decode_data_from_bits(rebuilt_root_addr, compressed_data, bit_offset_rebuild, total_bits)
print(f"{decoded_output.decode('ascii')}")

crew{7H15_15_4_V3rY_V3rY_V3rY_5UP3r_1NCr3D181Y_10N6_F146_600D_J08_1_C0MPr3553D_17_W17H_MY_C0MPU73r_5C13NC3_6C53_KN0W13D63_1_6U355_f4c91dbe}

(怎么这么长)