极客大挑战2025逆向部分题解

就写了部分逆向题解,涉及到vm,安卓等,其他的有点懒就没写嘻嘻

stack bomb

直接找到主函数看汇编,这里的内容就是key=[1,2,3,4]

image-20251110214105180

这里就是密文的内容,不难理解,这一部分是在进行比对

enc=[0x9A8C0C4B, 0xC412FF1C,0xBFC3A488, 0xB16C8FD0,0x4136E319, 0x8835E4FF,0x118263A7, 0x7C85D629 ]

image-20251110214213233

这一块是类似vm的实现

image-20251110214350355

为了便于理解是如何进行函数调用的,我在每个return返回值都下了断点

调试时可以看到delta的值为0x9000000

image-20251110215027778

一下是我通过调试得到的内容,是标准的tea加密,只改了delta

image-20251110220523359

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
//push    offset loc_411307    return a2 + a1;
//push offset sub_41109B return a1 - a2;
//push offset sub_4113F2 return a2 ^ a1;
//push offset loc_411186 return a2 & a1;
//push offset sub_41115E return a1 << a2;
//push offset sub_411091 return a1 >> a2;
//push offset sub_4112D0 return a1 % a2;
//push offset sub_411163 return a2 * a1;
//push offset sub_411005 return a1 / a2;
//
//
//key[1,2,3,4];
//enc[0x9A8C0C4B, 0xC412FF1C,0xBFC3A488, 0xB16C8FD0,0x4136E319, 0x8835E4FF,0x118263A7, 0x7C85D629];
//STACK_0x250_=delat=0x9000000;

//STACK_0x250_=0x9000000;
//v71 = *STACK[0x26C]; -->v0
//v70 = *(STACK[0x26C] + 4);-->v1
//STACK[0x264] = 0; -->sum=0
//v67 = *DWORD1(STACK[0x26C]); -->k0
//v66 = *(DWORD1(STACK[0x26C]) + 4); -->k1
//v65 = *(DWORD1(STACK[0x26C]) + 8); -->k2
//v64 = *(DWORD1(STACK[0x26C]) + 12); -->k3
//
//
// for (int r = 0; r <= r_limit; ++r) {
// sum = F_2C0(delta, sum); +
//
// t0 = << (v70, 4); <<
// t1 = +1 (k0, t0); + 1
// t2 = + (sum, v70); +
// t3 = ^ (t2, t1); ^
//
// t4 = >> (v70, 5); >>
// t5 = +2 (k1, t4); + 2
// t6 = ^ (t3, t5); ^
//
// v70 = + (v71, t6); +
//
// t7 = << (v71, 4); <<
// t8 = +3 (k2, t7); + 3
// t9 = + (sum, v71); +
// t10 = ^ (t9, t8); ^
//
// t11 = >> (v71, 5); >>
// t12 = +4 (k3, t11); + 4
// t13 = ^ (t10, t12); ^
//
// v71 = + (v70, t13); +
// }

解密脚本:

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

#define NUM_BLOCKS 4
#define DELTA 0x9000000U

typedef uint32_t u32;

void tea_decrypt(u32 v[2], const u32 k[4]) {
u32 left = v[0];
u32 right = v[1];
u32 sum = DELTA * 32;

for (int i = 0; i < 32; ++i) {
right -= (((left << 4) + k[2]) ^ (left + sum)) ^ ((left >> 5) + k[3]);
left -= (((right << 4) + k[0]) ^ (right + sum)) ^ ((right >> 5) + k[1]);
sum -= DELTA;
}

v[0] = left;
v[1] = right;
}

int main(void) {
const u32 key[4] = {1, 2, 3, 4};
u32 ciphertext[NUM_BLOCKS][2] = {
{0x9A8C0C4B, 0xC412FF1C},
{0xBFC3A488, 0xB16C8FD0},
{0x4136E319, 0x8835E4FF},
{0x118263A7, 0x7C85D629}
};

char plaintext[NUM_BLOCKS * 8 + 1] = {0};
char *out = plaintext;

for (int i = 0; i < NUM_BLOCKS; ++i) {
u32 block[2] = { ciphertext[i][0], ciphertext[i][1] };
tea_decrypt(block, key);

for (int j = 0; j < 4; ++j) *out++ = (block[0] >> (j * 8)) & 0xFF;
for (int j = 0; j < 4; ++j) *out++ = (block[1] >> (j * 8)) & 0xFF;
}

printf("%s\n", plaintext);
return 0;
}
//syc{QYQS_F1nD_Th3_@nswer_H3re~~}

国产の光

首先看的是java层,这里我通过以下命令编译

1
java -jar jadx-dev-all.jar "E:\下载的附件\Ha3m0ny\ets\modules.abc"

编译.abc文件,找到密文yaApcJ5GoyGwhARDXZLQUdntqPpmVu2GuTChnsLoj5d8ABinwGSsgpGaiPWYbHTTbbzSXxLXwoLgjR1YgquyEnK

往下找到key和iv

image-20251114225635698

在native层,ida反编译.so文件找到主要加密过程

这里是AES/CBC加密,加密后再使用base58变表加密

image-20251114230017314

AES加密没有魔改,我是通过搜索字符串找到的base58变表

image-20251114230105593rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz

不过要注意iv长度为16,所以iv为helloimsamsarami,去掉ao

image-20251114230534136

SYC{HarmonyOS_1s_right?right!}

obfuscat3

程序对加密部分做了混淆,分析出主要加密逻辑就可以obf_encode

image-20251114210336515

在init_encode处的这边很明显rc4的sbox的形式

image-20251114210432929

v5 = 0x617261736D6153LL;实际是密钥Samsara,

魔改的地方在这,而rc4标准是s[i] ^= mysterious_box……

1
s[i] += mysterious_box[(((~(~mysterious_box[v9] | ~mysterious_box[v10] | ~((mysterious_box[v9] & (mysterious_box[v9] ^ 0xC38D792B) | ~(mysterious_box[v9] | 0x3C7286D4)) ^ 0x579B53FD ^ (v7 | ~(mysterious_box[v10] | 0x3C7286D4)) ^ 0x579B53FD)) | (~mysterious_box[v9] | ~mysterious_box[v10]) ^ 0x354E9C1B ^ (mysterious_box[v9] & (mysterious_box[v9] ^ 0xC38D792B) | ~(mysterious_box[v9] | 0x3C7286D4)) ^ 0x579B53FD ^ (v7 | ~(mysterious_box[v10] | 0x3C7286D4)) ^ 0x579B53FD ^ 0xCAB163E4) - ((~mysterious_box[v10] | ~(mysterious_box[v9] ^ 0x7911131C ^ mysterious_box[v10] ^ 0x86EEECE3))+ 1)) % 256)];

然后对照着解出rc4,代码如下

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

def rc4_key(S, length):
i = 0
j = 0
out = []
for _ in range(length):
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) % 256]
out.append(K)
return out

S = rc4_init(key)
ks = rc4_key(S, len(cipher))

plain = ''.join(chr((c - k) & 0xFF) for c, k in zip(cipher, ks))

print(plain)


//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!}

ez_android

程序分为三段加密,java层可以看到三个函数,第一层就是des加密,其余两层在native层

image-20251217204408862


在raw里能找到密文内容

image-20251217202244750

flag1:43464244374534323530413841423536

flag2:C2726AD56FC754D1

flag3:AEBCDE9B24029CA7


第一部分解密:得到Th1s_1s_

image-20251217205604386

第二部分:

打开libwrapper.so看第二段函数发现真正的so文件被加密了,加密逻辑就是xor

image-20251217205854261

提取出encrypt.so并解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
START = 0x9498        # encrypted_so 起始地址
SIZE = 6848
KEY = 0xAA

data = bytearray(get_bytes(START, SIZE))

print("[+] Read bytes:", len(data))

for i in range(len(data)):
data[i] ^= KEY

out = "libreal.so"
with open(out, "wb") as f:
f.write(data)

print("[+] Dump & decrypt finished:", out)

得到了real.so文件之后再反编译看到第二层函数就是一个rc4加密

image-20251217210029721

解密得到:Th1s_1s_

image-20251217210146554

第三部分是魔改的des加密,它使用的是自定义的sbox,密钥生成过程也与标准不同

解密脚本:得到 ly_F1ag!

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
KEY = bytes([0x13, 0x34, 0x57, 0x79, 0x9B, 0xBC, 0xDF, 0xF1])
CIPHERTEXT = bytes([0xAE, 0xBC, 0xDE, 0x9B, 0x24, 0x02, 0x9C, 0xA7])

PC1 = [57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63,
55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4]
SHIFTS = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]
PC2 = [14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55,
30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32]
E = [32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18, 19, 20, 21, 20, 21,
22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1]
P = [16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4,
25]
IP = [58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32,
24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47,
39, 31, 23, 15, 7]
FP = [40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53,
21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1,
41, 9, 49, 17, 57, 25]

SBOX = [
[[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7], [0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8],
[4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0], [15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13]],
[[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10], [3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5],
[0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15], [13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9]],
[[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8], [13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1],
[13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7], [1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12]],
[[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15], [13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9],
[10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4], [3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14]],
[[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9], [14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6],
[4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14], [11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3]],
[[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11], [10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8],
[9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6], [4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13]],
[[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1], [13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6],
[1, 4, 11, 13, 12, 3, 7, 10, 15, 6, 8, 0, 5, 9, 14, 2], [6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12]],
[[0] * 16, [0] * 16, [0] * 16, [0] * 16]
]

def bytes_to_bits(data):
return [(byte >> i) & 1 for byte in data for i in range(7, -1, -1)]
def bits_to_bytes(bits):
return bytes(int(''.join(map(str, bits[i:i + 8])), 2) for i in range(0, len(bits), 8))
def permute(bits, table):
return [bits[i - 1] for i in table]
def xor_bits(a, b):
return [x ^ y for x, y in zip(a, b)]
def left_shift_32bit(a_bytes, n):
val = int.from_bytes(a_bytes, 'big')
val = ((val << n) | (val >> (32 - n))) & 0xFFFFFFFF
return val.to_bytes(4, 'big')

def generate_subkeys(key_bytes):
key_bits = bytes_to_bits(key_bytes)
pc1_bits = permute(key_bits, PC1)
s_bytes = bits_to_bytes(pc1_bits)

C = s_bytes[0:4]
D = s_bytes[3:7]

subkeys = []
for shift_val in SHIFTS:
C = left_shift_32bit(C, shift_val)
D = left_shift_32bit(D, shift_val)

cd_bytes = C[0:3] + D[0:4]

cd_bits = bytes_to_bits(cd_bytes)
subkeys.append(permute(cd_bits, PC2))

return subkeys


def feistel(R_bits, subkey_bits):

expanded_bits = permute(R_bits, E)

xored_bits = xor_bits(expanded_bits, subkey_bits)

sbox_output_bits = []
for i in range(8):
block = xored_bits[i * 6: (i + 1) * 6]
row = (block[0] << 1) | block[5]
col = (block[1] << 3) | (block[2] << 2) | (block[3] << 1) | block[4]

val = SBOX[i][row][col]

sbox_output_bits.extend(list(map(int, f'{val:04b}')))

return permute(sbox_output_bits, P)


def des_decrypt(ciphertext_bytes, key_bytes):

cipher_bits = bytes_to_bits(ciphertext_bytes)
subkeys = generate_subkeys(key_bytes)

ip_bits = permute(cipher_bits, IP)
L, R = ip_bits[:32], ip_bits[32:]

for i in range(16):
subkey = subkeys[15 - i]
L, R = R, xor_bits(L, feistel(R, subkey))

combined = R + L
return bits_to_bytes(permute(combined, FP))

if __name__ == "__main__":
flag_bytes = des_decrypt(CIPHERTEXT, KEY)

print(f"flag3: {flag_bytes.rstrip(b'\\x00').decode('utf-8', errors='ignore')}")


flag:SYC{Th1s_1s_Th1s_1s_ly_F1ag!}

Her

跟进主函数sub_4024A0

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
int sub_4024A0()
{
unsigned int Seed; // eax
char v2; // [esp+0h] [ebp-F0h]
char v3; // [esp+0h] [ebp-F0h]
char v4; // [esp+0h] [ebp-F0h]
char v5; // [esp+0h] [ebp-F0h]
size_t i; // [esp+D0h] [ebp-20h]
int v7; // [esp+DCh] [ebp-14h]
size_t i_1; // [esp+E8h] [ebp-8h]

sub_40123A(&unk_40D0A4);
Seed = sub_402760(0);
srand(Seed);
sub_401177();
sub_401087("Enter the flag: ", v2);
sub_401023("%127s", &Str);
i_1 = strlen(&Str);
if ( i_1 == ::i )
{
sub_4011B3();
sub_401087("\nVerifying encrypted flag...\n", v3);
v7 = 1;
for ( i = 0; i < i_1; ++i )
{
if ( byte_40B260[i] != byte_40B000[i] )
v7 = 0;
}
if ( v7 )
{
sub_401087("\n", v4);
sub_401087("YES!YES!", v5);
}
else
{
sub_401087("\nWrong\n", v4);
}
return 0;
}
else
{
sub_401087("\nWrong\n", v3);
return 1;
}
}

主要跟进sub_4011B3,查看sub_401D30函数内容

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
size_t sub_401D30()
{
size_t i_1; // eax
size_t i; // [esp+F4h] [ebp-14h]
size_t i_2; // [esp+100h] [ebp-8h]

i_2 = strlen(Str);
if ( sub_4010CD() )
{
for ( i = 0; ; ++i )
{
i_1 = i;
if ( i >= i_2 )
break;
rand();
byte_40B260[i] = sub_401177();
}
}
else
{
i_1 = 0;
if ( i_2 )
{
dword_40B1DC = 0;
BUG();
}
}
return i_1;
}

看BUG函数的汇编内容,这里是Windows SEH的异常

1
2
3
4
5
6
7
8
9
10
11
12
push offset sub_401181
push fs:0
mov fs:0, esp

push offset sub_401186
push fs:0
mov fs:0, esp

push offset sub_401091
push fs:0
mov fs:0, esp


Windows 的 SEH 是一个“栈式单向链表”,永远从 fs:[0] 指向的节点开始调用

UD2 = 必然触发 #UD 异常


调用链:sub_401091->sub_401186->sub_401181

第一段加密函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl sub_401EA0(_DWORD *a1, int a2, int a3)
{
char v3; // bl

if ( *a1 != 0xC000001D || dword_40B1DC >= strlen(Str) )
return 1;
byte_40B2E0[dword_40B1DC] = ++dword_40B1D8 ^ Str[dword_40B1DC] ^ 0xAA;
if ( sub_4010CD() )
{
rand();
v3 = sub_401177();
byte_40B260[dword_40B1D8 % strlen(Str)] = v3;
}
else
{
*(a3 + 184) += 2;
++dword_40B360;
}
return 0;
}

dword_40B1DC是索引的下标,相当于i;dword_40B1D8是加密的中间量key/counter;byte_40B2E0[]密文输出的缓冲区;dword_40B360是异常记数;

以上函数的核心加密是

1
2
3
byte_40B2E0[dword_40B1DC] =
++dword_40B1D8 ^ Str[dword_40B1DC] ^ 0xAA;

第二段加密函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl sub_402010(_DWORD *a1, int a2, int a3)
{
char v3; // bl

if ( *a1 != -1073741676 || dword_40B1DC >= strlen(Str) )
return 1;
byte_40B2E0[dword_40B1DC] += ++dword_40B1D8 % 0x100u + dword_40B1DC * dword_40B1DC;
if ( sub_4010CD() )
{
rand();
v3 = sub_401177();
byte_40B260[dword_40B1D8 % strlen(Str)] = v3;
}
else
{
*(a3 + 184) += 2;
++dword_40B360;
}
return 0;
}

核心加密:

1
2
3
4
byte_40B2E0[dword_40B1DC] +=
++dword_40B1D8 % 0x100u
+ dword_40B1DC * dword_40B1DC;

dword_40B1DC当前符号索引;dword_40B1D8是key;

第三段加密:

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
int __cdecl sub_402190(_DWORD *a1, int a2, int a3)
{
char v3; // bl
int v5; // [esp+E8h] [ebp-8h]

sub_40123A(&unk_40D0A4);
if ( *a1 != 0xC0000005 )
return 1;
v5 = dword_40B1DC;
if ( dword_40B1DC >= strlen(Str) )
return 1;
++dword_40B1D8;
if ( sub_4010CD() )
{
rand();
v3 = sub_401177();
byte_40B260[dword_40B1D8 % strlen(Str)] = v3;
}
else
{
byte_40B260[v5] = (byte_40B2E0[v5] >> 5) | (8 * byte_40B2E0[v5]);
*(a3 + 184) += 2;
++dword_40B360;
}
return 0;
}

加密逻辑已经清晰了,现在只需要找到key即dword_40B1D8,可以交叉查找一下调用,在这个反调试部分

image-20251218214121760

核心部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if ( v9 )
{
if ( v8 )
{
v7[0] = 0;
v8(v7);
v1 = sub_401177();
v9(v1);
sub_401177();
if ( !v7[0] )
dword_40B1D8 ^= 0x1A373u;
}
}

解密脚本:

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
cipher = [
0x2D,0x4F,0x69,0x3D,0x5F,0x01,0xBD,0x9F,
0xA4,0x6D,0x89,0xAE,0x2A,0xEA,0xD1,0x9C,
0x71,0x6D,0xE1,0x1E,0x38,0x7E,0x8C,0x0A,
0xCE,0x6B,0xE0,0xF7,0x36,0x72,0x99
]

def rol3(x):
return ((x << 3) | (x >> 5)) & 0xFF

def encrypt_char(v, i, cnt):
out = None

for step in (1, 2, 3, 2, 3, 3):
cnt += 1

if step == 1:
v = (cnt ^ v ^ 0xAA) & 0xFF
elif step == 2:
v = (v + (cnt % 256 + i * i)) & 0xFF
elif step == 3:
out = rol3(v)

cnt ^= 0x1A373

return out, cnt

def decrypt(cipher):
res = []
cnt = 0x1A373

for i, c in enumerate(cipher):
for x in range(256):
enc, new_cnt = encrypt_char(x, i, cnt)
if enc == c:
res.append(chr(x))
cnt = new_cnt
break
else:
res.append('?')
_, cnt = encrypt_char(ord('A'), i, cnt)

return "".join(res)

print(decrypt(cipher))
//SYC{M@y_bE_m3et_HeR_1s_A_Err0R}

obfuscat3_revenge

题目函数部分有大量混淆,可以用d810一键去混淆,安装完插件之后ctrl + shift + D调出d810配置面板,载入去混淆规则之后start开始去混淆然后重新编译。

image-20260104185245714

主函数:

v10和v11是固定密钥,主要就是函数加密和验证的过程

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
unsigned __int8 s1[32]; // [rsp+40h] [rbp-280h] BYREF
void *src; // [rsp+60h] [rbp-260h]
size_t n; // [rsp+68h] [rbp-258h]
char s[48]; // [rsp+70h] [rbp-250h] BYREF
unsigned __int8 v8[256]; // [rsp+A0h] [rbp-220h] BYREF
unsigned __int8 v9[256]; // [rsp+1A0h] [rbp-120h] BYREF
unsigned __int8 v10[8]; // [rsp+2A0h] [rbp-20h] BYREF
__int64 v11; // [rsp+2A8h] [rbp-18h]
int v12; // [rsp+2BCh] [rbp-4h]

v12 = 0;
*v10 = 0x8877665540302010LL;
v11 = 0x2301EFDECDBCAB90LL;
gen(v9, v8, v10);
memset(s, 0, 0x24uLL);
printf("Input the flag: ");
if ( fgets(s, 36, stdin) )
{
n = strcspn(s, "\r\n");
s[n] = 0;
src = s;
if ( n >= 3 && *src == 239 && *(src + 1) == 187 && *(src + 2) == 191 )
{
src = src + 3;
n -= 3LL;
}
if ( n && n <= 0x20 )
{
memset(s1, 0, sizeof(s1));
memcpy(s1, src, n);
real_en(s1, v10, v9, v8);
if ( !memcmp(s1, &main::target_enc, 0x20uLL) )
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函数:S-box + 逆 S-box

image-20260104185927308

real_enc函数:

调用了enc1函数和perm函数,主要就是xor+sbox

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
unsigned __int8 *__fastcall real_en(
unsigned __int8 *s1,
unsigned __int8 *a2,
unsigned __int8 *a3,
const unsigned __int8 *a4)
{
unsigned __int8 *s1_2; // rax
int v5; // eax
unsigned __int8 *s1_3; // rax
unsigned int v7; // [rsp+14h] [rbp-ACh]
unsigned int v8; // [rsp+18h] [rbp-A8h]
unsigned int v9; // [rsp+1Ch] [rbp-A4h]
unsigned int v10; // [rsp+20h] [rbp-A0h]
unsigned int v11; // [rsp+24h] [rbp-9Ch]
int k; // [rsp+28h] [rbp-98h]
unsigned int v13; // [rsp+2Ch] [rbp-94h]
unsigned int v14; // [rsp+30h] [rbp-90h]
unsigned int v15; // [rsp+34h] [rbp-8Ch]
unsigned int v16; // [rsp+38h] [rbp-88h]
unsigned int v17; // [rsp+3Ch] [rbp-84h]
unsigned int v18; // [rsp+40h] [rbp-80h]
unsigned int v19; // [rsp+44h] [rbp-7Ch]
unsigned int v20; // [rsp+48h] [rbp-78h]
int j; // [rsp+4Ch] [rbp-74h]
_QWORD v22[5]; // [rsp+50h] [rbp-70h]
int i; // [rsp+7Ch] [rbp-44h]
const unsigned __int8 *v24; // [rsp+80h] [rbp-40h]
unsigned __int8 *v25; // [rsp+88h] [rbp-38h]
unsigned __int8 *v26; // [rsp+90h] [rbp-30h]
unsigned __int8 *s1_1; // [rsp+98h] [rbp-28h]

s1_1 = s1;
v26 = a2;
v25 = a3;
v24 = a4;
en1(s1, a2, 32);
for ( i = 0; i < 32; ++i )
s1_1[i] = v25[s1_1[i]];
for ( j = 0; j < 32; ++j )
*(v22 + j) = s1_1[perm[j]];
s1_2 = s1_1;
*s1_1 = v22[0];
*(s1_2 + 1) = v22[1];
*(s1_2 + 2) = v22[2];
*(s1_2 + 3) = v22[3];
v20 = *s1_1;
v19 = *(s1_1 + 1);
v18 = *(s1_1 + 2);
v17 = *(s1_1 + 3);
v16 = *(s1_1 + 4);
v15 = *(s1_1 + 5);
v14 = *(s1_1 + 6);
v13 = *(s1_1 + 7);
for ( k = 0; k < 4; ++k )
{
v11 = *&v26[4 * (k % 4)];
v10 = F(v19, v11, v25) ^ 0xEB4BED4 ^ (v20 & 0xA048412B | ~v20 & 0x51030000 | 0x8A430C0) ^ (~v20 & 0x6108E14 | v20 & 0x8A430C0 | 0x51030000);
v20 = v19;
v19 = v10;
v5 = F(v17, v11, v25);
v9 = (v18 & 0x805110C0 | ~v18 & 0x2C28E804 | 0x43000638) ^ (~v18 & 0x10860103 | v18 & 0x43000638 | 0x2C28E804) ^ (v5 & 0x2C3060C4 | ~v5 & 0x80499800 | 0x40860723) ^ (~v5 & 0x13000018 | v5 & 0x40860723 | 0x80499800);
v18 = v17;
v17 = v9;
v8 = v16 ^ F(v15, v11, v25);
v16 = v15;
v15 = v8;
v7 = F(v13, v11, v25) ^ v14;
v14 = v13;
v13 = v7;
}
*s1_1 = v20;
*(s1_1 + 1) = v19;
*(s1_1 + 2) = v18;
*(s1_1 + 3) = v17;
*(s1_1 + 4) = v16;
*(s1_1 + 5) = v15;
*(s1_1 + 6) = v14;
s1_3 = s1_1;
*(s1_1 + 7) = v13;
return s1_3;
}

F函数:

混淆严重,去混淆后很清晰。是xor密钥后sbox替换再循环左移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__int64 __fastcall F(int a1, unsigned int a2, const unsigned __int8 *a3)
{
int v3; // ebx
int v4; // r12d
unsigned __int8 v6; // [rsp+4h] [rbp-3Ch]
unsigned __int8 v7; // [rsp+5h] [rbp-3Bh]
unsigned __int8 v8; // [rsp+6h] [rbp-3Ah]
unsigned __int8 v9; // [rsp+7h] [rbp-39h]

v9 = a3[(a1 ^ a2)];
v8 = a3[(a1 ^ a2) >> 8];
v7 = a3[((a1 ^ a2) >> 16)];
v6 = a3[(a1 ^ a2) >> 24];
v3 = ~((v7 << 16) | (v8 << 8) | v9) & 0x1E7532C0;
v4 = ~((v7 << 16) | (v8 << 8) | v9) | 0x1E7532C0;
return not_en(
(v6 << 24) & ((v7 << 16) | (v8 << 8) | v9) | ((v6 << 24) ^ 0xE18ACD3F) & (~(v4 | ~v3) | ((v7 << 16) | (v8 << 8) | v9) & 0xE18ACD3F ^ v3) | ~(~(v4 | ~v3) | ((v7 << 16) | (v8 << 8) | v9) & 0xE18ACD3F ^ v3) & ((v6 << 24) ^ 0x1E7532C0),
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
from typing import List
MASK32 = 0xFFFFFFFF
target_enc = bytes.fromhex(
"CA5A96FF084972393618138A14C00C78"
"F87C49C7BEE891ED7FB002AD7774D434"
)

perm = bytes.fromhex(
"1F000102030405060708090A0B0C0D0E"
"0F101112131415161718191A1B1C1D1E"
)

key = bytes.fromhex("102030405566778890ABBCCDDEEF0123")

def rol32(x: int, r: int) -> int:
r &= 31
return ((x << r) | (x >> (32 - r))) & MASK32


def b2nle(b: bytes, n: int) -> List[int]:
return [int.from_bytes(b[i:i+n], 'little')
for i in range(0, len(b), n)]


def n2ble(a: List[int], n: int) -> bytes:
out = bytearray()
for x in a:
out.extend((x & ((1 << (8*n)) - 1)).to_bytes(n, 'little'))
return bytes(out)

def gen(key: bytes):
S = bytearray(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % 16]) & 0xFF
S[i], S[j] = S[j], S[i]

invS = bytearray(256)
for i in range(256):
invS[S[i]] = i
return S, invS

def en1(buf: bytearray, key: bytes):
for i in range(len(buf)):
buf[i] ^= key[i % 16]

def F(a1: int, rk: int, S: bytearray) -> int:
x = (a1 ^ rk) & MASK32
b0 = S[x & 0xFF]
b1 = S[(x >> 8) & 0xFF]
b2 = S[(x >> 16) & 0xFF]
b3 = S[(x >> 24) & 0xFF]

T = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0
return rol32(T, 5)
def decrypt_real_en(enc: bytes) -> bytes:
S, invS = gen(key)
blocks = b2nle(enc, 4)[::-1]
dkey = b2nle(key, 4)
for r in range(3, -1, -1):
for i in range(0, 8, 2):
L = blocks[i]
R = blocks[i+1]
blocks[i] = R
blocks[i+1] = (L ^ F(R, dkey[r], S)) & MASK32

blocks = blocks[::-1]
buf = bytearray(n2ble(blocks, 4))
buf = buf[1:] + buf[:1]
for i in range(32):
buf[i] = invS[buf[i]]

en1(buf, key)

return bytes(buf)


pt = decrypt_real_en(target_enc)
print(pt.rstrip(b"\x00").decode("utf-8", errors="replace"))
//SYC{Then_you_are_1mpressivse}

flutter

用blutter恢复符号表,主要看libverify.so文件

主要逻辑,一个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
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
bool __fastcall sub_19F38(__int64 a1)
{
bool v1; // w21
__int64 v2; // x27
__int64 v4; // x23
unsigned __int64 v5; // x24
unsigned __int64 n0xF; // x8
int n9; // w8
int n2; // w8

v2 = obj_;
if ( qword_42058 == obj_ )
return 0;
v4 = 0LL;
v5 = 0LL;
do
{
n0xF = *(v2 + v4);
if ( n0xF > 0xF || !off_3D1C8[n0xF] )
{
*(a1 + 28) = 1;
n2 = 2;
goto LABEL_12;
}
(off_3D1C8[*(v2 + v4)])(a1, v2 + v4);
n9 = *(v2 + v4);
if ( n9 == 9 )
goto LABEL_10;
if ( n9 != 8 )
{
n2 = *(a1 + 28);
if ( !*(a1 + 28) )
goto LABEL_12;
LABEL_10:
v1 = 0;
goto LABEL_11;
}
v1 = *(a1 + 28) == 0;
LABEL_11:
n2 = 1;
LABEL_12:
if ( n2 )
goto LABEL_15;
v2 = obj_;
++v5;
v4 += 3LL;
}
while ( v5 < 0xAAAAAAAAAAAAAAABLL * (qword_42058 - obj_) );
n2 = 2;
LABEL_15:
if ( n2 == 2 )
return 0;
return v1;
}

函数指针表 off_3D1C8[opcode]

解密思路:input[i] = ((byte_FF92[i] ^ 0x52) ^ 0x55) + 3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
byte_FF92 = [
0x57,0x51,0x47,0x7F,0x44,0x29,0x75,0x76,
0x76,0x37,0x68,0x5B,0x67,0x3A,0x6C,0x5B,
0x55,0x77,0x37,0x5B,0x65,0x74,0x37,0x68,
0x71,0x73,0x42,0x65,0x68,0x37,0x19,0x7D
]

flag = []

for i, b in enumerate(byte_FF92):
temp= b ^ 0x52
temp ^= 0x55
ch = (temp + 3) & 0xFF

flag.append(chr(ch))

print("".join(flag))
//SYC{F1utt3r_c@n_Us3_ev3rywHer3!}

极客大挑战2025逆向部分题解
https://j1nxem-o.github.io/2025/11/09/极客大挑战2025逆向部分题解/
作者
J1NXEM
发布于
2025年11月9日
更新于
2026年1月5日
许可协议