0%

LACTF2024逆向方向部分wp

参赛ID:1K0CT

shattered-memories

签到题 DIE查壳 无壳64x IDA64打开直接拼flag:

image-20240223202112685

aplet321 | 接轨pwn

DIE查壳 无壳64x IDA64打开:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v3; // rbx
size_t v4; // rax
int v5; // ebp
int v6; // r12d
char *v7; // r15
char s[568]; // [rsp+10h] [rbp-238h] BYREF

v3 = s;
setbuf(stdout, 0LL);
puts("hi, i'm aplet321. how can i help?");
fgets(s, 512, stdin);
v4 = strlen(s);
if ( v4 <= 5 )
goto LABEL_10;
v5 = 0;
v6 = 0;
v7 = &s[(v4 - 6) + 1];
do
{
v6 += strncmp(v3, "pretty", 6uLL) == 0; // v6 = 15
// v5 = 39
v5 += strncmp(v3++, "please", 6uLL) == 0;
}
while ( v3 != v7 );
if ( v5 )
{
if ( strstr(s, "flag") )
{
if ( v6 + v5 == 54 && v6 - v5 == -24 )
{
puts("ok here's your flag");
system("cat flag.txt");
}
else
{
puts("sorry, i'm not allowed to do that");
}
}
else
{
puts("sorry, i didn't understand what you mean");
}
}
else
{
LABEL_10:
puts("so rude");
}
return 0;
}

逻辑是检测接收到的数据中包含连续的prettyplease的个数并要求含有flag 简单解解方程算出分别需要出现多少次 然后写出exp发送数据:

1
2
3
4
5
6
7
8
9
10
11
from pwn import *

v6 = b"pretty" * 15
v5 = b"please" * 39
fin = b"flag"

r = remote("chall.lac.tf", 31321)

r.sendline(v6 + v5 + fin)
r.interactive()
# lactf{next_year_i'll_make_aplet456_hqp3c1a7bip5bmnc}

the-secret-of-java-island | java逆向 | 爆破

一个按钮点击小游戏 目标是进入以下状态:

image-20240223204927460

但是直接nc到该端口无法获得flag 还是要玩游戏 核心逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if (paramInt == 0) {
exploit += "d";
story.setText("You clobbered the DOM. That was exploit #" + exploit.length() + ".");
} else {
exploit += "p";
story.setText("You polluted the prototype. That was exploit #" + exploit.length() + ".");
}
if (exploit.length() == 8)
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
if (!Arrays.equals(messageDigest.digest(exploit.getBytes("UTF-8")), new byte[] {
69, 70, -81, -117, -10, 109, 15, 29, 19, 113,
61, -123, -39, 82, -11, -34, 104, -98, -111, 9,
43, 35, -19, 22, 52, -55, -124, -45, -72, -23,
96, -77 })) {
state = 7;
} else {
state = 6;
}
updateGame();

根据按钮点击情况向校验字符串添加d或者p然后求sha256哈希值进行校验 由于长度只有8可能性很少所以直接爆破:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import hashlib
enc = "4546af8bf66d0f1d13713d85d952f5de689e91092b23ed1634c984d3b8e960b3"
def sha256_encrypt(plaintext):
plaintext_bytes = plaintext.encode('utf-8')
sha256_hash = hashlib.sha256(plaintext_bytes)
return sha256_hash.hexdigest().lower()
if __name__ == "__main__":
for i in range(0x100):
plaintext = "{:08b}".format(i).replace("0", "p").replace("1", "d")
encrypted_hash = sha256_encrypt(plaintext)
if encrypted_hash == enc:
print("find:", plaintext)
break
# find: dpddpdpp

然后在游戏中进行对应操作获得flag

image-20240223205333972

flag-finder | GM逆向

文件包含data.win 说明是使用GameMaker编写的游戏 二进制文件只包含窗口消息的处理等最底层的逻辑 所以没必要太仔细分析 用目前最好的GM解包应用UndertaleModTool解包data.win

image-20240223205719675

游戏的目标是找到key 但是地图上找不到 根据提示key一定在这个room

在游戏Rooms中找到这样一个实例:

image-20240223210214778

对应生成位置:image-20240223210252760

超出了地图空气墙范围 查看flag所在位置并将key修改到相近位置 UndertaleModTool支持直接运行修改后的data.win 完成任务得到flag:

image-20240223210545560

glottem | algre

明文 直接notepad打开:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/sh
1<<4201337
1//1,"""
exit=process.exit;argv=process.argv.slice(1)/*
4201337
read -p "flag? " flag
node $0 "$flag" && python3 $0 "$flag" && echo correct || echo incorrect
1<<4201337
*///""";from sys import argv
e = [[[...], ...], ...]
alpha="abcdefghijklmnopqrstuvwxyz_"
d=0;s=argv[1];1//1;"""
/*"""
#*/for (let i = 0; i < s.length; i ++) {/*
for i in range(6,len(s)-2):
#*/d=(d*31+s.charCodeAt(i))%93097/*
d+=e[i-6][alpha.index(s[i])][alpha.index(s[i+1])]#*/}
exit(+(d!=260,[d!=61343])[0])
4201337

数据e过长不展示 这个文件想表达的意思应该是输入的flag要分别满足python展示的代码和注释起来的java代码 java的逻辑很好理解 python代码的意思是保证flag内容中当前字符的位置作为1级索引来确定”层” 当前字符在表中的位置作为2级索引确定在层中的”段” 当前字符的后一个字符在表中的位置为3级索引从”段”中选取加数 题目提示flag长度34 去掉lactf{}后为26 而最后d就是要与260校验 python简单写个程序发现e中最小的数就是10 所以目标就是找到0~25层之间所有10的通路 结合java代码的逻辑最后写出脚本:

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
e = [[[...], ...], ...]
# ind = []
# for _ in range(26):
# for __ in range(len(e[_])):
# for ___ in range(len(e[_][__])):
# if e[_][__][___] == 10:
# ind.append([_,__,___])
# print(ind)

alpha="abcdefghijklmnopqrstuvwxyz_"

def find_paths(node_list):
paths = []
stack = []
visited = set()

for node in node_list:
if node[0] == 0:
stack.append([node])

while stack:
path = stack.pop()
current_node = path[-1]

if current_node[0] == 25:
paths.append(path)
visited.clear()

for node in node_list:
if node[0] == current_node[0] + 1 and node[1] == current_node[2]:
if node not in path:
new_path = list(path)
new_path.append(node)
stack.append(new_path)
visited.add(node)

return paths

node_list = [(...), ...]

paths = find_paths(node_list)
flags = []
for path in paths:
flags.append("lactf{" + "".join([alpha[node[1]] for node in path]) + alpha[path[-1][2]] + "}")
for s in flags:
d = 0
for each in s:
d = (31 * d + ord(each)) % 93097
if d == 61343:
print(s)
break
# lactf{solve_one_get_two_free_deal}

meow meow | 爆破

附件给了7个很大的data文件 先看二进制文件 DIE查壳 无壳64x IDA64打开:

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
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char v4; // al
size_t v5; // rbx
size_t v6; // rbx
char name[6]; // [rsp+Ah] [rbp-1A6h] BYREF
__int64 *proc_1[12]; // [rsp+10h] [rbp-1A0h] BYREF
int v9; // [rsp+70h] [rbp-140h]
char s[8]; // [rsp+80h] [rbp-130h] BYREF
__int64 v11; // [rsp+88h] [rbp-128h]
__int64 v12; // [rsp+90h] [rbp-120h]
__int64 v13; // [rsp+98h] [rbp-118h]
__int64 v14; // [rsp+A0h] [rbp-110h]
__int64 v15; // [rsp+A8h] [rbp-108h]
__int64 v16; // [rsp+B0h] [rbp-100h]
__int64 v17; // [rsp+B8h] [rbp-F8h]
__int64 v18; // [rsp+C0h] [rbp-F0h]
__int64 v19; // [rsp+C8h] [rbp-E8h]
__int64 v20; // [rsp+D0h] [rbp-E0h]
__int64 v21; // [rsp+D8h] [rbp-D8h]
int v22; // [rsp+E0h] [rbp-D0h]
__int64 input_proc[2]; // [rsp+F0h] [rbp-C0h] BYREF
int v24; // [rsp+100h] [rbp-B0h]
int v25; // [rsp+190h] [rbp-20h]
int k; // [rsp+194h] [rbp-1Ch]
int j; // [rsp+198h] [rbp-18h]
int i; // [rsp+19Ch] [rbp-14h]

*s = 0LL;
v11 = 0LL;
v12 = 0LL;
v13 = 0LL;
v14 = 0LL;
v15 = 0LL;
v16 = 0LL;
v17 = 0LL;
v18 = 0LL;
v19 = 0LL;
v20 = 0LL;
v21 = 0LL;
v22 = 0;
memset(proc_1, 0, sizeof(proc_1));
v9 = 0;
printf("Meow Meow? ");
fgets(s, 95, stdin);
s[strcspn(s, "\n")] = 0;
if ( strlen(s) % 5 )
{
puts("WOOOOOOOF BARK BARK BARK");
return 1LL;
}
else
{
for ( i = 0; ; ++i )
{
v5 = i;
if ( v5 >= strlen(s) )
break;
v4 = trans(s[i]);
*(proc_1 + i) = v4; // lowercase->'a' offset
// _ ->26
// { ->27
// } ->28
if ( *(proc_1 + i) == 0xFF )
{
puts("WOOOOOOOF BARK BARK BARK");
return 2LL;
}
}
strcpy(name, "data0");
for ( j = 0; ; ++j )
{
v6 = j;
if ( v6 >= strlen(s) / 5 )
{
++name[4];
if ( access(name, 0) )
{
v25 = 0;
v24 = 0;
input_proc[0] = s;
input_proc[1] = proc_1;
GOMP_parallel(func, input_proc, 0LL, 0LL);
v25 = v24;
if ( v24 == 7 )
{
printf("MEOW");
for ( k = 0; k <= 999; ++k )
{
putchar(33);
fflush(stdout);
usleep(0x3E8u);
}
putchar(10);
return 0LL;
}
else
{
puts("Woof.....");
return 0xFFFFFFFFLL;
}
}
else
{
puts("WOOOOOOOF BARK BARK BARK");
return 4LL;
}
}
++name[4];
if ( access(name, 0) )
break;
}
if ( j )
{
puts("WOOOOOOOF BARK BARK BARK");
return 3LL;
}
else
{
puts("Error: make sure you have downloaded and extracted the data.zip files into the same folder as the executable.");
return 1LL;
}
}
}

输入的长度为5n 使用GOMP_parallel创建了func的线程 func:

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
__int64 __fastcall sub_55811199B67A(__int64 input_proc)
{
int v1; // r12d
int num_threads; // ebx
int thread_num; // esi
int v4; // ecx
int v5; // eax
int v6; // eax
int v7; // ebx
__int64 result; // rax
int buf[29]; // [rsp+10h] [rbp-A0h] BYREF
char file[4]; // [rsp+85h] [rbp-2Bh] BYREF
__int16 v11; // [rsp+89h] [rbp-27h]
char v12; // [rsp+8Bh] [rbp-25h]
int fd; // [rsp+8Ch] [rbp-24h]
int i; // [rsp+90h] [rbp-20h]
int key; // [rsp+94h] [rbp-1Ch]
int v16; // [rsp+98h] [rbp-18h]
unsigned int v17; // [rsp+9Ch] [rbp-14h]

v17 = 0;
v1 = strlen(*input_proc) / 5;
num_threads = omp_get_num_threads();
thread_num = omp_get_thread_num();
v4 = v1 / num_threads;
v5 = v1 % num_threads;
if ( thread_num < v1 % num_threads )
{
v5 = 0;
++v4;
}
v6 = v4 * thread_num + v5;
v7 = v6 + v4;
if ( v6 < v6 + v4 )
{
v16 = v6;
do
{
*file = 0x61746164;
v11 = (v16 + 49);
fd = open(file, 0);
key = 0;
for ( i = 0; i <= 4; ++i )
{
lseek(fd, (0x74 * key), 0);
v12 = *(*(input_proc + 8) + 5 * v16 + i);// key = 0
// loop(0, 5):
// offset += key * 0x74
// key = data[offset + proc[5 * n + i]]
// offset += 0x74
read(fd, buf, 0x74uLL);
key = buf[v12];
}
if ( key == 0x63617400 )
++v17;
++v16;
}
while ( v16 < v7 );
}
result = v17;
_InterlockedAdd((input_proc + 16), v17);
return result;
}

5 bytes一组用输入来控制文件指针 要求最后读到的数据中某处是0x63617400(cat\x0) 不知道怎么逆向计算出这5 bytes 直接爆破:

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
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>

using namespace std;

int main(){
// for(int _ = 1; _ <= 7; _++){
// string data = "data" + to_string(_);
// auto fd = open(data.c_str(), 0);
// int table[] = {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};
// for(int a : table){
// bool f = 0;
// for(int b : table){
// for(int c : table){
// for(int d : table){
// for(int e : table){
// int key = 0;
// int buf[29];
// int temp[5] = {a, b, c, d, e};
// for(int __ = 0; __ <= 4; __++){
// lseek(fd, 0x74 * key, 0);
// int v = temp[__];
// read(fd, buf, 0x74);
// key = buf[v];
// }
// if(key == 0x63617400){
// printf("{%d, %d, %d, %d, %d},\n", a, b, c, d, e);
// f = 1;
// break;
// }
// }
// if(f) break;
// }
// if(f) break;
// }
// if(f) break;
// }
// if(f) break;
// }
// close(fd);
// printf("====================\n");
// }
char alpha_table[29] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '_', '{', '}'};
int flag_pieces[7][5] = {
{11, 0, 2, 19, 5},
{27, 12, 4, 14, 22},
{26, 24, 14, 20, 26},
{5, 14, 20, 13, 3},
{26, 12, 4, 26, 4},
{15, 2, 18, 8, 7},
{13, 23, 14, 18, 28},
};
for(int i = 0; i < 7; i++){
for(int j = 0; j < 5; j++){
printf("%c", alpha_table[flag_pieces[i][j]]);
}
}
// lactf{meow_you_found_me_epcsihnxos}
}

爆破花了5mins左右 不知道这是不是预期解 得到的flag片段刚好就是按顺序排列的 说明GOMP_parallel函数产生的线程的id就是从0开始递增而不是随机的

rbp | 函数控制

DIE查壳 无壳64x IDA64打开:

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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
__int64 __fastcall sub_71247(char *a1)
{
int v1; // eax
__int64 result; // rax
int v3; // eax
int v4; // [rsp+14h] [rbp-FCh]
int v5; // [rsp+18h] [rbp-F8h]
int i; // [rsp+1Ch] [rbp-F4h]
int v7; // [rsp+20h] [rbp-F0h]
int j; // [rsp+24h] [rbp-ECh]
int k; // [rsp+28h] [rbp-E8h]
int m; // [rsp+2Ch] [rbp-E4h]
__int64 v11; // [rsp+30h] [rbp-E0h]
__int64 v12; // [rsp+38h] [rbp-D8h]
__int128 v13[2]; // [rsp+40h] [rbp-D0h] BYREF
__int64 v14; // [rsp+60h] [rbp-B0h]
int v15[38]; // [rsp+70h] [rbp-A0h]
unsigned __int64 v16; // [rsp+108h] [rbp-8h]

v16 = __readfsqword(0x28u);
v12 = sub_717F0(a1);
memset(v13, 0, sizeof(v13));
v14 = 0LL;
v4 = 0;
v5 = 2;
LABEL_7:
while ( v4 <= 9 )
{
++v5;
for ( i = 2; i < v5; ++i )
{
if ( !(v5 % i) )
goto LABEL_7;
}
v1 = v4++;
*(v13 + v1) = v5;
}
if ( v12 == DWORD1(v13[0]) * DWORD2(v13[0]) )
{
v15[0] = 0;
v15[1] = 13;
v15[2] = 15;
v15[3] = 24;
v15[4] = 10;
v15[5] = 23;
v15[6] = 13;
v15[7] = 0;
v15[8] = 2;
v15[9] = 21;
v15[10] = 7;
v15[11] = 26;
v15[12] = 15;
v15[13] = 2;
v15[14] = 0;
v15[15] = 23;
v15[16] = 5;
v15[17] = 24;
v15[18] = 24;
v15[19] = 21;
v15[20] = 23;
v15[21] = 0;
v15[22] = 18;
v15[23] = 15;
v15[24] = 10;
v15[25] = 7;
v15[26] = 5;
v15[27] = 18;
v15[28] = 0;
v15[29] = 29;
v15[30] = 23;
v15[31] = 26;
v15[32] = 24;
v15[33] = 15;
v15[34] = 29;
v15[35] = 0;
v11 = 1LL;
v7 = 0;
for ( j = 0; j <= 5; ++j )
{
for ( k = 0; k <= 5; ++k )
{
v3 = v7++;
if ( (a1[j] ^ a1[k]) != v15[v3] )
{
result = 0LL;
goto LABEL_32;
}
}
v11 *= a1[j];
}
if ( v11 == 0x15F6D1945A0LL ) //↑head with lactf{
{
if ( a1[34] == 125 )
{
for ( m = 6; m <= 33; ++m )
{
if ( flag != -1 )
{
reset(flag);
flag = -1;
}
if ( lowcase(a1[m]) != 1 || num(a1[m]) != 1 || underline(a1[m]) != 1 )
{
result = 0LL;
goto LABEL_32;
}
}
result = check(byte_70B00, "vwbowpcjrhpkobfryu") == 0;
}
else
{
result = 0LL;
}
}
else
{
result = 0LL;
}
}
else
{
result = 0LL;
}
LABEL_32:
if ( v16 != __readfsqword(0x28u) )
return sub_71800();
return result;
}

//lowercase:
__int64 __fastcall sub_68000(char a1)
{
int v2; // eax

if ( a1 <= 96 || a1 > 122 )
return 0LL;
v2 = dword_70AE4++;
byte_70B00[v2] = a1;
if ( (a1 == 98 || a1 == 99 || a1 == 104 || a1 == 115 || a1 == 116)
&& ++count != 2
&& count != 8
&& count != 9
&& count != 12 )
{
flag = 1;
}
if ( a1 == 100 )
flag = 2;
return 1LL;
}

//num:
__int64 __fastcall sub_69000(char a1)
{
if ( a1 <= 47 || a1 > 57 )
return 0LL;
if ( a1 - 48 != all_num % 10 )
return 0LL;
all_num /= 10;
if ( count == 3 )
flag = 2;
else
flag = 0;
return 1LL;
}

//underline:
__int64 __fastcall sub_70000(char a1)
{
if ( !(a1 * a1 % 9024) )
return 0LL;
flag = 0;
return 1LL;
}

//reset:
__int64 __fastcall reset(char flag)
{
unsigned int v1; // eax
unsigned int v2; // eax
unsigned int v3; // eax
__int64 result; // rax

if ( flag )
v1 = 3;
else
v1 = 7;
if ( sub_71850(lowcase, 4096LL, v1) == -1 )
sub_717C0();
if ( flag == 1 )
v2 = 7;
else
v2 = 3;
if ( sub_71850(num, 4096LL, v2) == -1 )
sub_717C0();
if ( flag == 2 )
v3 = 7;
else
v3 = 3;
result = sub_71850(underline, 4096LL, v3);
if ( result == -1 )
return sub_717C0();
return result;
}

//sub_710F9
__int64 __fastcall sub_710F9(__int64 a1, __int64 a2, __int64 a3)
{
__int64 result; // rax
int i; // [rsp+1Ch] [rbp-Ch]

for ( i = 0; i < dword_70AE4; ++i )
byte_70B00[i] = (byte_70B00[i] - 96) % 26 + 97;
*(a3 + 144) = 1LL;
result = a3;
*(a3 + 168) = **(a3 + 160);
return result;
}

通过栈帧可以得知sub_71850()实际上是mprotect() 直接点进去看不到

image-20240223224117646

mprotect()可以设置程序对函数的权限(wrx) 7对应的就是wrx 3对应wr 所以被设置为7的函数可以执行 程序就是靠这个函数控制控制流的 据此可以分析出程序的所有逻辑:

1
2
3
4
5
6
7
8
1.读取到特定字母会导致count_2 += 1
2.读取到字母会向byte_70B00中添加对应字母
3.除了第2, 8, 9, 12个特定字母以外特定字母后必须跟数字
4.字母d后必须跟下划线
5.普通字母后必须跟字母
6.第三个特殊字母后的第一个数字必须跟下划线
7.否则数字后必须跟字母
8.下划线后必须跟字母

动调中还发现读取到字母时就会触发两次main中的v4[0] = sub_710F9(不清楚原理 mprotect导致调试器无法正常触发断点 无法查看这个函数的汇编代码 估计是通过异常处理触发的) 据此写出脚本:

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
key = [ord(i) for i in "vwbowpcjrhpkobfryuryuryu"]
number_table = [i for i in "10430031"]
special = [ord(i) for i in "bchst"]
flag = [i for i in "lactf{"]
f = 0
f2 = 0
ind = 6
ind_for_num = 0
ind_for_alpha = 0
ind_for_alpha_in_table = 0
while(ind <= 33):
if f == 0:
now = (key[ind_for_alpha] - 97 + 100 * 26 - 2 * (34 - ind)) % 26 + 97
flag.append(chr(now))
ind += 1
ind_for_alpha += 1
if now in special:
ind_for_alpha_in_table += 1
if ind_for_alpha_in_table != 2 and ind_for_alpha_in_table != 8 and ind_for_alpha_in_table != 9 and ind_for_alpha_in_table != 12:
f = 1
elif now == 100:
f = 2
else:
f = 0
if ind_for_alpha_in_table == 3:
f2 = 1
continue
if f == 1:
now = number_table[ind_for_num]
flag.append(now)
ind += 1
ind_for_num += 1
if f2:
f = 2
f2 = 0
else:
f = 0
continue
if f == 2:
flag.append("_")
ind += 1
f = 0
continue
print("".join(flag) + "}")
# lactf{rub1sc0_b4s3d_ph0t0synth3s1s}

puzzlepalooza | Z3

DIE查壳 无壳64x IDA64打开:

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
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char *v3; // rdi
int v4; // edx
unsigned __int8 v5; // al
int v7; // edx
char *v8; // rdx
int v9; // ebx
int v10; // esi
int v11; // r9d
int v12; // r8d
int v13; // edi
unsigned int i; // eax
int v15; // edx
char v16; // cl
unsigned int v17; // ecx
unsigned int v18; // r14d
unsigned int v19; // edx
__m128i key; // [rsp+0h] [rbp-F8h]
__m128i v21[2]; // [rsp+10h] [rbp-E8h]
__int128 v22[2]; // [rsp+30h] [rbp-C8h] BYREF
__int64 v23; // [rsp+50h] [rbp-A8h]
int v24; // [rsp+58h] [rbp-A0h]
__int16 v25; // [rsp+5Ch] [rbp-9Ch]
char v26; // [rsp+5Eh] [rbp-9Ah] BYREF
char s[152]; // [rsp+60h] [rbp-98h] BYREF

puts("Welcome to the greatest puzzlepalooza ever!");
puts("Can you solve our puzzle without looking?");
fgets(s, 100, stdin);
s[strcspn(s, "\n")] = 0;
if ( strlen(s) == 54 )
{
v3 = s;
v4 = 0;
key = _mm_load_si128(&xmmword_5632A397C0E0);
v21[0] = _mm_load_si128(&xmmword_5632A397C0F0);
*(v21 + 9) = _mm_load_si128(&xmmword_5632A397C100);
while ( 1 )
{
v5 = *v3 - 64;
if ( v5 > 0x3Fu )
break;
key.get_sbyte[v4 >> 3] ^= v5 << (v4 & 7);
if ( (v4 & 7u) > 2 )
key.get_sbyte[(v4 >> 3) + 1] ^= v5 >> (8 - (v4 & 7));
v4 += 6;
++v3;
if ( v4 == 324 )
{
if ( (key.get_sbyte[0] & 0xFu) <= 8 )
{
v7 = 0;
while ( ++v7 != 81 )
{
if ( ((key.get_ubyte[v7 >> 1] >> (4 * (v7 & 1))) & 0xFu) > 8 )
goto LABEL_7;
}
v8 = v22;
v25 = 1871;
v23 = 0x64A0847033B0136LL;
v22[0] = _mm_load_si128(&xmmword_5632A397C0C0);
v24 = 38667339;
v22[1] = _mm_load_si128(&xmmword_5632A397C0D0);
while ( v8[1] == ((key.get_ubyte[*v8 >> 1] >> (4 * (*v8 & 1))) & 0xF) )
{
v8 += 2;
if ( &v26 == v8 )
{
v9 = 0;
while ( 1 )
{
v10 = v9;
v11 = 0;
v12 = 0;
v13 = 0;
for ( i = 0; i != 9; ++i )
{
v13 ^= 1 << ((key.get_ubyte[(i + 9 * v9) >> 1] >> (4 * ((i + 9 * v9) & 1))) & 0xF);
v15 = v10;
v16 = 4 * (v10 & 1);
v10 += 9;
v12 ^= 1 << ((key.get_ubyte[v15 >> 1] >> v16) & 0xF);
v17 = i / 3 + v9 - v9 % 3;
v18 = 3 * (i / 3);
v19 = i;
v11 ^= 1 << ((key.get_ubyte[(3 * (v9 % 3) + v19 - v18 + 9 * v17) >> 1] >> (4
* ((3 * (v9 % 3)
+ v19
- v18
+ 9 * v17) & 1))) & 0xF);
}
if ( v12 != 511 || v13 != 511 || v11 != 511 )
break;
if ( ++v9 == 9 )
{
puts("Woah! You're so good at puzzles!");
return 0LL;
}
}
goto LABEL_7;
}
}
}
break;
}
}
}
LABEL_7:
puts("That's not a valid solution you silly goose!");
return 1LL;
}

z3求解符合条件的flag:

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
from z3 import *

s = Solver()

key = [ 0x08, 0x6B, 0xD0, 0xFE, 0x49, 0xCB, 0xAC, 0x9B, 0x9C, 0xF7,
0x65, 0xBA, 0x4B, 0xAE, 0x95, 0x69, 0x08, 0xF3, 0xBC, 0x4E,
0xED, 0x18, 0x4A, 0x6B, 0xE0, 0xDE, 0xF4, 0x42, 0xE5, 0xD3,
0xD9, 0xA8, 0x3C, 0xCF, 0x4A, 0x49, 0x71, 0x0E, 0x16, 0x16,
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
key2 = [0x01, 0x06, 0x03, 0x05, 0x04, 0x02, 0x0F, 0x00, 0x14, 0x02,
0x15, 0x07, 0x18, 0x04, 0x19, 0x05, 0x1C, 0x03, 0x20, 0x04,
0x21, 0x05, 0x22, 0x08, 0x26, 0x05, 0x2C, 0x00, 0x30, 0x08,
0x35, 0x06, 0x36, 0x01, 0x3B, 0x03, 0x47, 0x08, 0x4A, 0x06,
0x4B, 0x04, 0x4E, 0x02, 0x4F, 0x07]
masks = [240, 240, 15, 0, 0, 0, 0, 240, 0, 0, 255, 0, 255, 0, 15, 0, 255, 15, 0, 15, 0, 0, 15, 0, 15, 0, 240, 15, 0, 240, 0, 0, 0, 0, 0, 240, 0, 255, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0]
key_after_mask = [96, 80, 2, 0, 0, 0, 0, 0, 0, 0, 114, 0, 84, 0, 3, 0, 84, 8, 0, 5, 0, 0, 0, 0, 8, 0, 96, 1, 0, 48, 0, 0, 0, 0, 0, 128, 0, 70, 0, 114, 0, 0, 0, 0, 0, 0, 0, 0]
flag = [BitVec('flag_%d' % i, 8) for i in range(54)]
s.add(And([And(flag[i] < 0x7f, flag[i] > 0x40) for i in range(54)]))
for i in range(0, 54 * 6, 6):
v5 = ((flag[int(i / 6)] - 0x40) << (i & 7)) & 0xff
key[i >> 3] ^= v5
if (i & 7) > 2:
v6 = ((flag[int(i / 6)] - 0x40) >> (8 - (i & 7))) & 0xff
key[(i >> 3) + 1] ^= v6
s.add(And([And([(key[i] >> 4) & 0xf <= 8, key[i] & 0xf <= 8]) for i in range(41)]))
for i in range(48):
s.add(key[i] & masks[i] == key_after_mask[i])
for round in range(9):
v11 = 0
v12 = 0
v13 = 0
count = round
for i in range(9):
temp1 = BitVec("temp1", 12)
temp2 = BitVec("temp2", 12)
temp1 = 1
temp2 = key[(i + 9 * round) >> 1]
temp2 >>= 4 * ((i + 9 * round) & 1)
temp2 &= 0xf
temp1 <<= temp2
v13 ^= temp1
v15 = count
temp1 = 1
temp2 = key[count >> 1]
temp2 >>= 4 * (count & 1)
temp2 &= 0xf
temp1 <<= temp2
v12 ^= temp1
temp1 = 1
v17 = int(i / 3) + round - round % 3
v18 = 3 * int(i / 3)
temp2 = key[(3 * (round % 3) + i - v18 + 9 * v17) >> 1]
temp2 >>= 4 * ((3 * (round % 3) + i - v18 + 9 * v17) & 1)
temp2 &= 0xf
temp1 <<= temp2
v11 ^= temp1
count += 9
s.add(And(v11 == 0x1FF, v12 == 0x1FF, v13 == 0x1FF))
final_key = [BitVec('final_key_%d' % i, 8) for i in range(41)]
for i in range(41):
s.add(final_key[i] == key[i])
s.add(flag[0] == ord("l"), flag[1] == ord("a"), flag[2] == ord("c"), flag[3] == ord("t"), flag[4] == ord("f"), flag[5] == ord("{"), flag[53] == ord("}"))

while s.check() == sat:
m = s.model()
for i in range(54):
print(chr(m[flag[i]].as_long()), end = "")
print()
# for i in range(41):
# print(hex(m[final_key[i]].as_long()), end = ", ")
# print()
condition = []
for d in m:
condition.append(d() != m[d])
s.add(Or(condition))
# lactf{looking_at_the_puzzles_is_honestly_so_overrated}

technically-correct | ELF文件格式

ELF文件头格式分析

总结

感觉大部分的题目考验的都是对代码的理解 并没有涉及非常神必的加密