前言

也是赶上ddl了)第二天要交wp今天才开始写,不过还是会努力写详细一点回忆一下心路历程的
突然发现我怎么有四五题都没上)
我的出题源码会放在:https://github.com/Samsara-lo/Geek2025/tree/main

Reverse

Week1

ez_pyyy

下载下来是pyc文件,本意是学习一下pyc文件内容、反编译

pyc反编译工具:(推荐使用pycdc,可以去了解一下这几个的区别,不过其实都有一定的局限性)

反编译出来的内容

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
cipher=[0x30,0x37,0x39,0x32,0x35,0x37,0x35,0x32,0x34,0x32,0x30,0x37,0x65,0x34,0x35,0x32,0x34,0x32,0x34,0x32,0x30,0x37,0x35,0x37,0x37,0x37,0x32,0x36,0x35,0x37,0x36,0x37,0x37,0x37,0x35,0x36,0x62,0x37,0x61,0x36,0x32,0x35,0x38,0x34,0x32,0x34,0x63,0x36,0x32,0x32,0x34,0x32,0x32,0x36]

def str_to_hex_bytes(s: str) -> bytes:
return s.encode("utf-8")

def enc(data: bytes, key: int) -> bytes:
return bytes([b ^ key for b in data])

def en3(b: int) -> int:
# 循环左移4位,相当于高低半字节交换
return ((b << 4) & 0xF0) | ((b >> 4) & 0x0F)

def en33(data: bytes, n: int) -> bytes:
"""整体 bitstream 循环左移 n 位"""
bit_len = len(data) * 8
n = n % bit_len
# 转成整数
val = int.from_bytes(data, "big")
val = ((val << n) | (val >> (bit_len - n))) & ((1 << bit_len) - 1)
return val.to_bytes(len(data), "big")


if __name__ == "__main__":
flag =""
data = str_to_hex_bytes(flag)

# 2. xor 0x11
data = enc(data, 0x11)

# 3. 循环左移4位(对每字节)
data = bytes([en3(b) for b in data])

# 4. reverse
data = data[::-1]

# 5. 整体循环左移32位
data = en33(data, 32)

# 6. tohex
if data.hex() == cipher:
print("Correct! ")
else:
print("Wrong!!!!!!!!")

每一步反着改回去即可:

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
cipher = [48, 55, 57, 50, 53, 55, 53, 50, 52, 50, 48, 55, 101, 52, 53, 50, 52, 50, 52, 50, 48, 55, 53, 55, 55, 55, 50, 54, 53, 55, 54, 55, 55, 55, 53, 54, 98, 55, 97, 54, 50, 53, 56, 52, 50, 52, 99, 54, 50, 50, 52, 50, 50, 54]

def enc(data: bytes, key: int) -> bytes:
return bytes([b ^ key for b in data])

def en3(b: int) -> int:
return b << 4 & 240 | b >> 4 & 15

def en33(data: bytes, n: int) -> bytes:
bit_len = len(data) * 8
n = n % bit_len
val = int.from_bytes(data, 'big')
val = (val >> n | val << bit_len - n) & (1 << bit_len) - 1
return val.to_bytes(len(data), 'big')

if __name__ == '__main__':
data = bytes.fromhex(bytes(cipher).decode())
data = en33(data, 32)
data = data[::-1]
data = bytes([en3(b) for b in data])
data = enc(data, 17)
print(data)
# SYC{jtfgdsfda554_a54d8as53}

only_flower

source code

第一周似乎有挺多选手哭这道的?但是其实只有一种花

花指令实则就是垃圾代码,它不会影响程序运行但是会影响IDA反编译

可以了解一下IDA递归下降反编译&花指令原理

因为不影响执行,所以花指令动调一下就直接可以跳过,把跳过的字节码nop调就可以。我们讲静态:

实现原理:

1
2
3
__asm__ __volatile__ (
".byte 0xEB,0xFF,0xC0,0x48;"
);

0xEB JMP rel8(短跳转指令)
0xFF 作为 JMP 的 跳转偏移(-1),也会被当作下一条指令的 opcode 导致错位
0xC0 根据上下文会被解析成各种非法或奇怪指令
0x48 REX.W 前缀(x86_64),单独出现会让反汇编器误判接下来的结构

IDA里反编译的效果:

1
2
3
4
5
6
.text:0040161F                               loc_40161F:                             ; CODE XREF: _main:loc_40161F↑j
.text:0040161F EB FF jmp short near ptr loc_40161F+1
.text:0040161F _main endp
.text:0040161F
.text:0040161F ; ---------------------------------------------------------------------------
.text:00401621 C0 48 A1 db 0C0h, 48h, 0A1h

首先你需要打开字节码:
IDA-Options-General-Numbers of Opcode这里改成5或者10

EB FF就是字节码

jmp short near ptr loc_40161F+1的意思是跳到loc_40161F往后偏移 +1 字节的地址,所以是跳过EB执行FF,也就是跳过JMP指令,首先在0x040161F的位置按U取消定义,把db 0EBh nop掉(不懂nop去搜,可以下一个keypatch)。

下面的也这么去就好了。

去完花就可以看到清晰的逻辑写exp了

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
# decrypt_onlyflower.py
CIPHER = [
0x0A,0x84,0xC2,0x84,0x51,0x48,0x5F,0xF2,0x9E,0x8D,0xD0,0x84,0x75,0x67,0x73,0x8F,
0xCA,0x57,0xD7,0xE6,0x14,0x6E,0x77,0xE2,0x29,0xFE,0xDF,0xCC
]
KEY = b"GEEK2025"

def ror8(v, n):
n &= 7
return ((v >> n) | ((v << (8 - n)) & 0xFF)) & 0xFF

def decrypt(cipher, key):
klen = len(key)
out = bytearray(len(cipher))
for i, y in enumerate(cipher):
k = key[i % klen]
r = (y - (i & 0xFF)) & 0xFF
x = ror8(r, k & 7)
b = x ^ k
out[i] = b
return bytes(out)

if __name__ == "__main__":
flag = decrypt(CIPHER, KEY)
print(flag.decode('utf-8'))
# SYC{asdjjasdhjk12wk12ijkejk}

ezRu3t

source code: https://github.com/Samsara-lo/Geek2025/blob/main/ezRu3t/main.rs

shift+F12找到类似Base85和Base64码表的字符串,本意其实不是rust代码审计要求全学一遍,因为校内的进度Base家族不会的话要挨骂了。

密文为AA;XAM?,_@;T[r@7E779h8;s‘`pt=>3c6ASuHFASOtP<Gkf_A4&gPAl1]S

直接base85+base64解密

SYC{Ohjhhh_y0u_g3t_Ezzzzz3_Ru3t!@}

ezSMC

source code: https://github.com/Samsara-lo/Geek2025/tree/main/ezSMC

ida 打开审计代码,联系题目很容易发现.miao段代码是被SMC加密处理过的

两种解法:1.动调过掉SMC自解密;2.写ida python代码解密

第二种解法可以看这个师傅的,写的很详细:https://fairy-10124.github.io/2025/11/28/Geek%202025%EF%BC%88reverse%E6%96%B9%E5%90%91%EF%BC%89/

程序主函数:

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
_main(*(_QWORD *)&argc, argv, envp);
Str2 = "tHMoSoMX71sm62ARQ8aHF6i88nhkH9Ac2J7CrkQsQgXpiy6efoC8YVkzZu1tMyFxCLbbqvgXZHxtwK5TACVhPi1EE5mK6JG56wPNR4d2GmkELGfJHgtcAEH7";
printf("Plz input your flag miao: ");
Stream = __acrt_iob_func(0);
fgets(Buffer, 1024, Stream);
Buffer[strcspn(Buffer, "\r\n")] = 0;
hex = (char *)ascii_to_hexbytes(Buffer, &outlen);
data = hexstr_to_bytes(hex, &len);
key = 17;
init(&ctx, &key, 1);
encode(&ctx, data, len);
Str = bytes_to_hexstr(data, len);
miao_encrypt();
len_1 = strlen(Str);
data_1 = (uint8_t *)encodee((const uint8_t *)Str, len_1);
if ( data_1 )
{
len_2 = strlen((const char *)data_1);
Str1 = enc0de(data_1, len_2);
if ( !strcmp(Str1, Str2) )
puts("Correct!");
else
puts("Wrong!");
free(hex);
free(data);
free(Str);
free(data_1);
free(Str1);
return 0;
}
else
{
puts("encodee returned NULL");
return 0;
}
}

被SMC的函数是一个base64,加密顺序为密钥为单字节0x11的RC4→转hex→base64→使用自定义表的base58,直接解密即可
SYC{OHhhhhhhh_y0u_Kn0m_SMCCCC@!}

Week2

Mission Ghost Signal

RE部分有两个思路:

  • 程序给了解密函数,直接patch
  • 同构AES,发现魔改自实现的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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
int __cdecl main(int argc, const char **argv, const char **envp)
{
_BYTE v4[32]; // [esp+1Ch] [ebp-20Ch] BYREF
char v5[192]; // [esp+3Ch] [ebp-1ECh] BYREF
char Buffer[256]; // [esp+FCh] [ebp-12Ch] BYREF
_BYTE v7[16]; // [esp+1FCh] [ebp-2Ch] BYREF
int n7; // [esp+20Ch] [ebp-1Ch]
int j; // [esp+210h] [ebp-18h]
char v10; // [esp+217h] [ebp-11h]
int i; // [esp+218h] [ebp-10h]
size_t n25_1; // [esp+21Ch] [ebp-Ch]

sub_403500();
SetConsoleOutputCP(0xFDE9u);
SetConsoleCP(0xFDE9u);
qmemcpy(v7, "1145141145144332", sizeof(v7));
puts("密码验证系统");
printf("请输入压缩包密码: ");
if ( fgets(Buffer, 256, (FILE *)iob[0]._ptr) )
{
n25_1 = strlen(Buffer);
if ( n25_1 && Buffer[n25_1 - 1] == 10 )
Buffer[--n25_1] = 0;
if ( n25_1 == 25 )
{
sub_401C08(v5, p_Syclover2025Geek, v7); // "Syclover2025Geek"
memcpy(v4, Buffer, 0x19u);
n7 = 7;
for ( i = 25; i <= 31; ++i )
v4[i] = n7;
AES_CBC_Modified(v5, v4, 0x20u);
v10 = 1;
for ( j = 0; j <= 31; ++j )
{
if ( byte_406020[j] != v4[j] )
{
v10 = 0;
break;
}
}
if ( v10 )
puts("恭喜!这是正确的压缩包密码。");
else
puts("特工,你失败了!密码不正确。");
return 0;
}
else
{
puts("特工,你失败了!密码长度不正确。");
return 1;
}
}
else
{
puts("输入错误!");
return 1;
}
}

其中AES是一个自实现的AES,但程序中给出了解密函数,patch加密为解密即可

1
2
3
.text:00402F59                 call    AES_CBC_Modified ; 0x402B57

.text:00402F59 call AES_CBC_Modified_Dec ; 0x402BE8

密码为We_ve_Trapped_in_The_Sink,解压后得到一个sstv,读取得到一张含有二维码的图片,扫描后得到一个下载链接https://wwnr.lanzoum.com/iIjAG39n7mlg,下载后是一个含摩斯电码的音频,识别得到55316C44657A646F4D5456664D564E664E463835636A52755246396A4D4534316344467951474E5A4C6E303D,转hex得到U1lDezdoMTVfMVNfNF85cjRuRF9jME41cDFyQGNZLn0=,解base64得到flag

SYC{7h15_1S_4_9r4nD_c0N5p1r@cY.}

Week4

国产の光

其实没什么难度不是week4的题,只要配好鸿蒙反编译环境就行。
密文yaApcJ5GoyGwhARDXZLQUdntqPpmVu2GuTChnsLoj5d8ABinwGSsgpGaiPWYbHTTbbzSXxLXwoLgjR1YgquyEnK,自动识别base58,外层密钥welcometosyc2025,iv为helloimsamsaramiao,内层AESCBC,直接解密即可得到flag

**SYC{HarmonyOS_1s_right?right!}**

obfuscat3

source code:https://github.com/Samsara-lo/Geek2025/blob/main/obfuscat3/obfuscat3.cpp
忘记把256处理一下了成简单题了,我的小巧思没了TT
可以去学习一下ollvm,这题的主要考点是指令替换+瞪眼法,被瞪的太容易了,很容易发现是一个把最后异或魔改成+的RC4

函数里又调用了init_encode和exchange_encode

具体审核分析代码看起来是有个魔改后又拆分加了混淆的RC4,进行了自定义的S盒流密码变体,其中init_encode函数实现了KSA的功能,exchange_encode函数实现了swap的功能,而obf_encode应该是实现了PRGA的功能,RC4算法的魔改点是把最后char ^ S[(S[i] + S[j]) % 256]​中的异或运算改成了 + ,所以解密的时候只要改成 - 就行了

具体的GAMBA表达式可以看源码,是源代码混淆的基础上套了两层标准ollvm的指令替换

然后就可以写解密代码了

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
cipher = [
0xB4, 0xCD, 0x69, 0x54, 0xBD, 0x67, 0x20, 0x9D, 0xF2, 0xC3,
0x24, 0x14, 0xC2, 0x1B, 0xE9, 0x6A, 0x44, 0x14, 0x4E, 0x39,
0xC5, 0xC8, 0x5B, 0x11, 0x75, 0xAD, 0xDE, 0xBB, 0xFE, 0xE4,
0x6E, 0x65, 0x06, 0x9A, 0x91, 0xFE, 0xA0, 0x68, 0xA4, 0x86,
0x17, 0x6C, 0x0A, 0xCF, 0x1E, 0x67, 0xE3, 0x0D, 0x60, 0x47,
0x13, 0x6B, 0xD1, 0x36, 0xF2, 0x77, 0x58, 0x76, 0x1E, 0x98,
0xF5, 0x7F, 0x0A, 0x92, 0xB7, 0x0A, 0xEA, 0xAE, 0x46, 0x7E,
0x6A, 0x18, 0x4A, 0x59, 0x4E, 0x71, 0xB2, 0xE1, 0x41, 0x7A,
0x0B, 0x31, 0xBA, 0xC6, 0xAA, 0xCF, 0xCE, 0x09, 0xBF, 0x2E,
0xF8, 0x4D, 0x75, 0xEF, 0x14, 0xED, 0x5F, 0x66, 0x44, 0x6F,
0xDE, 0xE2, 0x7C, 0x10, 0x8C, 0xB7, 0x4E, 0x6B, 0xB2, 0xD4,
0xF6, 0x91, 0xD7, 0x84, 0x86, 0x1F, 0xF8, 0x65, 0x94, 0x0B,
0x14, 0x28, 0xFB, 0xDD, 0x47, 0xF4, 0xC1, 0x17, 0x42, 0x3F,
0x1E, 0x38, 0x07, 0xBB, 0x37, 0x33, 0x12, 0x0C, 0x16, 0x68,
0xE0, 0x23, 0x12, 0x75, 0x72, 0xD9, 0x71, 0x7A, 0x88, 0xD0,
0x46, 0x28, 0x88, 0xAD, 0x1E, 0x98, 0x8F, 0x92, 0x7E, 0x0E,
0x69, 0x29, 0x37, 0xB1, 0xFF, 0xC5, 0xAF, 0x6F, 0x41, 0x37,
0x65, 0x0E, 0xD2, 0x62, 0x11, 0x8F, 0xA6, 0x3E, 0x95, 0xF5,
0x80, 0x9A, 0xDC
]

key = b"Samsara"

def rc4_keystream(length, key_bytes):
S = list(range(256))
j = 0
# KSA
for i in range(256):
j = (j + S[i] + key_bytes[i % len(key_bytes)]) & 0xFF
S[i], S[j] = S[j], S[i]
# PRGA
i = 0
j = 0
out = []
for _ in range(length):
i = (i + 1) & 0xFF
j = (j + S[i]) & 0xFF
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) & 0xFF]
out.append(K)
return out

ks = rc4_keystream(len(cipher), list(key))
plain = bytes((c - ks[i]) & 0xFF for i, c in enumerate(cipher))
print(plain.decode('ascii'))

#SYC{Alright_I_sti1l_h0pe_th3t_you_solved_the_chall3nge_by_deobfuscating_them_Geek_is_just_the_first_step_of_your_CTF_journey_Im_glad_I_could_be_part_of_your_growth_Good_luck_for_y0u!}

obfuscat3_revenge

source code:https://github.com/Samsara-lo/Geek2025/blob/main/obfuscat3_revenge/en_obfuscat3_revenge.cpp
主要考点是指令替换GAMBA+逆向

主函数:

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
v11 = 0;
*(_QWORD *)key = 0x8877665540302010LL;
*(_QWORD *)&key[8] = 0x2301EFDECDBCAB90LL;
gen(out1, out2, key);
memset(s, 0, 0x24u);
printf("Input the flag: ");
if ( fgets(s, 36, stdin) )
{
n = strcspn(s, "\r\n");
s[n] = 0;
src = s;
if ( n >= 3
&& *(unsigned __int8 *)src == 0xEF
&& *((unsigned __int8 *)src + 1) == 0xBB
&& *((unsigned __int8 *)src + 2) == 0xBF )
{
src = (char *)src + 3;
n -= 3LL;
}
if ( n && n <= 0x20 )
{
memset(inp, 0, sizeof(inp));
memcpy(inp, src, n);
real_en(inp, key, out1, out2);
if ( !memcmp(inp, main::target_enc, 0x20u) )
printf("Congratulations!\n");
else
printf("Try again?\n");
return 0;
}
else
{
printf("Try again?\n");
return 0;
}
}
else
{
printf("Input error.\n");
return 1;
}
}

查看gen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
__int64 __fastcall gen(unsigned __int8 *p_out1, unsigned __int8 *p_out2, const unsigned __int8 *p_key)
{
for ( i = 0; i < 256; ++i )
{
p_out1[i] = i;
}
v8 = 0;
for ( j = 0; j < 256; ++j )
{
v4 = p_out1[j] + v8;
v8 = p_key[j % 16] + v4;
v6 = p_out1[j];
p_out1[j] = p_out1[v8];
p_out1[v8] = v6;
}
for ( k = 0; k < 256; ++k )
{
p_out2[p_out1[k]] = k;
}
}

同构gen:

1
2
3
4
5
6
7
def gen(sbox1, sbox2, key):
for i in range(256): sbox1[i] = i
t = 0
for j in range(256):
t = (t + key[j % 16] + sbox1[j]) & 0xFF
sbox1[j], sbox1[t] = sbox1[t], sbox1[j]
for k in range(256): sbox2[sbox1[k]] = k

用d810和hrtng反混淆加密函数:

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
unsigned __int8 *__fastcall real_en(
unsigned __int8 *inp,
unsigned __int8 *key,
unsigned __int8 *sbox1,
const unsigned __int8 *sbox2)
{
_inp = inp;
_key = key;
_sbox1 = sbox1;
_sbox2 = sbox2;
en1(inp, key, 32);
for ( i = 0; i < 32; ++i )
*((_BYTE *)_inp + i) = _sbox1[*((unsigned __int8 *)_inp + i)];
for ( j = 0; j < 32; ++j )
*((_BYTE *)_inp_1 + j) = *((_BYTE *)_inp + (unsigned __int8)perm[j]);
__inp = _inp;
*(_QWORD *)_inp = _inp_1[0];
__inp[1] = _inp_1[1];
__inp[2] = _inp_1[2];
__inp[3] = _inp_1[3];
___inp[7] = *_inp;
___inp[6] = _inp[1];
___inp[5] = _inp[2];
___inp[4] = _inp[3];
___inp[3] = _inp[4];
___inp[2] = _inp[5];
___inp[1] = _inp[6];
___inp[0] = _inp[7];
for ( n4 = 0; n4 < 4; ++n4 )
{
v11 = *(_DWORD *)&_key[4 * (n4 % 4)];
v7 = ___inp[7];
___inp[7] = ___inp[6];
___inp[6] = F(___inp[6], v11, _sbox1) ^ v7;
v5 = F(___inp[4], v11, _sbox1);
v10 = ___inp[5] ^ v5;
___inp[5] = ___inp[4];
___inp[4] = v10;
v9 = ___inp[3] ^ F(___inp[2], v11, _sbox1);
___inp[3] = ___inp[2];
___inp[2] = v9;
v8 = F(___inp[0], v11, _sbox1) ^ ___inp[1];
___inp[1] = ___inp[0];
___inp[0] = v8;
}
*_inp = ___inp[7];
_inp[1] = ___inp[6];
_inp[2] = ___inp[5];
_inp[3] = ___inp[4];
_inp[4] = ___inp[3];
_inp[5] = ___inp[2];
_inp[6] = ___inp[1];
p_s1a_2 = (unsigned __int8 *)_inp;
_inp[7] = ___inp[0];
return p_s1a_2;
}

查看en1:

1
2
3
4
5
6
7
8
9
10
11
__int64 __fastcall en1(unsigned __int8 *inp, const unsigned __int8 *key, int n32)
{
for ( n32_1 = 0; ; ++n32_1 )
{
n32_2 = (unsigned int)n32_1;
if ( n32_1 >= n32 )
break;
inp[n32_1] = ~((key[n32_1 % 16] | ~inp[n32_1]) & ~(key[n32_1 % 16] & ~inp[n32_1]));
}
return n32_2;
}

是一个循环异或,之后是S盒1替换、顺序替换、倒排、4轮函数、再次倒排,同构:

1
2
3
4
5
6
7
8
9
10
11
for i in range(32): inp[i] ^= key[i%16]
for i in range(32): inp[i] = sbox1[inp[i]]
mid = bytearray(32)
for i in range(32): mid[i] = inp[perm[i]]
dmid = b2nle(mid, 4)
dmid = dmid[::-1]
for i in range(4):
for j in range(0, 8, 2):
dmid[j], dmid[j+1] = dmid[j+1] ^ F(dmid[j], dkey[i], sbox1), dmid[j]
dmid = dmid[::-1]
res = n2ble(dmid, 4)

对于轮函数F:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
__int64 __fastcall F(int inp, unsigned int dkey, const unsigned __int8 *sbox)
{
v8 = sbox[((inp ^ dkey) >> 0x00) & 0xFF];
v7 = sbox[((inp ^ dkey) >> 0x08) & 0xFF];
v6 = sbox[((inp ^ dkey) >> 0x10) & 0xFF];
v5 = sbox[((inp ^ dkey) >> 0x18) & 0xFF];
return not_en((v5 << 0x18) | (v6 << 0x10) | (v7 << 0x08) | (v8 << 0x00), 5);
}

__int64 __fastcall not_en(unsigned int a1, char n5)
{
v2 = a1 << n5;
v3 = a1 >> (32 - n5);
return v2 | v3;
}

是一个异或密钥后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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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 n2ble(na, n):
b = bytearray()
for a in na: b.extend(a.to_bytes(n, byteorder='little'))
return b

key = bytearray.fromhex("102030405566778890ABBCCDDEEF0123")
dkey = b2nle(key, 4)
perm = bytearray.fromhex("1F000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E")
sbox = bytearray(256)
inv_sbox = bytearray(256)
enc = bytearray.fromhex("CA5A96FF084972393618138A14C00C78F87C49C7BEE891ED7FB002AD7774D434")
inp = bytearray(b"12345678901234567890123456789012")

def rol(b, shift):
shift &= 31
return ((b << shift) | (b >> (32-shift))) & 0xFFFFFFFF

def gen(sbox, inv_sbox, key):
for i in range(256): sbox[i] = i
t = 0
for j in range(256):
t = (t + key[j % 16] + sbox[j]) & 0xFF
sbox[j], sbox[t] = sbox[t], sbox[j]
for k in range(256): inv_sbox[sbox[k]] = k

def F(dm, dk, sbox):
dm ^= dk
bm = n2ble([dm], 4)
for i in range(4): bm[i] = sbox[bm[i]]
dm = b2nle(bm, 4)[0]
return rol(dm, 5)

gen(sbox, inv_sbox, key)
for i in range(32): inp[i] ^= key[i%16]
for i in range(32): inp[i] = sbox[inp[i]]
inp = inp[-1:] + inp[:-1]
dinp = b2nle(inp, 4)[::-1]
for i in range(4):
for j in range(6, -2, -2):
dinp[j], dinp[j+1] = dinp[j+1] ^ F(dinp[j], dkey[i], sbox), dinp[j]
dinp = dinp[::-1]
res = n2ble(dinp, 4)
print(res.hex())

denc = b2nle(enc, 4)
denc = denc[::-1]
for i in range(3, -1, -1):
for j in range(0, 8, 2):
denc[j], denc[j+1] = denc[j+1], denc[j] ^ F(denc[j+1], dkey[i], sbox)
denc = denc[::-1]
benc = n2ble(denc, 4)
rec = benc[1:] + benc[:1]
for i in range(32): rec[i] = inv_sbox[rec[i]]
for i in range(32): rec[i] ^= key[i % 16]
print(rec)

SYC{Then_you_are_1mpressivse}

Misc

Bite off picture

解压缩包发现被加密了,根据提示在010中发现编码

一个翻过来的base64,反转一下解密就行werwerr,解压得到一张图片,用随波 逐流分析发现被修改了宽高,随波逐流直接自动更正,或者去学一下文件结构010改宽高

hidden

flag1其实是隐藏文字,把隐藏文字可见打开就可以
打开文件时不是正常打开,所以把后缀改成zip再寻找剩余flag,都在doc文件夹 里,其中flag3需要修复文件头,jpg文件缺失文件头FF D8,补上即可

gift

压缩包密码g1ft的base64在末尾
根据文件名判断是盲水印,直接用watermark提取
SYC{IT3_gift-f0r-you}

4ak5ra

随波逐流发现有个压缩包,用binwalk提取出来,解压出来是一张png图片,根据题目提示lsb,我们用Stegsolve或者zsteg提取flag
SYC{Im_waiting_for_Sakura_t0_become_a_top_pwn_master}

后记

嘻嘻有前记那也有后记,主要是后面再来博客完善一下wp吧讲讲知识点