第三届黄河流域公安院校网络安全技能挑战赛 WriteUp (Reverse方向)

2.9k 词

由于和京麒同天,优先打了京麒,但是把题都下下来了,比赛结束后自己做了一遍.

签个到:

Part1部分所给出的是一个加密函数的字节码,写入一个文件后ida打开并解析,发现在64bit和32bit下均只有一个异或,奇偶位分别异或的是0x310x58,提取密文异或后得到flag的上半段iwannaknow,对于下半段进行解包:

1
2
3
4
5
6
7
8
9
from secret import decrypt
key = bytes.fromhex('EC3700DFCD4F364EC54B19C5E7E26DEF6A25087C4FCDF4F8507A40A9019E3B48BD70129D0141A5B8F089F280F4BE6CCD')
ciphertext = b'\xd4z\'0L\x10\xca\x0b\x0b\xaa\x15\xbeK0"\xbf\xb2\xc6\x05'
cipher = decrypt(ciphertext, key)
a = bytes(input('flag呢'), encoding='utf-8')
if a == cipher:
print('没错没错')
else:
print('不对不对')

反编译secret:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def key_schedule(key: bytes) -> list:
S = list(range(128))
v6 = 0
for j in range(128):
v6 = (S[j] + key[j % len(key)] + v6) % 128
v6 = (v6 ^ 55) % 128
S[j], S[v6] = (S[v6], S[j])
return S

def next_byte(state: dict) -> int:
S = state['S']
state['i'] = (state['i'] + 1) % 128
state['j'] = (state['j'] + S[state['i']]) % 128
S[state['i']], S[state['j']] = (S[state['j']], S[state['i']])
v2 = S[(S[state['i']] + S[state['j']]) % 128]
return (16 * v2 | v2 >> 4) & 255

def decrypt(ciphertext: bytes, key: bytes) -> bytes:
state = {'S': key_schedule(key), 'i': 0, 'j': 0}
plaintext = bytearray()
for byte in ciphertext:
plaintext.append(byte ^ next_byte(state))
return bytes(plaintext)

直接写入同一个python文件,解密并打印得到第二段what_DO_you_mean#@!
拼合得到flag{iwannaknowwhat_DO_you_mean#@!}

go:

查看main:

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
void __fastcall main_main()
{
p_string = (string *)runtime_newobject(&RTYPE_string);
p_string_1 = p_string;
p_string->ptr = 0;
v56[0] = &RTYPE__ptr_string;
v56[1] = p_string;
v2 = qword_857068;
fmt_Fscanf(2, v56, v3, "%s", 1, 1);
if ( v2 || p_string_1->len != 0x20 ) // 长度为32
goto LABEL_4;
input[0] = v0;
input[1] = v0;
len_2 = (unsigned __int64)p_string_1->ptr;
input_1 = runtime_stringtoslicebyte(v48, p_string_1->ptr, p_string_1->len);
v51 = input_1;
ptr_1 = len_2;
v46 = v6;
for ( i = 0; (__int64)len_2 > i; i = len_1 + 8 )
{
len_1 = i;
while ( i < (__int64)(len_1 + 4) )
{
if ( len_2 <= i )
runtime_panicIndex(i, len_2, len_2);
if ( len_2 <= i + 2 )
runtime_panicIndex(i + 2, len_2, len_2);
res = *(_BYTE *)(i + input_1) ^ *(_BYTE *)(i + input_1 + 2);
*(_BYTE *)(input_1 + i) = res;
if ( len_2 <= i + 1 )
runtime_panicIndex(i + 1, len_2, len_2);
res_1 = res ^ *(_BYTE *)(i + input_1 + 2) ^ *(_BYTE *)(i + input_1 + 1);
*(_BYTE *)(i + input_1 + 1) = res_1;
*(_BYTE *)(i + input_1 + 2) ^= res_1;
i += 3LL;
}
v57[0] = 0;
v58 = 0;
v59 = v0;
p_bytes_Buffer_1 = 0;
v10 = (_QWORD *)math_big__ptr_Int_SetInt64(v57, 0);
ptr_4 = len_1;
ptr_5 = len_1 + 8;
if ( v46 < len_1 + 8 )
runtime_panicSliceAcap(v10, 0, len_1 + 8);
if ( len_1 > ptr_5 )
goto LABEL_47;
v50 = v10;
v13 = (len_1 & ((__int64)(len_1 - v46) >> 63)) + v51;
v14 = v10[2];
v15 = math_big_nat_setBytes(v10[1], v14, v10[3], v13, 8, v46 - len_1);
v16 = v50;
v50[2] = v14;
v16[3] = v17;
if ( dword_8ABB30 )
{
LODWORD(v13) = (_DWORD)v16 + 8;
runtime_gcWriteBarrier(v16 + 1);
}
else
{
v16[1] = v15;
}
*(_BYTE *)v16 = 0;
if ( v16[2] )
v18 = *(_QWORD *)v16[1];
else
v18 = 0;
for ( n64 = 0; n64 < 64; ++n64 )
{
if ( v18 < 0 )
v18 = (2 * v18) ^ 0x2EF20D07161E85F7LL;
else
v18 *= 2;
}
v44 = v18;
p_bytes_Buffer = (bytes_Buffer *)runtime_newobject(&RTYPE_bytes_Buffer);
v25 = (_QWORD *)runtime_convT64(v44, v14, v20, v13, 8, v21, v22, v23, v24, v43);
p_bytes_Buffer_1 = p_bytes_Buffer;
v29 = encoding_binary_Write(
(__int64)go_itab__ptr_bytes_Buffer_comma__ptr_io_Writer,
(__int64)p_bytes_Buffer,
(void (__golang **)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD))go_itab__ptr_binary_littleEndian_comma__ptr_binary_ByteOrder,
(__int64)&unk_8ABAD8,
&RTYPE_int64,
v25,
v26,
v27,
v28);
if ( v29 )
{
runtime_gopanic(
(unsigned int)v29[1],
(_DWORD)p_bytes_Buffer_1,
v30,
(unsigned int)&unk_8ABAD8,
(unsigned int)&RTYPE_int64,
v31,
v32,
v33,
v34,
v43);
LABEL_47:
runtime_panicSliceB(ptr_4, p_bytes_Buffer_1, ptr_5);
}
p_bytes_Buffer_2 = p_bytes_Buffer;
for ( n8 = 0; n8 < 8; ++n8 )
{
for ( p_bytes_Buffer_3 = 0; p_bytes_Buffer_3 < 8; ++p_bytes_Buffer_3 )
{
len = p_bytes_Buffer_2->buf.len;
off = p_bytes_Buffer_2->off;
if ( off > len )
runtime_panicSliceB(p_bytes_Buffer_2->off, p_bytes_Buffer_1, p_bytes_Buffer_2->buf.len);
p_bytes_Buffer_4 = len - off;
if ( p_bytes_Buffer_3 >= p_bytes_Buffer_4 )
runtime_panicIndex(p_bytes_Buffer_3, p_bytes_Buffer_1, p_bytes_Buffer_4);
p_bytes_Buffer_1 = (bytes_Buffer *)p_bytes_Buffer_3;
if ( ((0x80u >> n8)
& __ROL1__(
p_bytes_Buffer_2->buf.ptr[(((__int64)(off - p_bytes_Buffer_2->buf.cap) >> 63) & p_bytes_Buffer_2->off)
+ p_bytes_Buffer_3],
5)) != 0 )
{
if ( n8 + len_1 >= 0x20 )
runtime_panicIndex(n8 + len_1, p_bytes_Buffer_3, 32);
*((_BYTE *)input + n8 + len_1) |= 0x80u >> p_bytes_Buffer_3;
}
}
}
input_1 = v51;
len_2 = ptr_1;
}
cipher[0] = 0x8ADD5C04E5934C8LL;
cipher[1] = 0x199AC0E6DA4C2BC9LL;
cipher[2] = 0xFF83F5E87D5510B5LL;
cipher[3] = 0x58447D6AD4E38B74LL;
if ( (unsigned __int8)runtime_memequal(input, cipher, 32) )
{
v55[0] = &RTYPE_string;
v55[1] = &off_7CE248; // "Right!"
fmt_Fprintln(go_itab__ptr_os_File_comma__ptr_io_Writer, qword_857070, v55, 1, 1);
}
else
{
LABEL_4:
v54[0] = &RTYPE_string;
v54[1] = &off_7CE258; // "Wrong!"
fmt_Fprintln(go_itab__ptr_os_File_comma__ptr_io_Writer, qword_857070, v54, 1, 1);
}
}

发现一共有数层加密,分别为连续异或、反转端序、左移+选择性异或、字节左移5位和二进制位行列重排,编写脚本,先对其进行同构,并逆向其中可逆的部分:

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
def q2ble(dword_array):
byte_list = []
for dword in dword_array:
byte_list.extend(dword.to_bytes(8, byteorder='little'))
return byte_list

def b2qle(byte_array):
return [int.from_bytes(byte_array[i:i+8], byteorder='little', signed=False)
for i in range(0, len(byte_array), 8)]

m = bytearray(b"flag{??????????????????????????}")
output = []

for i in range(0, len(m), 8):
j = i
while i < j + 4:
m[i] = m[i] ^ m[i+2]
m[i+1] = m[i] ^ m[i+2] ^ m[i+1]
m[i+2] ^= m[i+1]
i += 3
print(m)
e = b2qle(m[j:j+8][::-1])[0]
print(hex(e))
for _ in range(64):
if e >= 0x8000000000000000: e = e << 1 & 0xFFFFFFFFFFFFFFFF ^ 0x2EF20D07161E85F7
else: e = e << 1 & 0xFFFFFFFFFFFFFFFF
k = q2ble([e])
print(hex(b2qle(k)[0]))
for p in range(8):
rol = ((k[p] << 5) | (k[p] >> 3)) & 0xFF
k[p] = bin(rol + 0b100000000)[3:]
print(k)
u = [""]*8
for q in range(8):
for r in range(8):
u[q] += k[r][q:q+1]
print(u)
re = ""
for s in range(8):
re = hex(int("0b" + u[s], 2) + 0x100)[3:] + re
print(re)
output.append(int("0x" + re, 16))

for l in range(4):
print(hex(output[l]), end = ", " if not l == 3 else "\n")

output = [0x08ADD5C04E5934C8, 0x199AC0E6DA4C2BC9, 0xFF83F5E87D5510B5, 0x58447D6AD4E38B74]

c = q2ble(output)
print(c)
for i in range(0, len(c), 8):
otc = [bin(i+0b100000000)[3:] for i in c[i:i+8]]
print(otc)
ote = [""]*8
for q in range(8):
for r in range(8):
ote[q] += otc[r][q:q+1]
print(ote)
ind = [None]*8
for p in range(8):
ote[p] = int("0b" + ote[p], 2)
ind[p] = ((ote[p] >> 5) | (ote[p] << 3)) & 0xFF
mdc = b2qle(ind)[0]
print(hex(mdc))

得到中间密文0x3180b29d2312e574,0xed2419f9842e5f4,0xbd18adc8afece97c,0x2343b46aac65fd83,剩余的部分用z3求解:

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

PRINTABLE_RANGE = (0x20, 0x7E)

def simulate_block(data):
d = data[:]
i = 0
while i < 4:
d[i] ^= d[i+2]
d[i+1] ^= d[i] ^ d[i+2]
d[i+2] ^= d[i+1]
i += 3
return d

def b2qle_z3(bytes_list):
return Concat(list(reversed(bytes_list)))

def crc64_step(e):
poly = 0x2EF20D07161E85F7
for _ in range(64):
high_bit = (e >> 63) & 1
e = If(high_bit != 0, (e << 1) ^ poly, e << 1)
return e

solver = Solver()

flag_len = 32
flag_bytes = [BitVec(f'flag_{i}', 8) for i in range(flag_len)]

for b in flag_bytes:
solver.add(b >= PRINTABLE_RANGE[0], b <= PRINTABLE_RANGE[1])

outputs = [0x3180b29d2312e574,0xed2419f9842e5f4,0xbd18adc8afece97c,0x2343b46aac65fd83]

blocks = []
for idx in range(0, flag_len, 8):
block = flag_bytes[idx:idx+8]
if len(block) < 8:
block += [BitVecVal(0, 8)] * (8 - len(block))

transformed = simulate_block(block)
reversed_block = list(reversed(transformed))
qword = b2qle_z3(reversed_block)
encrypted_qword = crc64_step(qword)

blocks.append(encrypted_qword)

for out, expected in zip(blocks, outputs):
solver.add(out == expected)

if solver.check() == sat:
model = solver.model()
flag = bytes([model.eval(b).as_long() for b in flag_bytes])
print("Flag:", b"flag{" + flag + b"}")
else:
print("No solution found.")

得到flag:flag{5e28e27a835c4958b2d6dd186b21727a}.

rust:

查看main:

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
__int64 sub_1400040E0()
{
v29 = -2i64;
loadstring(v17, &off_140021CC0); // "input your flag:\n"
std::io::stdio::_print::hfb1cb878ecb43904(v17);
sub_140002A10(input);
stdin = std::io::stdio::stdin::he1ba180739c71ab9();
v19[0] = std::io::stdio::Stdin::read_line::h381832b7ef3a4ed2(&stdin, input);
v19[1] = v0;
sub_140001B80(v19);
in = sub_1400036A0(input);
input_ = sub_140002080(in, v2);
v16 = v3;
key = (void *)sub_140005C00(8i64, 1i64);
qmemcpy(key, "lntfvpus", 8);
loadenc(key_buf, key, 8i64);
jumpbuf_sp = _except_get_jumpbuf_sp(key_buf);
v22[0] = sub_140006470(0i64, jumpbuf_sp);
v22[1] = v5;
while ( 1 )
{
v23 = sub_140005440(v22);
index = v6;
if ( !v23 )
break;
xorkey = index;
loadkey = (_BYTE *)sub_140006780(key_buf, index, &off_140021D10);// "rust.rs"
*loadkey ^= xorkey;
}
v7 = sub_1400065E0(key_buf);
sub_7FF6870F3C80((unsigned int)_input, input_, len_input, key_, len_key);
cipher = (_BYTE *)sub_140005C00(19i64, 1i64);
*cipher = 0x29;
cipher[1] = 5;
cipher[2] = 0x13;
cipher[3] = 0xC;
cipher[4] = 0xE7;
cipher[5] = 0xA5;
cipher[6] = 0xD2;
cipher[7] = 0xA2;
cipher[8] = 0xA4;
cipher[9] = 0x3A;
cipher[10] = 0x3A;
cipher[11] = 0x5A;
cipher[12] = 0xBB;
cipher[13] = 0x23;
cipher[14] = 0x9C;
cipher[15] = 0xE4;
cipher[16] = 0xD7;
cipher[17] = 2;
cipher[18] = 0xBF;
loadenc(enc, cipher, 19i64);
v10 = checkinput(_input, enc);
if ( (v10 & 1) != 0 )
{
loadstring(v27, &off_140021D00); // "right!!!\n"
std::io::stdio::_print::hfb1cb878ecb43904(v27);
}
else
{
loadstring(v28, &off_140021CE0); // "wrong...\n"
std::io::stdio::_print::hfb1cb878ecb43904(v28);
}
sub_140001AB0(enc);
sub_140001AB0(_input);
sub_140001AB0(key_buf);
return sub_140001A70(input);
}

可以看到是先读取输入,然后加载了一个密钥“lntfvpus”,再对密钥按索引进行异或(得到的是“loverust”),然后加载密文,并与加密的明文进行比较,动态调试查找加密逻辑,注意到sub_7FF6870F3C80带入了所有的关键参数,查看该函数:

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
__int64 __fastcall sub_7FF6870F3C80(__int64 p__input,__int64 input_,__int64 len_input,_BYTE *key_,unsigned __int64 len_key)
{
v38 = -2i64;
input_a = input_;
p__inputa = p__input;
p__input_1 = p__input;
LOBYTE(p__input) = 0;
LOBYTE(input_) = -1;
v5 = sub_7FF6870F5120(p__input, input_);
v34 = v5;
v35 = BYTE2(v5);
v25 = BYTE2(v5);
v24 = v5;
v37 = BYTE2(v5);
v36 = v5;
sub_7FF6870F5620(v23, (BYTE2(v5) << 16) | (unsigned int)(unsigned __int16)v5);
index_4 = 0;
v27[0] = sub_7FF6870F6470(0i64, 256i64);
v27[1] = v6;
while ( 1 )
{
v28 = sub_7FF6870F5440(v27);
index = index_1;
if ( !v28 )
break;
index_2 = index;
if ( !len_key )
core::panicking::panic_const::panic_const_sub_overflow::h1caaed214377046f(&off_7FF687111B70);// "rust.rs"
v15 = key_[index % len_key] ^ 0x66;
index_5 = index_4;
_rust.rs_ = *(unsigned __int8 *)sub_7FF6870F66C0(v23, index, &off_7FF687111B88);// "rust.rs"
v14 = _rust.rs_ + index_5;
if ( __CFADD__(_rust.rs_, index_5) )
core::panicking::panic_const::panic_const_rem_by_zero::h79a295546191bac3(&off_7FF687111BA0);// "rust.rs"
if ( __CFADD__(v15, v14) )
core::panicking::panic_const::panic_const_rem_by_zero::h79a295546191bac3(&off_7FF687111BB8);// "rust.rs"
index_4 = (unsigned __int8)(v15 + v14);
v12 = sub_7FF6870F6680((__int64)v23);
sub_7FF6870F57D0(v12, index_3, index_2, index_4, (__int64)&off_7FF687111BD0);// "rust.rs"
}
v30 = 0;
v31 = 0;
v8 = sub_7FF6870F57C0(input_a, len_input);
v33[0] = (__int64)&v30;
v33[1] = (__int64)&v31;
v33[2] = (__int64)v23;
sub_7FF6870F2200(v32, v8, v9, v33);
sub_7FF6870F5680(p__inputa);
sub_7FF6870F1AB0((__int64)v23);
return p__input_1;
}

动态调试过程中发现key被异或0x66后生成的v15是一个RC4的S盒,但是没找到PRGA部分,给整个S盒打上硬件断点,找到一个函数:

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
char __fastcall sub_7FF6870F3F40(unsigned __int16 **a1, char *a2)
{
__int16 v2; // ax
__int16 _rust.rs_; // cx
__int64 v4; // rax
unsigned __int64 index; // rdx
__int16 _rust.rs__2; // cx
_BYTE *_rust.rs__3; // rax
unsigned __int16 *v9; // [rsp+28h] [rbp-20h]
__int16 _rust.rs__1; // [rsp+30h] [rbp-18h]
__int16 v11; // [rsp+34h] [rbp-14h]
char v13; // [rsp+45h] [rbp-3h]

v13 = *a2;
v2 = **a1;
if ( __CFADD__(v2, 1) )
core::panicking::panic_const::panic_const_rem_by_zero::h79a295546191bac3(&off_7FF687111BE8);// "rust.rs"
**a1 = (unsigned __int8)(v2 + 1);
v11 = *a1[1];
_rust.rs_ = *(unsigned __int8 *)sub_7FF6870F66C0((__int64)a1[2], **a1, (__int64)&off_7FF687111C00);// "rust.rs"
if ( __CFADD__(_rust.rs_, v11) )
core::panicking::panic_const::panic_const_rem_by_zero::h79a295546191bac3(&off_7FF687111C18);// "rust.rs"
*a1[1] = (unsigned __int8)(_rust.rs_ + v11);
v4 = sub_7FF6870F6680((__int64)a1[2]);
sub_7FF6870F57D0(v4, index, **a1, *a1[1], (__int64)&off_7FF687111C30);// "rust.rs"
v9 = a1[2];
_rust.rs__1 = *(unsigned __int8 *)sub_7FF6870F66C0((__int64)v9, **a1, (__int64)&off_7FF687111C48);// "rust.rs"
_rust.rs__2 = *(unsigned __int8 *)sub_7FF6870F66C0((__int64)a1[2], *a1[1], (__int64)&off_7FF687111C60);// "rust.rs"
if ( __CFADD__(_rust.rs__2, _rust.rs__1) )
core::panicking::panic_const::panic_const_rem_by_zero::h79a295546191bac3(&off_7FF687111C78);// "rust.rs"
_rust.rs__3 = (_BYTE *)sub_7FF6870F66C0(
(__int64)v9,
(unsigned __int8)(_rust.rs__2 + _rust.rs__1),
(__int64)&off_7FF687111C90);// "rust.rs"
return ((((*_rust.rs__3 >> 4) | (16 * *_rust.rs__3)) + 1) ^ v13) + 1;
}

发现这是RC4的PRGA部分,其中v13是明文,前面那串则是密钥流,编写同构加密脚本:

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
def KSA(key):
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) & 0xFF
S[i], S[j] = S[j], S[i]
return S

def PRGA(S, data):
i = j = 0
result = []
for byte in data:
i = (i + 1) & 0xFF
j = (j + S[i]) & 0xFF
S[i], S[j] = S[j], S[i]
k = S[(S[i] + S[j]) & 0xFF]
K = ((k >> 4) | (k << 4)) & 0xFF
res = ((byte ^ (K + 1)) + 1) & 0xFF
result.append(res)
return bytes(result)

m = bytearray(b"flag{1234567890123}")
key = bytearray(b"lntfvpus")
for i in range(len(key)):
key[i] ^= i
key[i] ^= 0x66

sbox = KSA(key)
m = PRGA(sbox, m)
print(m)

测试发现加密结果相同,编写脚本解密:

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
def KSA(key):
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) & 0xFF
S[i], S[j] = S[j], S[i]
return S

def PRGA(S, data):
i = j = 0
result = []
for byte in data:
i = (i + 1) & 0xFF
j = (j + S[i]) & 0xFF
S[i], S[j] = S[j], S[i]
k = S[(S[i] + S[j]) & 0xFF]
K = ((k >> 4) | (k << 4)) & 0xFF
res = ((byte ^ (K + 1)) + 1) & 0xFF
result.append(res)
return bytes(result)

def de_PRGA(S, data):
i = j = 0
result = []
for byte in data:
i = (i + 1) & 0xFF
j = (j + S[i]) & 0xFF
S[i], S[j] = S[j], S[i]
k = S[(S[i] + S[j]) & 0xFF]
K = ((k >> 4) | (k << 4)) & 0xFF
res = ((byte - 1) ^ (K + 1)) & 0xFF
result.append(res)
return bytes(result)

m = bytearray(b"flag{1234567890123}")
key = bytearray(b"lntfvpus")
for i in range(len(key)):
key[i] ^= i
key[i] ^= 0x66

sbox = KSA(key)
m = PRGA(sbox.copy(), m)
print(m)
m = de_PRGA(sbox.copy(), m)
print(m)

encflag = bytearray.fromhex("2905130CE7A5D2A2A43A3A5ABB239CE4D702BF")
decflag = de_PRGA(sbox.copy(), encflag)
print("flag{" + decflag.decode() + "}")

flag{Y0uKn0wRu5tV@ryW@1l}.

vm:

查看main:

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
int __fastcall main_0(int argc, const char **argv, const char **envp)
{
__int64 v4; // [rsp+28h] [rbp+8h]

j___CheckForDebuggerJustMyCode(&unk_7FF7685F2014, argv, envp);
printf("input sth...\n");
v4 = initialize();
if ( (unsigned int)vm(v4) )
printf("Noooo\n");
else
printf("Ez right?! flag is flag{md5(your_input)}\n");
return 0;
}
查看vm函数:
int __fastcall vm_0(unsigned int *a1, __int64 a2, __int64 a3)
{
j___CheckForDebuggerJustMyCode(&unk_7FF7685F2014, a2, a3);
while ( 1 )
{
switch ( *((_BYTE *)a1 + a1[2] + 268) )
{
case 0x10:
*a1 = *((char *)a1 + a1[2] + 269);
a1[2] += 2;
break;
case 0x11:
a1[1] = *((char *)a1 + a1[2] + 269);
a1[2] += 2;
break;
case 0x20:
*((_BYTE *)a1 + *((char *)a1 + a1[2] + 269) + 12) = *((_BYTE *)a1 + a1[2] + 270);
a1[2] += 3;
break;
case 0x30:
*((_BYTE *)a1 + *a1 + 12) ^= *((_BYTE *)a1 + 4);
++a1[2];
break;
case 0x40:
sub_7FF7685E109B("%7s", (const char *)a1 + 12);
++a1[2];
break;
case 0x50:
return j_memcmp(a1 + 3, (char *)a1 + *((char *)a1 + a1[2] + 269) + 12, *((char *)a1 + a1[2] + 270));
default:
continue;
}
}
}

发现一共有0x10、0x11、0x20、0x30、0x40、0x50这六种字节码,其中0x50的是最终结束时的比较,读取字节码的指针是a1+a1[2]+268,那么后续的269、270等就是参数的指针,0x10和0x11是把参数加载到寄存器0或寄存器1,0x20是把第二个参数写入第一个参数对应的“内存”偏移,0x30是一个简单的异或,对内存中对应偏移的值异或a1[1]的值,0x40貌似是某种加载,动态调试取得vm字节码:

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
opc = [0x20, 0x10, 0x5B, 
0x20, 0x11, 0x58,
0x20, 0x12, 0x56,
0x20, 0x13, 0x6E,
0x20, 0x14, 0x11,
0x20, 0x15, 0x4E,
0x20, 0x16, 0x00,
0x40,
0x11, 0x21,
0x10, 0x00,
0x30,
0x10, 0x01,
0x30,
0x10, 0x02,
0x30,
0x10, 0x03,
0x30,
0x10, 0x04,
0x30,
0x10, 0x05,
0x30,
0x10, 0x06,
0x30,
0x10, 0x07,
0x30,
0x50, 0x10, 0x07]

发现实际上就是一个7位明文的异或,先加载了值为[0x5B, 0x58, 0x56, 0x6E, 0x11, 0x4E, 0x00]的异或密文,然后0x40读取7位输入,接下来加载了0x21的异或值到a1[1]并对这7位输入进行异或后与密文比较,解密得到明文为zywO0o!flag{ccd5cace2d47f2fc8b3a7c632f5f7b49}.