中间有很多失误,没细心看算法直接猜了,还被符号数狠狠坑了一笔。

WEB

GameV4.0

签到题,F12在datajs中发现flag的base64字符串

image-20220212231433347

base64+URL解码即可

1
VNCTF{Welcome_to_VNCTF2022}

Crypto

ezmath

解决如下方程,提示知道第一个是15即n=4,可得如下等式
$$
2^n -1 mod 15==0 \quad ==> \quad 2^n mod 15==1
$$

n从4开始,左右两侧取多少次方都可以 故第k个 n就是4*k k=1,2,3,4…

exp如下,记得当时手动输入数据测试不行,用pwntool就可以了。

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
from hashlib import sha256
from pwn import *

def check(s,enc):
table = '0123456789abcdefghijklmnopqrstuvwxyz' + 'abcdefghijklmnopqrstuvwxyz'.upper()
ans=''
for i in table:
for j in table:
for k in table:
for l in table:
a = i + j + k + l + s
if sha256(a.encode()).hexdigest() == enc:
ans+=a[:4]
print(ans)
return (ans)


r=remote('node4.buuoj.cn',28687)
p=r.recvuntil(':').decode()[3:].replace(' == ','#')
s=p[p.index('+')+1:p.index(')')]
enc=p[p.index('#')+1:p.index('\n')]
tmp=check(s,enc)
r.sendline(tmp)
for i in range(777):
a=r.recvline().decode().split(' ')
num=int(a[4][:a[4].index('t')])
r.sendline(str(int(num)*4))
r.recvline()
r.interactive()
#flag{7e625b4e-fdec-47aa-8808-c59157d0bb1d}

RE

有点小遗憾,败在了C的符号数处理上 最后一题单单SM4秘钥扩就写了好久,赛后python写直接出了,百感交寄G_G

BabyMaze

python3.8 不可直接uncompyle反编译 用py38的dis或decompy++都可以反汇编

这里用decompy++ 执行 .\pycdas BabyMaze.pyc > code.txt

部分代码如下

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
BabyMaze.pyc (Python 3.8)
[Code]
File Name: .\BabyMaze.py
Object Name: <module>
Arg Count: 0
Pos Only Arg Count: 0
KW Only Arg Count: 0
Locals: 0
Stack Size: 61
Flags: 0x00000040 (CO_NOFREE)
[Names]
'_map'
'maze'
'main'
'__name__'
[Var Names]
[Free Vars]
[Cell Vars]
[Constants]
1
5
0
7
[Code]
File Name: .\BabyMaze.py
Object Name: maze
Arg Count: 0
Pos Only Arg Count: 0
KW Only Arg Count: 0
Locals: 4
Stack Size: 3
Flags: 0x00000043 (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)
[Names]
'input'
'range'
'len'
'_map'
[Var Names]
'x'
'y'
'step'
'i'
[Free Vars]
[Cell Vars]
[Constants]
None
1
'w'
's'
'a'
'd'
False
29
True
[Disassembly]
0 LOAD_CONST 1: 1
2 STORE_FAST 0: x
4 LOAD_CONST 1: 1
6 STORE_FAST 1: y
8 LOAD_GLOBAL 0: input
10 CALL_FUNCTION 0
12 STORE_FAST 2: step
14 LOAD_GLOBAL 1: range
16 LOAD_GLOBAL 2: len
18 LOAD_FAST 2: step
20 CALL_FUNCTION 1
22 CALL_FUNCTION 1
24 GET_ITER
26 FOR_ITER 142 (to 170)
28 STORE_FAST 3: i
30 LOAD_FAST 2: step
32 LOAD_FAST 3: i
34 BINARY_SUBSCR
36 LOAD_CONST 2: 'w'
38 COMPARE_OP 2 (==)
40 POP_JUMP_IF_FALSE 52
42 LOAD_FAST 0: x
44 LOAD_CONST 1: 1
46 INPLACE_SUBTRACT
48 STORE_FAST 0: x
50 JUMP_FORWARD 72 (to 124)
52 LOAD_FAST 2: step
54 LOAD_FAST 3: i
56 BINARY_SUBSCR
58 LOAD_CONST 3: 's'
60 COMPARE_OP 2 (==)
62 POP_JUMP_IF_FALSE 74
64 LOAD_FAST 0: x
66 LOAD_CONST 1: 1
68 INPLACE_ADD
70 STORE_FAST 0: x
72 JUMP_FORWARD 50 (to 124)
74 LOAD_FAST 2: step
76 LOAD_FAST 3: i
78 BINARY_SUBSCR
80 LOAD_CONST 4: 'a'
82 COMPARE_OP 2 (==)
84 POP_JUMP_IF_FALSE 96
86 LOAD_FAST 1: y
88 LOAD_CONST 1: 1
90 INPLACE_SUBTRACT
92 STORE_FAST 1: y
94 JUMP_FORWARD 28 (to 124)
96 LOAD_FAST 2: step
98 LOAD_FAST 3: i
100 BINARY_SUBSCR
102 LOAD_CONST 5: 'd'
104 COMPARE_OP 2 (==)
106 POP_JUMP_IF_FALSE 118
108 LOAD_FAST 1: y
110 LOAD_CONST 1: 1
112 INPLACE_ADD
114 STORE_FAST 1: y
116 JUMP_FORWARD 6 (to 124)
118 POP_TOP
120 LOAD_CONST 6: False
122 RETURN_VALUE
124 LOAD_GLOBAL 3: _map
126 LOAD_FAST 0: x
128 BINARY_SUBSCR
130 LOAD_FAST 1: y
132 BINARY_SUBSCR
134 LOAD_CONST 1: 1
136 COMPARE_OP 2 (==)
138 POP_JUMP_IF_FALSE 146
140 POP_TOP
142 LOAD_CONST 6: False
144 RETURN_VALUE
146 LOAD_FAST 0: x
148 LOAD_CONST 7: 29
150 COMPARE_OP 2 (==)
152 POP_JUMP_IF_FALSE 26
154 LOAD_FAST 1: y
156 LOAD_CONST 7: 29
158 COMPARE_OP 2 (==)
160 POP_JUMP_IF_FALSE 26
162 POP_TOP
164 LOAD_CONST 8: True
166 RETURN_VALUE
168 JUMP_ABSOLUTE 26
170 LOAD_CONST 0: None
172 RETURN_VALUE

其余主要是map数组,参考py字节码官网写出伪码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
x=1
y=1
step=input()
for i in range(len(step)):
if step[i]=='w':
x-=1
elif step[i]=='s':
x+=1
elif step[i]=='a':
y-=1
elif step[i]=='d':
y+=1
else:
print('noo')
if map[31*x+y]==1:
print('noo')
else:
if x==29 and y==29:
print('yes')

solve maze problem

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
#include<iostream>
uint8_t map[961] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
uint8_t ans[256] = { 0 };
uint8_t visit[961] = { 0 };
uint8_t next[4] = { 119,115,97,100 };
uint32_t step = 0;
void maze(uint32_t x, uint32_t y) {
visit[31 * x + y] = 1;
uint32_t tx, ty;
if (x == 29 && y == 29) {
printf("yes\n");
for (int i = 0; i < step; i++)
{
printf("%c", ans[i]);
}

exit(0);
}
for (int i = 0; i < 4; i++) {
switch (next[i])
{
case 119:
tx = x - 1;
ty = y;
break;
case 115:
tx = x + 1;
ty = y;
break;
case 97:
ty = y - 1;
tx = x;
break;
case 100:
ty = y + 1;
tx = x;
break;
}

if ((tx >= 0 && tx < 31) && (ty >= 0 && ty < 31) && map[31 * tx + ty] != 1 && visit[31 * tx + ty] == 0) {
ans[step] = next[i];
step += 1;
maze(tx, ty);
step -= 1;
}
}
return;
}
int main() {
maze(1, 1);
return 0;
}
//print('VNCTF{'+md5('ssssddssaassddddwwwwddwwddddddwwddddddssddwwddddddddssssaawwaassaassaassddssaassaawwwwwwaaaaaaaassaassddddwwddssddssssaassddssssaaaaaawwddwwaawwwwaassssssssssssddddssddssddddddddwwaaaaaawwwwddssddwwwwwwwwddssddssssssssddddss'.encode()).hexdigest()+'}')
//VNCTF{801f190737434100e7d2790bd5b0732e}

cm狗

go的虚拟机 索性是模拟的汇编大致相同,硬静态分析还是可以出的

VM_Init 将一些函数地址存在某偏移处,V1结构体有点大,没去定义。

VM_run

image-20220212233121694

根据VM_Run可知指令长度是3Byte,并且0xFF4偏移处是eip,0x1000偏移是opcode,接下来是对20个fun的分析

为了减少工作量可以提前对opcode的地址码进行遍历,只解释用到的指令即可

部分函数

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
__int64 __usercall main___ptr_MzVm__init_func2@<rax>(__int64 a1)
{ //高32位为Rnum 低32位为Lnum
__int64 v1; // rdx
__int64 v2; // rdx
__int64 result; // rax
__int64 v4; // [rsp+0h] [rbp-18h]

v2 = *(_QWORD *)(v1 + 8); // mov r[Lnum],Rnum
result = (unsigned int)a1;
if ( (unsigned int)a1 >= 0x15uLL )
runtime_panicIndex(v4);
*(_DWORD *)(v2 + 4LL * (unsigned int)a1) = HIDWORD(a1);// 0x14个寄存器 Lnum,Rnum
return result;
}

unsigned __int64 __usercall main___ptr_MzVm__init_func6@<rax>(unsigned int a1)
{
__int64 v1; // rdx
__int64 v2; // rdx
unsigned __int64 result; // rax
__int64 v4; // [rsp+0h] [rbp-18h]

v2 = *(_QWORD *)(v1 + 8);
result = (unsigned int)(*(_DWORD *)(v2 + 0xFF8) - 1);
*(_DWORD *)(v2 + 0xFF8) = result;
if ( a1 >= 0x15uLL )
runtime_panicIndex(v4);
if ( result >= 0x3E8 )
runtime_panicIndex(v4);
*(_DWORD *)(v2 + 4 * result + 84) = *(_DWORD *)(v2 + 4LL * a1);// push r[Lnum]
return result;
}

__int64 __usercall main___ptr_MzVm__init_func13@<rax>(unsigned int a1)
{
__int64 v1; // rdx
__int64 v2; // rdx
__int64 result; // rax
__int64 v4; // [rsp+0h] [rbp-18h]

v2 = *(_QWORD *)(v1 + 8);
if ( a1 >= 0x15uLL )
runtime_panicIndex(v4);
result = (unsigned int)(3 * *(_DWORD *)(v2 + 4LL * a1));// jmp r[Lnum]
*(_DWORD *)(v2 + 0xFF4) = result;
return result;
}

记住几个重要的偏移,剩下的猜结合调试很快就能了解函数功能

parser

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
op=[opcode...]
f=[]
for i in range(0,len(op),3):
if op[i]+1 not in f:
f.append(op[i]+1)

print(bytes.fromhex('61336366'))
for i in range(0,len(op),3):

adr=op[i]+1
Lnum=op[i+1]
Rnum=op[i+2]

if adr==1:
print("%s: "%hex(i)+f"nop")
elif adr==2:
print("%s: "%hex(i)+f"mov r[{Lnum}],{hex(Rnum)}")
elif adr==3:
print("%s: "%hex(i)+f"mov r[{Lnum}],r[{Rnum}]")
elif adr==6:
print("%s: "%hex(i)+f"push r[{Lnum}]")
elif adr==7:
print("%s: "%hex(i)+f"pop r[{Lnum}]")
elif adr==8:
print("%s: "%hex(i)+f"add r[{Lnum}],r[{Rnum}]")
elif adr==9:
print("%s: "%hex(i)+f"sub r[{Lnum}],r[{Rnum}]")
elif adr==10:
print("%s: "%hex(i)+f"div r[{Lnum}],r[{Rnum}]")
elif adr==11:
print("%s: "%hex(i)+f"mul r[{Lnum}],r[{Rnum}]")
elif adr==12:
print("%s: "%hex(i)+f"xor r[{Lnum}],r[{Rnum}]")
elif adr==13:
print("%s: "%hex(i)+f"jmp r[{Lnum}]")
elif adr==15:
print("%s: "%hex(i)+f"cmp r[{Lnum}],r[{Rnum}] -- jnz r[19]")
elif adr==0x62:
print("%s: "%hex(i)+f"getchar(r[{Lnum}])")
elif adr==0x63:
print("%s: "%hex(i)+f'putchar(r[{Lnum})]')
elif adr==0x64:
print("%s: "%hex(i)+f"exit()")

伪码

截取片段,前面的putchar 输出 VNCTF…啥的

1
2
3
4
5
6
7
8
0xcc: mov r[19],0x49
0xcf: mov r[3],0x0
0xd2: mov r[1],0x2b #长度0x2b
0xd5: mov r[2],0x1
0xd8: getchar(r[0])
0xdb: push r[0] #输入进栈
0xde: sub r[1],r[2]
0xe1: cmp r[1],r[3] -- jnz r[19]

arry2int

4个Byte转为 int

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
0xe4: mov r[0],0x0
0xe7: push r[0]
0xea: nop
0xed: nop
0xf0: pop r[0]
0xf3: mov r[5],0x100
0xf6: mul r[0],r[5]
0xf9: mov r[6],r[0]
0xfc: pop r[0]
0xff: add r[6],r[0]
0x102: mov r[0],r[6]
0x105: mul r[0],r[5]
0x108: mov r[6],r[0]
0x10b: pop r[0]
0x10e: add r[6],r[0]
0x111: mov r[0],r[6]
0x114: mul r[0],r[5]
0x117: mov r[6],r[0]
0x11a: pop r[0]
0x11d: add r[6],r[0]
0x120: nop

Tea

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
0x3f9: mov r[3],0x9e3779b9 ;delta
0x3fc: mov r[4],0x95c4c ;key
0x3ff: mov r[5],0x871d
0x402: mov r[6],0x1a7b7
0x405: mov r[7],0x12c7c7
0x408: mov r[8],0x0
0x40b: mov r[17],0x10
0x40e: mov r[18],0x20
0x411: mov r[19],0x160
0x414: mov r[10],0x0
0x417: mov r[11],0x20
0x41a: mov r[12],0x1
0x41d: add r[8],r[3]
0x420: mov r[0],r[2]
0x423: mul r[0],r[17]
0x426: add r[0],r[4]
0x429: mov r[14],r[0]
0x42c: mov r[0],r[2]
0x42f: add r[0],r[8]
0x432: mov r[15],r[0]
0x435: mov r[0],r[2]
0x438: div r[0],r[18]
0x43b: add r[0],r[5]
0x43e: mov r[16],r[0]
0x441: mov r[0],r[14]
0x444: xor r[0],r[15]
0x447: xor r[0],r[16]
0x44a: add r[1],r[0]
0x44d: mov r[0],r[1]
0x450: mul r[0],r[17]
0x453: add r[0],r[6]
0x456: mov r[14],r[0]
0x459: mov r[0],r[1]
0x45c: add r[0],r[8]
0x45f: mov r[15],r[0]
0x462: mov r[0],r[1]
0x465: div r[0],r[18]
0x468: add r[0],r[7]
0x46b: mov r[16],r[0]
0x46e: mov r[0],r[14]
0x471: xor r[0],r[15]
0x474: xor r[0],r[16]
0x477: add r[2],r[0]
0x47a: sub r[11],r[12]
0x47d: cmp r[11],r[10] -- jnz r[19]
0x480: jmp r[20]
0x483: nop

感谢作者Tea不魔改之恩

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
0x348: mov r[20],0x11c
0x34b: mov r[0],0x154
0x34e: jmp r[0]
0x351: mov r[0],0xe8d1d5df
0x354: mov r[19],0x183
0x357: mov r[20],0x153
0x35a: cmp r[1],r[0] -- jnz r[19]
0x35d: mov r[0],0xf5e3c114
0x360: cmp r[2],r[0] -- jnz r[19]
0x363: pop r[1]
0x366: pop r[2]
0x369: mov r[20],0x127
0x36c: mov r[0],0x154
0x36f: jmp r[0]
0x372: mov r[0],0x228ec216
0x375: mov r[19],0x183
0x378: mov r[20],0x153
0x37b: cmp r[1],r[0] -- jnz r[19]
0x37e: mov r[0],0x89d45a61
0x381: cmp r[2],r[0] -- jnz r[19]
0x384: pop r[1]
0x387: pop r[2]
0x38a: mov r[20],0x132
0x38d: mov r[0],0x154
0x390: jmp r[0]
0x393: mov r[0],0x655b8f69
0x396: mov r[19],0x183
0x399: mov r[20],0x153
0x39c: cmp r[1],r[0] -- jnz r[19]
0x39f: mov r[0],0x2484a07a
0x3a2: cmp r[2],r[0] -- jnz r[19]
0x3a5: pop r[1]
0x3a8: pop r[2]
0x3ab: mov r[20],0x13d
0x3ae: mov r[0],0x154
0x3b1: jmp r[0]
0x3b4: mov r[0],0xd9e5e7f8
0x3b7: mov r[19],0x183
0x3ba: mov r[20],0x153
0x3bd: cmp r[1],r[0] -- jnz r[19]
0x3c0: mov r[0],0x3a441532
0x3c3: cmp r[2],r[0] -- jnz r[19]
0x3c6: pop r[1]
0x3c9: pop r[2]
0x3cc: mov r[20],0x148
0x3cf: mov r[0],0x154
0x3d2: jmp r[0]
0x3d5: mov r[0],0x91ab7e88
0x3d8: mov r[19],0x183
0x3db: mov r[20],0x153
0x3de: cmp r[1],r[0] -- jnz r[19]
0x3e1: mov r[0],0x69fc64bc
0x3e4: cmp r[2],r[0] -- jnz r[19]
0x3e7: pop r[1]
0x3ea: mov r[0],0x7d3765
0x3ed: cmp r[1],r[0] -- jnz r[19]
0x3f0: mov r[0],0x189
0x3f3: jmp r[0]
0x3f6: exit()

长度44,转为int是11个,前10个两两一组Tea,最后一个不处理,解密即可。

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
#include<iostream>
#define ut32 unsigned int
#define delta 0x9E3779B9
void Tea_Decrypt(ut32* enc, ut32* k) {
ut32 sum = delta * 0x20;
ut32 v0 = enc[0];
ut32 v1 = enc[1];
for (int i = 0; i < 0x20; i++) {
v1 -= ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
v0 -= ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
sum -= delta;
}
enc[0] = v0;
enc[1] = v1;
}


int main() {
ut32 m[2] = { 0xe8d1d5df,0xf5e3c114 }; //依次密文
ut32 k[4] = { 0x95c4c,0x871d,0x1a7b7,0x12c7c7};
Tea_Decrypt(m, k);
printf("%x %x", m[0], m[1]);
return 0;
}
//常规Tea解密
//VNCTF{ecd63ae5-8945-4ac4-b5a5-34fc3ade81e7}

cm1

Android题主要逻辑在隐藏的一个dex文件中

image-20220212234819160

主要是copyfile这个函数

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
public static void copyFiles(android.content.Context r6, java.lang.String r7, java.io.File r8) {

java.lang.String r0 = "vn2022"
byte[] r0 = r0.getBytes()
r1 = 0
android.content.Context r6 = r6.getApplicationContext() // Catch: IOException -> 0x005a, all -> 0x0056
android.content.res.AssetManager r6 = r6.getAssets() // Catch: IOException -> 0x005a, all -> 0x0056
java.io.InputStream r6 = r6.open(r7) // Catch: IOException -> 0x005a, all -> 0x0056
java.io.FileOutputStream r7 = new java.io.FileOutputStream // Catch: IOException -> 0x0052, all -> 0x004f
java.lang.String r8 = r8.getAbsolutePath() // Catch: IOException -> 0x0052, all -> 0x004f
r7.<init>(r8) // Catch: IOException -> 0x0052, all -> 0x004f
r8 = 1024(0x400, float:1.435E-42)
byte[] r8 = new byte[r8] // Catch: IOException -> 0x004d, all -> 0x004b
L_0x0020:
int r1 = r6.read(r8) // Catch: IOException -> 0x004d, all -> 0x004b
r2 = -1
if (r1 == r2) goto L_0x003f
r2 = 0
r3 = 0
L_0x0029:
if (r3 >= r1) goto L_0x003b
byte r4 = r8[r3] // Catch: IOException -> 0x004d, all -> 0x004b
int r5 = r0.length // Catch: IOException -> 0x004d, all -> 0x004b
int r5 = r3 % r5
byte r5 = r0[r5] // Catch: IOException -> 0x004d, all -> 0x004b
r4 = r4 ^ r5
r4 = r4 & 255(0xff, float:3.57E-43)
byte r4 = (byte) r4 // Catch: IOException -> 0x004d, all -> 0x004b
r8[r3] = r4 // Catch: IOException -> 0x004d, all -> 0x004b
int r3 = r3 + 1
goto L_0x0029
L_0x003b:
r7.write(r8, r2, r1) // Catch: IOException -> 0x004d, all -> 0x004b
goto L_0x0020

有点小坑,这里是1024byte读取一次,每次重新开始与vn2022异或,不是直接异或 (坑死了,粗心)

re dex

1
2
3
4
5
6
7
8
9
10
11
key=b'vn2022'
f1=open(r'ooo','rb+')
f2=open(r'111','wb+')
while True:
t=[]
s=f1.read(1024)
if len(s)==0:
break
for i in range(len(s)):
t.append(s[i]^key[i%len(key)])
f2.write(bytes(t))

image-20220212235258147

主体是XXtea,其余就是一些 arry2int 小端序变化 ,请出祖传的XXTea脚本即可,delta没魔改(-法转为+法补码是一样的)

获取key和enc,其实就是小端序转int

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from Crypto.Util.number import *
a=b'H4pPY_VNCTF!!OvO'
r=[0]*4
for i in range(16):
i2 = i >> 2
r[i2] = r[i2] | ((a[i] & 255) << ((i & 3) << 3))
for i in r:
print(hex(i),end=',')

print()
enc=[0]*11
c=[0x44 ,0x27 ,0xffffffa4 ,0x6c ,0xffffffae ,0xffffffee ,0x48 ,0xffffffc9 ,0x4a ,0xffffffc8 ,0x26 ,0x0b ,0x3c ,0x54 ,0x61 ,0xffffffd8 ,0x57 ,0x47 ,0x63 ,0xffffffae ,0x78 ,0x68 ,0x2f ,0xffffffb9 ,0xffffffc6 ,0xffffffc7 ,0x00 ,0x21 ,0x2a ,0x26 ,0xffffffd4 ,0xffffffd9 ,0xffffffc4 ,0x71 ,0xfffffffe ,0x5c ,0xffffffb5 ,0x76 ,0xffffffb3 ,0x32 ,0xffffff87 ,0x2b ,0x20 ,0xffffff96]
for i in range(len(c)):
i2 = i >> 2
enc[i2] = enc[i2] | ((c[i] & 255) << ((i & 3) << 3))
for i in enc:
print(hex(i),end=',')
print()
key=[0x50703448,0x4e565f59,0x21465443,0x4f764f21]
enc=[0x6ca42744,0xc948eeae,0xb26c84a,0xd861543c,0xae634757,0xb92f6878,0x2100c7c6,0xd9d4262a,0x5cfe71c4,0x32b376b5,0x96202b87]

XXtea

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

#include <stdint.h>
#include<stdio.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))

void btea(uint32_t* v, int n, uint32_t const key[4]) {
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) { /* Coding Part */
rounds = 6 + 52 / n; //加密轮数
sum = 0;
z = v[n - 1];
do {
sum += DELTA;
e = (sum >> 2) & 3;
for (p = 0; p < n - 1; p++) {
y = v[p + 1];
z = v[p] += MX;
}
y = v[0];
z = v[n - 1] += MX;
} while (--rounds);
}

else if (n < -1) { /* Decoding Part */
n = -n;
rounds = 6 + 52 / n;
sum = rounds * DELTA;
y = v[0];
do {
e = (sum >> 2) & 3;
for (p = n - 1; p > 0; p--) {
z = v[p - 1];
y = v[p] -= MX;
}
z = v[n - 1];
y = v[0] -= MX;
sum -= DELTA;
} while (--rounds);
}
}


int main() {
unsigned int m[11] = { 0x6ca42744,0xc948eeae,0xb26c84a,0xd861543c,0xae634757,0xb92f6878,0x2100c7c6,0xd9d4262a,0x5cfe71c4,0x32b376b5,0x96202b87 };
unsigned int k[4] = { 0x50703448,0x4e565f59,0x21465443,0x4f764f21 };
btea(m, -11, k);
for (int i = 0; i < 11; i++) {
printf("0x%x ,",m[i]);
}
return 0;
}

//VNCTF{93ee7688-f216-42cb-a5c2-191ff4e412ba}

时空飞行

太遗憾了,被C++的符号搞懵逼了

首先是求日期,处理函数类似SM4的秘钥扩展,不过L函数魔改了

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
void __fastcall SM4_keyInit(_DWORD *a1, unsigned int *a2)
{
int v2; // esi
int v3; // ebx
int v4[36]; // [rsp+20h] [rbp-B0h]
unsigned int v5; // [rsp+B0h] [rbp-20h]
unsigned int v6; // [rsp+B4h] [rbp-1Ch]
unsigned int v7; // [rsp+B8h] [rbp-18h]
unsigned int v8; // [rsp+BCh] [rbp-14h]
int i; // [rsp+CCh] [rbp-4h]

v5 = _byteswap_ulong(*a2);
v6 = _byteswap_ulong(a2[1]);
v7 = _byteswap_ulong(a2[2]);
v8 = _byteswap_ulong(a2[3]);
v4[0] = v5 ^ 0xA3B1BAC6;
v4[1] = v6 ^ 0x56AA3350;
v4[2] = v7 ^ 0x677D9197;
v4[3] = v8 ^ 0xB27022DC;
for ( i = 0; i <= 31; ++i )
{
v2 = i + 4;
v3 = v4[i];
v4[v2] = sub_401A3B(v4[i + 3] ^ v4[i + 2] ^ (unsigned int)v4[i + 1] ^ CK[i]) ^ v3;
a1[i] = v4[i + 4];
}
}

__int64 __fastcall sub_401A3B(int a1)
{
return a1 ^ (unsigned int)(__ROL4__(a1, 13) ^ __ROR4__(a1, 9));
}

给出了Rkey的后4位,直接逆序往前推即可,主要加密是异或,具有对称性。

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
def L(a):
x=((a<<13)|(a>>(32-13)))&0xffffffff
y=((a>>9)|(a<<(32-9)))&0xffffffff
return x^y^a
FK=[0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]
CK=[0x00070E15, 0x1C232A31, 0x383F464D, 0x545B6269, 0x70777E85, 0x8C939AA1, 0xA8AFB6BD, 0xC4CBD2D9,
0xE0E7EEF5, 0xFC030A11, 0x181F262D, 0x343B4249, 0x50575E65, 0x6C737A81, 0x888F969D, 0xA4ABB2B9,
0xC0C7CED5, 0xDCE3EAF1, 0xF8FF060D, 0x141B2229, 0x30373E45, 0x4C535A61, 0x686F767D, 0x848B9299,
0xA0A7AEB5, 0xBCC3CAD1, 0xD8DFE6ED, 0xF4FB0209, 0x10171E25, 0x2C333A41, 0x484F565D, 0x646B7279]

def change(k):
for i in range(4):
k[i]^=FK[i]
for i in range(32):
tmp=L(k[i+1]^k[i+2]^k[i+3]^CK[i])
k.append(tmp^k[i])
for i in range(4,36):
print(hex(k[i]),end=',')

def reverse():
k=[0]*36
k[32] = 0xFD07C452
k[33] = 0xEC90A488
k[34] = 0x68D33CD1
k[35] = 0x96F64587
for i in range(31,-1,-1):
tmp=L(k[i+1]^k[i+2]^k[i+3]^CK[i])
k[i]=tmp^k[i+4]
for i in range(4):
k[i] ^= FK[i]
for i in range(4):
print(int.to_bytes(k[i],4,'big').decode(),end='')

reverse()
#20211205

之后又进行输入,第一次处理函数类似AES秘钥扩展

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
  for ( i = 0; i <= 5; ++i )
a1[i] = sub_401E21((char *)(4 * i + a2)); // 大端序
v5 = 6;
v3 = 0;
while ( v5 <= 65 )
{
if ( v5 % 6 )
{
a1[v5] = a1[v5 - 6] ^ a1[v5 - 1];
}
else
{
v2 = a1[v5 - 6];
a1[v5] = v2 ^ sub_401FFB(a1[v5 - 1], v3++);
}
++v5;
}
}

__int64 __fastcall sub_401FFB(unsigned int a1, int a2)
{
char v3[28]; // [rsp+20h] [rbp-20h] BYREF
unsigned int v4; // [rsp+3Ch] [rbp-4h]

int_to_array(a1, v3); // 大端
shift_left1(v3, 1i64); //左移一
v4 = array_to_int(v3); // 大端
return v4 ^ round_key[a2]; //异或轮秘钥
}

之后转为小端序进行一个不可逆的异或处理。

1
2
3
4
5
6
7
8
9
for ( i = 0; i <= 5; ++i )                    // 扩展为66 v5是24个字节
{
v4[4 * i] = (unsigned __int8)ext_key[i + 60];
v4[4 * i + 1] = (unsigned __int8)BYTE1(ext_key[i + 60]);
v4[4 * i + 2] = (unsigned __int8)BYTE2(ext_key[i + 60]);
v4[4 * i + 3] = HIBYTE(ext_key[i + 60]);
} // v4是最后一轮key
for ( i = 1; i <= 23; ++i )
v4[i - 1] ^= (v4[i - 1] % 0x12u + v4[i] + 5) ^ 0x41;

之前遇到过类似不可逆的处理,可以用dfs爆破所有结果。

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
import struct, copy
#key1='20211205'
def arr2int(k): #小端
m=[0]*(len(k)//4)
for i in range(len(k)):
m[i//4]|=k[i]<<((i&3)*8)
return m

def F(a,round):
s=hex(a)[2:].zfill(8)
k=[]
for i in range(0,8,2): #大端序
k.append(int(s[i:i+2],16))
m=[0]*4
m[0]=k[1]
m[1]=k[2]
m[2]=k[3]
m[3]=k[0]

ans=0
for i in range(4):
ans|=m[i]<<((3-i)*8)

return ans^rk[round]


def dfs_get_cipher2(k): # 递归枚举所可能的密文
if k == 0:
cipher_list.append(copy.deepcopy(c))
return
for j in range(0x100):
if final[k-1] == j^(j%0x12+c[k] + 5)^0x41 :
c[k-1] = j
dfs_get_cipher2(k-1)

final = b'%\x15\xdf\xa2\xc0\x93\xad\x14F\xc5\x0f.\x9a\xeb0\xf8 \xe9\xcb\x88\xc6\xbe\x8d\xe3'
c = [-1] * 24
c[23] = final[23]
cipher_list = []
dfs_get_cipher2(23)
k=cipher_list[0]

rk=[0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000]
for k in cipher_list:
m=arr2int(k)
ff=[0]*60
ff.extend(m)

v3=9
for i in range(59,-1,-1):
if i%6!=0:
ff[i]=ff[i+6]^ff[i+5]
else:
ff[i]=ff[i+6]^F(ff[i+5],v3)
v3-=1
for i in range(6):
try:
print(int.to_bytes(ff[i],4,'big').decode(),end='')
except:
break
print()
#VNCTF{TimeFlightMachine} 爆破筛选结果

#VNCTF{TimeFl20211205ightMachine}

加密都是异或,第一步AES秘钥扩展也比较容易

image-20220213000738274

不虚此行,别等叶枯萎。

长路漫漫,再回首,依旧是昔日零解少年,当与君共勉,继续寻找新的答案。