F-Hacker-CTF

Reverse

先来一道签到

gcc 编译流程

.s 文件,GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0,在liinux内用gcc -c xxx.s -o xxx.o,gcc xxx.o -o xxx.exe,再用IDA打开反编译

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
.file   "chal_custom.c"
.text
.section .rodata
.LC0:
.string "%s"
.LC1:
.string "maybe try again!"
.LC2:
.string "good job!"
.data
target_data:
.string "TTDv^jrZu`Gg6tXfi+pZojpZSjXmbqbmt.&x"
.text
.globl main
.type main, @function
main:
endbr64
pushq %rbp
movq %rsp, %rbp
subq $96, %rsp
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
leaq -80(%rbp), %rax
movq %rax, %rsi
leaq .LC0(%rip), %rax
movq %rax, %rdi
movl $0, %eax
call __isoc99_scanf@PLT
movl $0, -84(%rbp)
leaq -80(%rbp), %rdi
jmp .L2
.L3:
movl -84(%rbp), %eax
movl %eax, %ecx
shl %ecx
movzbl (%rdi,%rcx), %eax
xorb $7, %al
movb %al, (%rdi,%rcx)
movzbl 1(%rdi,%rcx), %eax
subb $5, %al
movb %al, 1(%rdi,%rcx)
addl $1, -84(%rbp)
.L2:
movl -84(%rbp), %eax
imull $2, %eax
cmpl $36, %eax
jl .L3

leaq target_data(%rip), %rsi
leaq -80(%rbp), %rdi
call strcmp@PLT
testl %eax, %eax
jne .L4

leaq .LC2(%rip), %rax
jmp .L5
.L4:
leaq .LC1(%rip), %rax
.L5:
movq %rax, %rdi
call puts@PLT

movl $0, %eax
movq -8(%rbp), %rdx
subq %fs:40, %rdx
je .L6
call __stack_chk_fail@PLT
.L6:
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:

PixPin_2025-03-27_19-48-24

1
2
3
4
5
6
7
8
9
10
enc = [84, 84, 68, 118, 94, 106, 114, 90, 117, 96, 71, 103, 54, 116, 88, 102, 105, 43, 112, 90, 111, 106, 112, 90, 83,
106, 88, 109, 98, 113, 98, 109, 116, 46, 38, 120]

for i in range(len(enc)):
if i % 2 == 0:
print(chr(enc[i] ^ 7), end='')
else:
print(chr(enc[i] + 5), end='')
# SYC{You_re@l1y_kn0w_how_To_revers3!}

holle_re

UPX壳改了对应十六进制标志导致工具脱壳不成功,用010打开找到并改回来就行

查壳,发现有UPX壳

PixPin_2025-03-30_23-43-56

upx -d 脱壳失败,之前遇到过类似的情况,用010打开

PixPin_2025-03-30_23-48-42

把1处改为2处再保存就行,再upx -d 就成功了

PixPin_2025-03-30_23-50-34

IDA打开分析

PixPin_2025-03-30_23-57-59

脚本:

1
2
3
4
5
6
7
8
9
v7 = [
0, 1, 2, 52, 3, 96, 47, 28, 107, 15, 9, 24,
45, 62, 60, 2, 17, 123, 39, 58, 41, 48, 96,
26, 8, 52, 63, 100, 33, 106, 122, 48]
v5 = list('REVOLCYS')[::-1]
for j in range(len(v7)):
v7[j] ^= j ^ ord(v5[j % 8])
print(bytes(v7).decode())
# SYC{H3lI0_@_new_R3vers3_Ctf3r!!}

让康康你的调试

IDA打开分析

PixPin_2025-03-31_00-17-01

加密部分,对我们的输入只有异或操作,前面也只有一个异或0x14,也就是说,只要把密文重新输进去就可以得到flag

PixPin_2025-03-31_22-25-51

先提取密文,小端序33位,文件是elf要到liunx里启用远程调试服务

1
2
3
4
5
6
7
8
9
10
11
12
13
s2[0] = 0xA67A02C9047D5B94LL;
s2[1] = 0x7EF9680DBC980739LL;
s2[2] = 0x7104F81698BFBD08LL;
s2[3] = 0x61DB8498B686155FLL;
n0x6D = 0x6D;

enc = [
0x94, 0x5B, 0x7D, 0x04, 0xC9, 0x02, 0x7A, 0xA6, 0x39, 0x07,
0x98, 0xBC, 0x0D, 0x68, 0xF9, 0x7E, 0x08, 0xBD, 0xBF, 0x98,
0x16, 0xF8, 0x04, 0x71, 0x5F, 0x15, 0x86, 0xB6, 0x98, 0x84,
0xDB, 0x61, 0x6D
]
# 94 5B 7D 04 C9 02 7A A6 39 07 98 BC 0D 68 F9 7E 08 BD BF 98 16 F8 04 71 5F 15 86 B6 98 84 DB 61 6D

初始输入

PixPin_2025-03-31_22-52-00

使用change byte修改字节码

PixPin_2025-03-31_22-53-38

运行到验证结果处

PixPin_2025-03-31_22-55-34

flag

1
SYC{welcome_t0_Geek's_3asy_rc4!}

我嘞个z3

Z3约束求解器

PixPin_2025-03-27_20-30-18

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
# 0
# key
str2 = [
0x2A, 0x0E, 0x0E, 0x14, 0x3F, 0x3F, 0x3F, 0x26, 0x11, 0x0A,
0x15, 0x15, 0x0E, 0x17, 0x10, 0x0E
]
list_table = list('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?_')
for i in str2:
print(list_table[i], end='')
print()

# __int64 __fastcall sub_40179A(__int64 a1)
# {
# int i; // [rsp+Ch] [rbp-4h]
#
# for ( i = 0; i <= 31; i += 4 )
# {
# if ( *(char *)(a1 + i) + 8 * *(char *)(a1 + i + 1) + 6 * *(char *)(a1 + i + 2) + *(char *)(a1 + i + 3) != dword_4040A0[i]
# || *(char *)(a1 + i + 1) + 8 * *(char *)(a1 + i + 2) + 6 * *(char *)(a1 + i + 3) + *(char *)(a1 + i) != dword_4040A0[i + 1]
# || *(char *)(a1 + i + 2) + 8 * *(char *)(a1 + i + 3) + 6 * *(char *)(a1 + i) + *(char *)(a1 + i + 1) != dword_4040A0[i + 2]
# || *(char *)(a1 + i + 3) + 8 * *(char *)(a1 + i) + 6 * *(char *)(a1 + i + 1) + *(char *)(a1 + i + 2) != dword_4040A0[i + 3] )
# {
# return 0i64;
# }
# }
# return 1i64;
# }

# 1
# dword 双字节
dword_4040A0 = [
0x9B, 0x01, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x89, 0x01,
0x00, 0x00, 0xC9, 0x01, 0x00, 0x00, 0x50, 0x02, 0x00, 0x00,
0x36, 0x05, 0x00, 0x00, 0xDE, 0x04, 0x00, 0x00, 0xBC, 0x01,
0x00, 0x00, 0x1B, 0x04, 0x00, 0x00, 0x24, 0x07, 0x00, 0x00,
0xD0, 0x06, 0x00, 0x00, 0xA1, 0x04, 0x00, 0x00, 0x45, 0x06,
0x00, 0x00, 0x75, 0x04, 0x00, 0x00, 0xCA, 0x04, 0x00, 0x00,
0x8C, 0x06, 0x00, 0x00, 0xE5, 0x03, 0x00, 0x00, 0xC7, 0x01,
0x00, 0x00, 0x3D, 0x03, 0x00, 0x00, 0xB7, 0x05, 0x00, 0x00,
0x8D, 0x02, 0x00, 0x00, 0x44, 0x02, 0x00, 0x00, 0x0E, 0x03,
0x00, 0x00, 0x91, 0x02, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00,
0x01, 0x03, 0x00, 0x00, 0x5F, 0x04, 0x00, 0x00, 0x6F, 0x04,
0x00, 0x00, 0x17, 0x05, 0x00, 0x00, 0x1E, 0x04, 0x00, 0x00,
0x26, 0x04, 0x00, 0x00, 0xB5, 0x04, 0x00, 0x00
]

c = [
0x19B, 0x113, 0x189, 0x1C9, 0x250, 0x536, 0x4DE, 0x1BC, 0x41B, 0x724,
0x6D0, 0x4A1, 0x645, 0x475, 0x4CA, 0x68C, 0x3E5, 0x1C7, 0x33D, 0x5B7,
0x28D, 0x244, 0x30E, 0x291, 0x271, 0x301, 0x45F, 0x46F, 0x517, 0x41E,
0x426, 0x4B5
]

from z3 import *

s = Solver()

x = [Int(f"x{_}") for _ in range(0, 32)]
array = []
for i in range(0, len(c), 4):
s.add(x[i] + 8 * x[i + 1] + 6 * x[i + 2] + x[i + 3] == c[i])
s.add(x[i + 1] + 8 * x[i + 2] + 6 * x[i + 3] + x[i] == c[i + 1])
s.add(x[i + 2] + 8 * x[i + 3] + 6 * x[i] + x[i + 1] == c[i + 2])
s.add(x[i + 3] + 8 * x[i] + 6 * x[i + 1] + x[i + 2] == c[i + 3])
if s.check() == sat:
r = s.model()
print(r)
else:
print('no answer')

# [x6 = 69,
# x29 = 102,
# x2 = 7,
# x18 = 28,
# x7 = 125,
# x19 = 5,
# x3 = 26,
# x11 = 118,
# x9 = 9,
# x5 = 3,
# x17 = 89,
# x13 = 126,
# x14 = 74,
# x23 = 70,
# x22 = 9,
# x31 = 69,
# x1 = 40,
# x10 = 125,
# x25 = 26,
# x26 = 43,
# x21 = 63,
# x30 = 60,
# x15 = 54,
# x27 = 48,
# x24 = 111,
# x12 = 99,
# x0 = 23,
# x16 = 112,
# x28 = 58,
# x20 = 25,
# x8 = 111,
# x4 = 29]


# const char *__fastcall sub_401AAC(const char *a1, __int64 a2)
# {
# const char *result; // rax
# int v3; // [rsp+28h] [rbp-8h]
# int i; // [rsp+2Ch] [rbp-4h]
#
# sub_4019EB(a1);
# result = (const char *)strlen(a1);
# v3 = (int)result;
# for ( i = 0; i <= 31; ++i )
# {
# a1[i] ^= a1[(v3 + i - 1) % v3];
# result = &a1[i];
# *result ^= *(_BYTE *)(a2 + (47 - i) % 16) ^ (unsigned __int8)i;
# }
# return result;
# }

# 2
x = [23, 40, 7, 26, 29, 3, 69, 125, 111, 9, 125, 118, 99, 126, 74, 54, 112, 89, 28, 5, 25, 63, 9, 70, 111, 26, 43, 48,
58, 102, 60, 69]
v3 = len(x)
flag = ''
for i in range(len(x)):
x[i] ^= x[(i + v3 + 1) % v3]
r = x[i]

key = 'Geek___Challenge'
x = [23, 40, 7, 26, 29, 3, 69, 125, 111, 9, 125, 118, 99, 126, 74, 54, 112, 89, 28, 5, 25, 63, 9, 70, 111, 26, 43, 48,
58, 102, 60, 69]
for i in range(len(x) - 1, -1, -1):
x[i] ^= ord(key[(47 - i) % 16]) ^ i
x[i] ^= x[(len(x) + i - 1) % len(x)]

for i in range(len(x)):
print(chr(x[i]), end='')
print()
# SYC{ow!WY0!_ru_43_9od_A0r3t_}$!!

# 3
x = 'SYC{ow!WY0!_ru_43_9od_A0r3t_}$!!'
flag = ''
for i in range(8):
tmp1 = x[4 * i:4 * i + 4]
for j in range(i):
tmp2 = ''
tmp2 += tmp1[3] + tmp1[0:3]
tmp1 = tmp2
flag += tmp1
print(flag)
# SYC{Wow!!_Y0u_4r3_9o0d_At_r3$!!}

好像是python?

pyc反编译失败,用ai辅助直接看汇编逆向

一个没有后缀的文件,根据题目,应该改成.py或.pyc,再用pycdc或uncompyle6反编译

但都反编译失败,所以就直接看汇编再用ai写逻辑了

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
# -*- coding=utf-8 -*-
#
# 反编译后的代码
flag = 'SYC{MD5(input)}'

print('Please input0:')
input0 = ''


def test2(s2):
key = 'SYC'
length = 18
cipher = []
for i in range(length):
c = ord(s2[i]) ^ i
c += (~ord(key[i % 3]) + 1)
cipher.append(c)
return cipher


def test(s, R):
result = []
for i in s:
if 'A' <= i <= 'Z':
shifted = (ord(i) - ord('A') + R) % 26
result.append(chr(shifted + ord('A')))
elif 'a' <= i <= 'z':
shifted = (ord(i) - ord('a') + R) % 26
result.append(chr(shifted + ord('a')))
elif '0' <= i <= '9':
shifted = (ord(i) - ord('0') + R) % 10
result.append(chr(shifted + ord('0')))
else:
result.append(i)
return ''.join(result)


a = 13
b = 14
c = a ^ (b + a)
d = b * 100
e = a ^ b
m = d - c * 4 + e - 1
r = m % 26

cipher1 = test(input0, r)
cipher2 = test2(cipher1)

num = [-1, -36, 26, -5, 14, 41, 6, -9, 60, 29, -28, 17, 21, 7, 35, 38, 26, 48]

for i in range(18):
if cipher2[i] != num[i]:
print('wrong!')
exit()
print('Rrrright!')

脚本:

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
# 逆向推导步骤:
# 1. 计算已知变量的值
a = 13
b = 14
c = a ^ (a + b) # 13 ^ 27 = 22
d = b * 100 # 1400
e = a ^ b # 13 ^ 14 = 3
m = d - c * 4 + e - 1 # 1400 - 88 +3 -1 = 1314
r = m % 26 # 1314 % 26 = 14

# 2. 逆向test2函数
num = [-1, -36, 26, -5, 14, 41, 6, -9, 60, 29, -28, 17, 21, 7, 35, 38, 26, 48]
key = 'SYC'

cipher1 = []
for i in range(18):
# cipher2[i] = (cipher1[i] ^ i) + (~ord(key[i%3]) +1
# 逆向: cipher1[i] = (num[i] - (~ord(key[i%3]) +1 )) ^ i
k = ord(key[i % 3])
inv_k = ~k + 1
c1 = (num[i] - inv_k) ^ i
cipher1.append(c1)

# 3. 逆向test函数 (R=14)
plaintext = []
for c in cipher1:
c = chr(c)
if 'A' <= c <= 'Z':
shifted = (ord(c) - ord('A') - 14) % 26
plaintext.append(chr(shifted + ord('A')))
elif 'a' <= c <= 'z':
shifted = (ord(c) - ord('a') - 14) % 26
plaintext.append(chr(shifted + ord('a')))
elif '0' <= c <= '9':
shifted = (ord(c) - ord('0') - 14) % 10
plaintext.append(chr(shifted + ord('0')))
else:
plaintext.append(c)

input0 = ''.join(plaintext)
print("Flag:", f'SYC{{{input0}}}') # 注意计算MD5,但根据题目直接输出明文
# SYC{D0_You_Iik3_python}

也许你也听jay

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
# -*- coding=utf-8 -*-

b = [0x96, 0xa1, 0xa0, 0x9b, 0x9b, 0x5f, 0x49, 0x46, 0x85, 0x82, 0x53, 0x95, 0x7d, 0x36, 0x8d, 0x74, 0x82, 0x88, 0x46,
0x7a, 0x81, 0x65, 0x80, 0x6c, 0x78, 0x2f, 0x6b, 0x6a, 0x27, 0x50, 0x61, 0x38, 0x3f, 0x37, 0x33, 0xf1, 0x27, 0x32,
0x34, 0x1f, 0x39, 0x23, 0xde, 0x1c, 0x17, 0xd4]
c = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13,
0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C,
0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85,
0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D]
d = [0x5D, 0x5C, 0x5B, 0x5A, 0x59, 0x58, 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B,
0x4A, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38,
0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x28, 0x27, 0x26, 0x25,
0x24, 0x23, 0x22, 0x21, 0x20, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12,
0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]
l = len(b)
print(l)

for i in range(l):
b[i] -= d[i]

for i in range(l):
b[i] += c[47 + i]

for i in range(l):
b[i] ^= c[i]
print(chr(b[i]).encode('utf-8').decode('gbk'), end='')


url = 'https://am1re-sudo.github.io/Coisni'
print(len(url))

# https://am1re-sudo.github.io/Coisni漠github漠io寞35
# 在
# https://github.com/Am1re-sudo/Coisni.github.io/blob/main/geek/index.html
# 找到三个提示
# Q7u+cyiOQtKHRMqZNzPpApgmTL4j+TE= 密文
# lovebeforeBC 秘钥
# RC4 解密

玩就行了

方法一:是的,玩就行了,只要玩到对应分数就会在目录下生成一个Data.txt文件

方法二:用CE修改器找到分数修改就行

方法三:用despy打开Assembly-CSharp.dll,找到分数位置修改好后保存就行,或者改每次的得分大小

PixPin_2025-03-01_22-32-24

Data.txt文件里面是16进制数,用010十六进制编辑器打开,发现会被错误的识别然后再用十六进制翻译表示一遍,所以我们按ctrl+shift+v修正,

或者先将Data.txt中的内容复制一遍,再将光标放在第一位十六进制数字前按ctrl+v粘贴,相当于覆盖修改,然后另存为Data.exe文件

再用ID打开后看逻辑分析逆向即可

PixPin_2025-03-31_00-49-10

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
# -*- coding=utf-8 -*-

# __int64 __fastcall sub_14000144B(const char *a1, int a2)
# {
# __int64 result; // rax
# char v3; // [rsp+27h] [rbp-9h]
# int v4; // [rsp+28h] [rbp-8h]
# unsigned int i; // [rsp+2Ch] [rbp-4h]
#
# v4 = strlen(a1);
# for ( i = 0; ; ++i )
# {
# result = i;
# if ( (int)i >= v4 )
# break;
# v3 = a1[i];
# if ( v3 <= 96 || v3 > 122 )
# {
# if ( v3 <= 64 || v3 > 90 )
# {
# if ( v3 > 47 && v3 <= 57 )
# a1[i] = (a2 + v3 - 48) % 10 + 48;
# }
# else
# {
# a1[i] = (v3 - 65 + a2) % 26 + 65;
# }
# }
# else
# {
# a1[i] = (v3 - 97 + a2) % 26 + 97;
# }
# }
# return result;
# }


# __int64 __fastcall sub_140001596(const char *a1, const char *a2)
# {
# __int64 result; // rax
# int v3; // [rsp+24h] [rbp-Ch]
# int v4; // [rsp+28h] [rbp-8h]
# signed int i; // [rsp+2Ch] [rbp-4h]
#
# v4 = strlen(a1);
# v3 = strlen(a2);
# for ( i = 0; ; ++i )
# {
# result = (unsigned int)i;
# if ( i >= v4 )
# break;
# a1[i] ^= a2[i % v3];
# }
# return result;
# }

# 字符串转十六进制
# _BYTE *__fastcall sub_14000161C(const char *a1, __int64 a2)
# {
# _BYTE *result; // rax
# int v3; // [rsp+24h] [rbp-Ch]
# int v4; // [rsp+28h] [rbp-8h]
# int i; // [rsp+2Ch] [rbp-4h]
#
# v3 = strlen(a1);
# v4 = 0;
# for ( i = 0; i < v3; ++i )
# {
# sub_140001408(v4 + a2, "%02X", (unsigned int)a1[i]);
# v4 += 2;
# }
# result = (_BYTE *)(v4 + a2);
# *result = 0;
# return result;
# }

# v5 = '0A161230300C2D0A2B303D2428233005242C2D26182206233E097F133A'
v5 = [10, 22, 18, 48, 48, 12, 45, 10, 43, 48, 61, 36, 40, 35, 48, 5, 36, 44, 45, 38, 24, 34, 6, 35, 62, 9, 127, 19, 58]
v4 = 'GEEK'
flag = ''
for i in range(len(v5)):
v5[i] = v5[i] ^ ord(v4[i % 4])

a2 = 20
for char in v5:
ascii_val = char

# 处理小写字母 (a-z: 97-122)
if ord('a') <= ascii_val <= ord('z'):
flag += chr((ascii_val - ord('a') - a2) % 26 + ord('a'))
# 处理大写字母 (A-Z: 65-90)
elif ord('A') <= ascii_val <= ord("Z"):
flag += chr((ascii_val - ord('A') - a2) % 26 + ord('A'))
# 处理数字 (0-9: 48-57)
elif ord('0') <= ascii_val <= ord('9'):
flag += chr((ascii_val - 48 - a2) % 10 + ord('0'))
# 其他字符保持不变
else:
flag += chr(char)

print(flag)

# SYC{cOnGraduulaTions_mIneR:D}

致我的星星

maze,双重迷宫

题目在index.js最后面,是一个迷宫题

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

s = Solver()

a = [Int(f"a'_'") for _ in range(0, 8)]
array = []

s.add(43654*a1 - 57003*a2 -3158*a3 + 30120*a4 -58621*a5 -41947*a6 +122237*a7 +129534*a8 == 2594143)
s.add(-48022*a1 + 18308*a2 + 52478*a3 + 69397*a4 +49696*a5 + 12288*a6 -40437*a7 -23154*a8 == 651137)
s.add(124109*a1 +58952*a2 + 16645*a3 -17531*a4 + 53139*a5 +49937*a6 + 3282*a7 +7656*a8 == 2071815)
s.add(108286*a1 + 118886*a2 +116876*a3 +2281*a4 -64590*a5 -3021*a6 + 13386*a7 -56070*a8 == 703)
s.add(105983*a1 + 8794*a2 + 31851*a3 -35052*a4 -7880*a5 + 2183*a6 + 47575*a7 +107236*a8 == 2511919)
s.add(-38005*a1 -6833*a2 +107897*a3 +119771*a4 +124322*a5 + 13335*a6+ 121590*a7 -17434*a8 == 4816084)
s.add(60696*a1 +95253*a2 +101581*a3 + 93822*a4 +112989*a5 +65643*a6 +45639*a7 + 26705*a8 == 5330538)
s.add(49019*a1 +72343*a2 -21814*a3 +85020*a4 -62332*a5 +99828*a6 + 587*a7 -65119*a8 == 505173)

if s.check() == sat:
r = s.model()
print(r)
else:
print('no answer')

# [a3 = 6,
# a4 = 8,
# a5 = 13,
# a1 = 3,
# a8 = 15,
# a2 = 4,
# a6 = 13,
# a7 = 15]
# a1 = 3, a2 = 4, a3 = 6, a4 = 8, a5 = 13, a6 = 13, a7 = 15, a8 = 15
# S:(a1,a6) (a4,a5) Y:(a2,a8) (a3,a7)
# S:(3,13) (8,13) Y:(4,15) (6,15)

坑点,之前一直按后面的迷宫图来放起点与终点,结果就是怎么放都不对劲,题解也不对;最后详细看一遍代码才发现,起点与终点是先按这个图填入,再转化为真正的迷宫图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
data = [
'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#',
'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#',
'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', 'S1', '#', '#', '#', '#', '#', '#', '#',
'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', 'Y1', '#', '#', '#', '#', '#',
'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#',
'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', 'Y2', '#', '#', '#', '#', '#',
'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#',
'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', 'S2', '#', '#', '#', '#', '#', '#', '#',
'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#'
]

# 障碍物,遍历一遍,把障碍物用*来替代,但后面发现这其实才是路,w(゚Д゚)w
coordinates = [
[1, 11], [1, 12], [1, 13], [1, 14],
[2, 1], [2, 4], [2, 11], [2, 12], [2, 14],
[3, 4], [3, 14],
[5, 7], [5, 8], [5, 12], [5, 14], [5, 15], [5, 18],
[6, 1], [6, 2], [6, 4], [6, 5], [6, 7], [6, 8], [6, 11], [6, 15],
[7, 1], [7, 5], [7, 11], [7, 12], [7, 13], [7, 14], [7, 15], [7, 16],
[8, 1], [8, 12], [8, 13], [8, 15], [8, 18],
[9, 3], [9, 4], [9, 6], [9, 7], [9, 8]
]

Exp:

将字符串形式的迷宫变为指定行列的列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# maze_str_to_list
maze_str = ("###############################****######*##*######**#*#########*#########*################################**###*#**##*##**#**#**##*###*#####*###*#####******####*##########**#*##*#")
print(len(maze_str))
index = 0
row = 18
col = 10
maze = []
for i in range(row):
tmp = []
for j in range(col):
if maze_str[index] == '*' or maze_str[index] == ' ':
tmp.append(maze_str[index])
else:
tmp.append('#')
index += 1
maze.append(tmp)
print(maze)

用之前写好的通用求路径脚本,求解

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
from collections import deque

maze = [
['#', '#', '#', '#', '#', '#', '#', '#', '#', '#'],
['#', '#', '#', '#', '#', '#', '#', '#', '#', '#'],
['#', '#', '#', '#', '#', '#', '#', '#', '#', '#'],
['#', '*', '*', '*', '*', '#', '#', '#', '#', '#'],
['#', '*', '#', '#', '*', '#', '#', '#', '#', '#'],
['#', '*', '*', '#', '*', '#', '#', '#', '#', '#'],
['#', '#', '#', '#', '*', '#', '#', '#', '#', '#'],
['#', '#', '#', '#', '*', '#', '#', '#', '#', '#'],
['#', '#', '#', '#', '#', '#', '#', '#', '#', '#'],
['#', '#', '#', '#', '#', '#', '#', '#', '#', '#'],
['#', '#', '#', '#', '#', '#', '#', '*', '*', '#'],
['#', '#', '*', '#', 'Y2', '*', '#', '#', '*', '#'],
['#', '*', '*', '#', '*', '*', '#', '*', '*', '#'],
['#', '*', '#', '#', '#', '*', '#', '#', '#', '#'],
['#', '*', '#', '#', '#', '*', '#', '#', '#', '#'],
['#', '*', 'S2', '*', '*', '*', '*', '#', '#', '#'],
['#', '*', '#', '#', '#', '#', '#', '#', '#', '#'],
['#', '#', '*', '*', '#', '*', '#', '#', '*', '#']]
# S:(3,13) (8,13) Y:(4,15) (6,15)
path_len = 0 # 如果题目没给出终点坐标,就会给路径长度,但记得 len = 题目给的len + 1


def bfs(start, end, barrier):
# 方向:右、下、左、上
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]

start_pos = None
end_pos = None

# 查找起点和终点的坐标
for i in range(len(maze)):
for j in range(len(maze[i])):
if maze[i][j] == start: # 找到起点
start_pos = (i, j)
if maze[i][j] == end: # 找到终点
end_pos = (i, j)

if not start_pos or not end_pos:
print("起点或终点不存在")
return None # 如果起点或终点不存在,返回 None

queue = deque()
queue.append((start_pos, [start_pos])) # (当前位置, 路径)
visited = set()
visited.add(start_pos)

while queue:
position, path = queue.popleft()

if position == end_pos:
return path

# 遍历四个方向
for d in directions:
next_position = (position[0] + d[0], position[1] + d[1])

if 0 <= next_position[0] < len(maze) and 0 <= next_position[1] < len(maze[0]) and \
maze[next_position[0]][next_position[1]] != barrier and next_position not in visited:
queue.append((next_position, path + [next_position]))
visited.add(next_position)

return None # 如果没有找到路径,返回 None


if __name__ == '__main__':
maze[15][2] = 'S2' # 手动添加起点, x位置
path = bfs('S2', 'Y2', '#') # 手动添加, (起点, 终点, 障碍物)

if path is None:
print("没有找到路径")
else:
print("移动路径坐标:", path)
print("移动路径方位: ", end='')

for i in range(1, len(path)):
x1, y1 = path[i - 1]
x2, y2 = path[i]
if x1 > x2:
print("T", end='') # 上
elif x1 < x2:
print("R", end='') # 下
elif y1 > y2:
print("S", end='') # 左
elif y1 < y2:
print("A", end='') # 右

# STTAAARRRR
# AAATTTST AAATTTTS
# SYC{STTAAARRRRAAATTTTS}

giraffe_eat_rainbow

去除llvm,动态调试,输入32位字符,提取key和密文,进入encrypt( )就可以解密了

PixPin_2025-03-18_19-15-41

1
2
3
4
5
6
7
8
9
10
11
enc = [
0x1D, 0x36, 0x73, 0x16, 0x49, 0x2D, 0x1A, 0x1D, 0x29, 0x06,
0x42, 0x2C, 0x76, 0x07, 0x10, 0x0E, 0x7E, 0x39, 0x55, 0x32,
0x75, 0x03, 0x1B, 0x1D, 0x19, 0x5F, 0x52, 0x23, 0x01, 0x03,
0x1D, 0x3F
]

#'BOb0m0oN'
key = [
0x42, 0x4F, 0x62, 0x30, 0x6D, 0x30, 0x6F, 0x4E
]

PixPin_2025-03-18_19-28-22

扁平化没处理好还存在一些,但逻辑并不难,让ai处理一下逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
size_t encrypt(char *input, const char *key, unsigned char *output) {
size_t input_length = strlen(input);
size_t key_length = strlen(key);

for (size_t i = 0; i < input_length; i++) {
// 获取密钥中的对应字符(循环使用密钥)
char key_char = key[i % key_length];
// 获取输入字符
char input_char = input[i];
// 进行异或操作
output[i] = input_char ^ key_char;
}

return input_length;
}

解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# -*- coding=utf-8 -*-
enc = [
0x1D, 0x36, 0x73, 0x16, 0x49, 0x2D, 0x1A, 0x1D, 0x29, 0x06,
0x42, 0x2C, 0x76, 0x07, 0x10, 0x0E, 0x7E, 0x39, 0x55, 0x32,
0x75, 0x03, 0x1B, 0x1D, 0x19, 0x5F, 0x52, 0x23, 0x01, 0x03,
0x1D, 0x3F
]

#key = 'BOb0m0oN'
key = [
0x42, 0x4F, 0x62, 0x30, 0x6D, 0x30, 0x6F, 0x4E
]

flag = ''
for i in range(len(enc)-1, -1, -1):
key_idx = (len(enc)-1 - i) % len(key)
flag += chr(enc[i] ^ key[key_idx])

print(flag[::-1])
# SYC{yOU_girAFe_L0Ve_EaT_W0bN1aR}

AES!

aes加密,魔改/换了S盒

PixPin_2025-03-18_20-29-58

提取key与密文

1
2
3
4
5
6
7
8
enc = [
0x99, 0xE8, 0xB8, 0x01, 0xC8, 0x82, 0x51, 0x93, 0x12, 0xEE,
0x89, 0x64, 0xE7, 0xEF, 0x63, 0x8D, 0x51, 0xDF, 0x5D, 0x78,
0x39, 0xAA, 0x39, 0x62, 0xA0, 0xB4, 0x50, 0x30, 0x47, 0x30,
0x21, 0x06
]

key = b'SYCLOVERSYCLOVER'

PixPin_2025-03-18_20-28-55

改变的S盒,提取一下

PixPin_2025-03-18_20-41-24

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
S = [
0x7C, 0xCA, 0x7B, 0x77, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01,
0x67, 0x2B, 0xFE, 0xD7, 0x47, 0xAB, 0x76, 0x63, 0x82, 0xC9,
0x7D, 0xFA, 0x59, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4,
0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC,
0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7,
0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2,
0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E,
0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB,
0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB,
0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C,
0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0x97, 0xCD,
0x0C, 0x13, 0xEC, 0x5F, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D,
0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A,
0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3,
0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D,
0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A,
0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6,
0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E,
0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9,
0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9,
0x8E, 0x94, 0x9B, 0x1E, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 0x8C,
0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D,
0x0F, 0xB0, 0x54, 0xBB, 0x16, 0x87
]
print(len(s))
# 256

求逆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
def generate_inverse_sbox(sbox):
"""
生成逆S盒
:param sbox: 原始S盒(必须是一个双射的256字节列表)
:return: 逆S盒
"""
if len(sbox) != 256:
raise ValueError("S盒长度必须为256字节")

# 验证S盒是否为双射
unique_values = set(sbox)
if len(unique_values) != 256:
raise ValueError("输入的S盒不是双射(存在重复值)")

# 生成逆S盒
inverse_sbox = [0] * 256
for i, value in enumerate(sbox):
inverse_sbox[value] = i
return inverse_sbox

# ----------------------------
# AES标准S盒定义(示例)
# ----------------------------
aes_sbox = [
# 完整的AES S盒数据(此处仅展示部分,实际需要256个值)
# ...(完整数据需补充)
]

# ----------------------------
# 使用示例
# ----------------------------
if __name__ == "__main__":
try:
# 生成逆S盒
inverse_aes_sbox = generate_inverse_sbox(aes_sbox)

# 验证逆S盒
for i in range(256):
assert inverse_aes_sbox[aes_sbox[i]] == i, f"验证失败于索引 {i}"
print("逆S盒验证成功!")

# 打印前16个值示例
print("\n逆S盒前16字节:")
for i in range(16):
print(f"{inverse_aes_sbox[i]:02X}", end=' ')
if (i + 1) % 4 == 0:
print()

except ValueError as e:
print(f"错误:{e}")

逆S盒

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
S_inv = [
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x82, 0xF2, 0xD7, 0xFA,
0x7C, 0xE3, 0x39, 0x83, 0x9B, 0x2F, 0xFE, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xED, 0x4C, 0x95, 0x0B, 0x42, 0xF9, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF7, 0xF5, 0x64, 0x86, 0x68, 0x98, 0x0E, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFC, 0xEC, 0xB9, 0xDA, 0x5E, 0x16, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x85,
0x90, 0xD8, 0xAB, 0x11, 0x8C, 0xBC, 0xD3, 0x0A, 0xF6, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x10, 0x03, 0xC1, 0xAF, 0xBD, 0x02, 0x00, 0x14, 0x8A, 0x6B,
0x3A, 0x91, 0x12, 0x41, 0x4F, 0x67, 0xDC, 0xFF, 0x97, 0xF1, 0xCF, 0xCE, 0xEF, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x80, 0xE2, 0xF8, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF0, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0F, 0xAA, 0x18, 0xBE, 0x1B,
0xFB, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFD, 0x78, 0xCD, 0x5A, 0xF3,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x13, 0x01, 0x59, 0x27, 0x81, 0xEB, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEE,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF4, 0xB0, 0xC8, 0xEA, 0xBB, 0x3C, 0x84, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x15, 0x63, 0x55, 0x21, 0x0C, 0x7D
]

AES加密算法原理的详细介绍与实现-CSDN博客

AES加解密算法C实现 - 陌默安 - 博客园

AES加密,由S盒计算逆S盒程序_逆s盒如何计算-CSDN博客

找个完整的AES加密脚本,换好S盒与逆S盒,解密得flag

1
SYC{B3l1eue_Th@t_y0u__l3aRn_Aes}

ez_re

花指令,AES-CBC加密,TLS+API反调试,动态调试得key,iv,密文

IDA打开后发现没有main,因为花指令的影响,去除花指令后反编译得main逻辑,【CTF花指令讲解】&&【长城杯铁三reverse-junk】_哔哩哔哩_bilibili

用keypatch处理掉IsDebugger反调试后,运行发现调试出问题了,可以运行但运行不到main函数,cmd命令窗口也没有任何显示

原因是TLS反调试悄无声息地令调试器失效

用PEview + winhex去掉TLS

PixPin_2025-03-19_14-39-41

PixPin_2025-03-19_14-39-18

【miniLCTF2022】TWIN(TLS回调函数 & PEB反调试 & Windows异常处理_哔哩哔哩_bilibili

TLS回调函数–一文看懂(详)-CSDN博客

逆向常见反调试合集 - 纸飞机低空飞行 - 博客园

PixPin_2025-03-19_15-30-51

key和密文(小端序提取)可以直接提取,iv被处理过了,用动态调试提取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
*(_DWORD *)pbIV = 0x941E70C1;
*(_DWORD *)&pbIV[4] = 0x13142F4;
*(_DWORD *)&pbIV[8] = 0x550A3FB0;
*(_DWORD *)&pbIV[12] = 0xD399FFA1;
v12.m128_u64[0] = 0xFEF76ECE6FA34BA2ui64;
v12.m128_u64[1] = 0x67735D6CF76837ECi64;
v11[0] = 0x754CE0DD;
v11[1] = 0xB8143397;
*(__m128 *)pbIV = _mm_xor_ps(v12, *(__m128 *)pbIV);
v11[2] = 0x1119B617;
v11[3] = 0x23608A61;
v11[4] = 0x1AE21646;
v11[5] = 0x265BC365;
v11[6] = 0x30ADF568;
v11[7] = 0xC64BEEB1;
v11[8] = 0xBB9EB5AB;
v11[9] = 0xC24A9573;
v11[10] = 0xA9CBEF78;
v11[11] = 0xA0E171BE;

动态调试提取iv, key,enc

1
2
3
4
iv = '633BBDFB3A2CC6FF5C0862A2CDA2EAB4'	# pdIV
key = 'ADDDDB082DD94DCABAD5D75639EAC9DA' # sub_F0109D(PdSecret)
enc = 'DDE04C75973314B817B61911618A60234616E21A65C35B2668F5AD30B1EE4BC6ABB59EBB73954AC278EFCBA9BE71E1A0'
# v11

PixPin_2025-03-19_20-06-03

AES算法的CBC和ECB两种工作模式_aes cbc ebc区别-CSDN博客

分组密码的模式AES-CBC模式流程解析附:应用代码实现-CSDN博客


ez_hook

API反调试处理,hook,VirtualProtect

IDA打开,有反调试,先用keypatch处理掉,再分析

PixPin_2025-03-22_23-27-54

考点(坑点)在这,Address函数被hook了,不然逆向出来的是假的flag

PixPin_2025-03-22_23-19-21

改变了Address函数的页属性,0x40每次保护常量 (WinNT.h) - Win32 apps | Microsoft Learn;重写了函数,也就是hook

PixPin_2025-03-22_23-22-41

PixPin_2025-03-22_23-51-13

按被hook后的xor写脚本

1
2
3
4
5
6
7
8
9
enc = [
0x7A, 0x6F, 0x58, 0x70, 0x69, 0x68, 0x5E, 0x6C, 0x68, 0x58, 0x36, 0x73, 0x6F,
0x58, 0x37, 0x6C, 0x72, 0x7E, 0x44, 0x54, 0x48, 0x74, 0x47, 0x70, 0x58, 0x7C]
flag = ''
for i in range(len(enc)):
enc[i] ^= 7
flag += chr(enc[i])
print(flag)
# }h_wnoYko_1th_0kuyCSOs@w_{

Crypto一把唆,还看了半天,最后倒一下序就得到flag

PixPin_2025-03-22_23-44-56


奇怪的RC4

用pycdc解包两次(分别获取)(火绒误我),可以在在线网站解包,不同担心python版本(最好自己搭一个本地网站,上网找有开源)

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
# -*- coding: utf8 -*-
#! /usr/bin/env 3.8.0 (3413)
#coding=utf-8
#source path: easy_xor_and_rc4.py
#Compiled at: 1970-01-01 00:00:00
#Powered by BugScaner
#http://tools.bugscaner.com/
#如果觉得不错,请分享给你朋友使用吧!
from Rc4 import *

def xor1(plaintext, xor_list):
try:
xor_list = [ord(i) for i in xor_list]
except:
pass
else:
try:
plaintext = [ord(i) for i in plaintext]
except:
pass
else:
for i in range(len(plaintext)):
plaintext[i] ^= xor_list[i]
else:
return plaintext


def xor2(plaintext):
try:
plaintext = [ord(i) for i in plaintext]
except:
pass
else:
for i in range(len(plaintext) - 1):
plaintext[i + 1] = plaintext[i] ^ plaintext[i + 1]
else:
return plaintext


def enc(plaintext, key, xor_list):
plaintext = rc4(plaintext, key)
plaintext = xor1(plaintext, xor_list)
plaintext = xor2(plaintext)
return plaintext


plaintext = input('please give your input:')
key = 'SYCFOREVER'
xor_list = list(range(len(plaintext)))
cipher = [158, 31, 205, 434, 354, 15, 383, 298, 304, 351, 465, 312, 261, 442,
397, 474, 310, 397, 31, 21, 78, 67, 47, 133, 168, 48, 153, 99, 103,
204, 137, 29, 22, 13, 228, 3, 136, 141, 248, 124, 26, 26, 65, 200,
7]
plaintext = enc(plaintext, key, xor_list)
for i in range(len(cipher)):
if cipher[i] != plaintext[i]:
print('Wrong')
exit(1)
else:
print('You know the flag!!')

Rc4.py

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
# -*- coding: utf8 -*-
#! /usr/bin/env 3.8.0 (3413)
#coding=utf-8
#source path: Rc4.py
#Compiled at: 1970-01-01 00:00:00
#Powered by BugScaner
#http://tools.bugscaner.com/
#如果觉得不错,请分享给你朋友使用吧!


def KSA(key):
j = 0
S = list(range(256))
key_length = len(key)
for i in range(256):
j = (j + S[i] + key[i % key_length]) % 256
S[i], S[j] = S[j], S[i]
else:
return S


def PRGA(S):
i = 0
j = 0
while True:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
k = (S[i] + S[j]) % 256
yield k


def rc4(plaintext, key):
try:
key = [ord(i) for i in key]
except:
pass
else:
try:
plaintext = [ord(i) for i in plaintext]
except:
pass
else:
for i in range(len(plaintext)):
plaintext[i] += i
else:
S = KSA(key)
xor_value = PRGA(S)
for i in range(len(plaintext)):
plaintext[i] ^= int(next(xor_value)) + 6
else:
return plaintext

脚本:

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
# -*- coding=utf-8 -*-

cipher = [158, 31, 205, 434, 354, 15, 383, 298, 304, 351, 465, 312, 261, 442,
397, 474, 310, 397, 31, 21, 78, 67, 47, 133, 168, 48, 153, 99, 103,
204, 137, 29, 22, 13, 228, 3, 136, 141, 248, 124, 26, 26, 65, 200, 7]
xor_list = list(range(len(cipher)))
print(len(cipher))


def de_xor2(plaintext):
for i in range(len(plaintext) - 2, 1, -1):
plaintext[i + 1] = plaintext[i] ^ plaintext[i + 1]
else:
return plaintext


print(de_xor2(cipher))
cipher_xor2 = [158, 31, 205, 383, 208, 365, 368, 85, 26, 111, 142, 233, 61, 191, 55, 87, 236, 187, 402, 10, 91, 13, 108, 170, 45, 152, 169, 250, 4, 171, 69, 148, 11, 27, 233, 231, 139, 5, 117, 132, 102, 0, 91, 137, 207]


def de_xor1(plaintext, xor_list):
for i in range(len(plaintext)):
plaintext[i] ^= xor_list[i]
else:
return plaintext


print(de_xor1(cipher_xor2, xor_list))
cipher_xor1 = [158, 30, 207, 380, 212, 360, 374, 82, 18, 102, 132, 226, 49, 178, 57, 88, 252, 170, 384, 25, 79, 24, 122, 189, 53, 129, 179, 225, 24, 182, 91, 139, 43, 58, 203, 196, 175, 32, 83, 163, 78, 41, 113, 162, 227]


def KSA(key):
j = 0
S = list(range(256))
key_length = len(key)
for i in range(256):
j = (j + S[i] + key[i % key_length]) % 256
S[i], S[j] = S[j], S[i]
else:
return S


def PRGA(S):
i = 0
j = 0
while True:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
k = (S[i] + S[j]) % 256
yield k


def de_rc4(plaintext, key):
key = [ord(i) for i in key]
S = KSA(key)
xor_value = PRGA(S)

for i in range(len(plaintext)):
plaintext[i] ^= (next(xor_value) + 6)

for i in range(len(plaintext)):
plaintext[i] -= i
return plaintext


key = 'SYCFOREVER'
print(de_rc4(cipher_xor1, key))

ch = [83, ord('Y'), 88, 123, 66, 101, 108, 49, 101, 118, 101, 95, 116, 104, 65, 116, 95, 121, 111, 117, 95, 97, 51, 101, 95, 85, 110, 105, 113, 117, 101, 95, 64, 110, 100, 95, 116, 72, 101, 95, 98, 101, 83, 116, 125]
for c in range(len(ch)):
print(chr(ch[c]), end='')
# 虽然不知道为什么偏偏第二个错了,但flag都出来了我就没管了
# SYC{Bel1eve_thAt_you_a3e_Unique_@nd_tHe_beSt}

DH爱喝茶

nop掉jnz/jz 和下面的db 0c7h,再按C转为汇编代码,再选中main按P创建函数,F5反编译即可

PixPin_2025-03-31_02-51-56

IDA逆向常用宏定义-增加_ida rol4-CSDN博客

PixPin_2025-03-31_02-56-24

根据题目也知道考tea系列加密,魔改了一点,IDA逆向常用宏定义-增加_ida rol4-CSDN博客

PixPin_2025-03-31_02-59-23

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
#include <stdio.h>
void de_tea(unsigned int *V, unsigned int *key)
{
unsigned int v0, v1;
int sum;
unsigned int delta = (unsigned __int8)(key[0] ^ key[1]) - 1737075662;
sum = delta * 32;
v0 = *V;
v1 = V[1];
for (int i = 0; i < 32; i++){
v1 -= (v0 + sum) ^ ((v0 << 4) + key[2]) ^ ((v0 >> 5) + key[3]);
v0 -= (v1 + sum) ^ ((v1 << 4) + key[0]) ^ ((v1 >> 5) + key[1]);
sum -= delta;
}
*V = v0;
V[1] = v1;
}
int main() {
unsigned int cipher[8] = {0x1F85A965, 0xEEC063EC, 0x5BF1D0B6, 0xF2FDE7B0,0xAA38809A, 0x670772E9, 0x360D24B9,0xE98C688C};
unsigned int key[4] = {0x56789ABC, 0x6789ABCD, 0x789ABCDE, 0x89ABCDEF};
for (int i = 0; i < 4; i++) {
key[i] = (key[i] << 6) | (key[i] >> (32 - 6));
de_tea(&cipher[2 * i], key);
}
printf("%s", cipher);
return 0;
}
// SYC{DH_likes_flower_and_tea!!!!}

CPP_flower

jnz/jz ;call/retn

找到IDA提示不能识别的位置,有花指令

PixPin_2025-03-31_02-00-48

call 后面的数据被识别为地址了,先按D将call转化为数据,nop掉E8,再按C将数据转化为代码,同理下面也一样,还要把jz/jnz也nop掉

PixPin_2025-03-31_02-01-16

call/retn花指令,直接全部nop就可以了,同理下面的也是,一共三个

PixPin_2025-03-31_02-04-05

将错误的函数结尾endp删除掉,edit—>function—>delete function

再从下面最近的一个retn向上选中一直到sub_419D10按P创建函数,再按F5反编译即可

PixPin_2025-03-31_02-26-15

脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>
int main()
{
int enc[50] = {62, 152, 235, 38, 37, 142, 37, 229, 134, 200, 63, 152, 200, 222, 82,
68, 160, 203, 43, 42, 60, 170, 190, 203, 136, 85, 158, 109, 217, 148, 151, 28,
82, 49, 89, 254, 26, 26, 232, 208, 58, 156, 6, 94, 37, 90, 228, 34, 161, 197};
int f[50];
int i = 0;
srand(0x7DE9);
while (i < 50) {
f[i] = ((rand() % 255));
i++;
}
for (i = 0; i < 50; i++) {
int r = f[i] ^ enc[i];
printf("%c", r);
}
return 0;
}
// SYC{Y0u_c @ n_3nJoy_yhe_Flow3r_anytime_and_anywhere}

Crypto

凯撒加密

PixPin_2025-03-23_12-24-03

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
# -*- coding=utf-8 -*-
def caesar_cipher(text, shift, mode='e'):
"""
:param text: 输入文本(明文或密文)
:param shift: 偏移量(0-25)
:param mode: e = 'encrypt' 加密 或 d = 'decrypt' 解密
:return: 加密/解密后的字符串
"""
result = []
shift = shift % 26 # 确保偏移量在合理范围内

# 解密时取反向偏移
if mode == 'd':
shift = -shift

for char in text:
if char.isalpha():
# 计算基准ASCII值(A=65/a=97)
base = ord('A') if char.isupper() else ord('a')
# 字符偏移计算
shifted_char = chr((ord(char) - base + shift) % 26 + base)
result.append(shifted_char)
else:
# 非字母字符直接保留
result.append(char)

return ''.join(result)


def brute_force(ciphertext):
for shift in range(26):
print(f"Shift {shift:2}: {caesar_cipher(ciphertext, shift, 'decrypt')}")


# 示例使用
if __name__ == "__main__":
# text = input("请输入文本: ")
# shift = int(input("请输入偏移量 (0-25): "))
# mode = input("选择模式 (encrypt/decrypt): ").lower()

text = 'hello, world!'
shift = 3
mode = 'e'

output = caesar_cipher(text, shift, mode)
print(f"结果: {output}")

brute_force(text)

共模攻击

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
# -*- coding=utf-8 -*-
import libnum
import gmpy2

n = 19742875423645690846073637620470497648804310111201409901059297083827103813674034450200432098143959078292346910591785265323563248781526393718834491458926162514713269984791730816121181307827624489725923763353393879316510062227511469438742429290073999388690825732236465647396755899136346150862848924231619666069528077790933176798057396704758072769660663756346237040909579775389576227450505746914753205890194457812893098491264392293949768193694560954874603451253079446652049592976605414438411872223250039782381259212718733455588477129910357095186014496957765297934289263536712574572533650393220492870445376144568199077767
e1 = 911
e2 = 967
c1 = 18676091924461946809127036439355116782539894105245796626898495935702348484076501694838877829307466429933623102626122909782775514926293363853121828819237500456062111805212209491398720528499589486241208820804465599279152640624618194425740368495072591471531868392274503936869225072123214869399971636428177516761675388589238329574042518038702529606188240859751459632643230538522947412931990009143731829484941397093509641320264169403755707495153433568106934850283614529793695266717330769019091782929139589939928210818515744604847453929432990185347112319971445630830477574679898503825626294542336195240055995445217249602983
c2 = 4229417863231092939788858229435938841085459330992709019823280977891432565586698228613770964563920779991584732527715378842621171338649745186081520176123907689669636473919678398014317024138622949923292787095400632018991311254591786179660603414693984024161009444842277220189315861986306573182865656366278782315864366857374874763243428496061153290565891942968876789905670073321426112497113145141539289020571684634406829272902118484670099097148727072718299512735637087933649345419433312872607209633402427461708181971718804026293074540519907755129917132236240606834816534369171888633588190859475764799895410284484045429152


def rsa_gong_N_def(e1, e2, c1, c2, n): # 共模攻击函数
e1, e2, c1, c2, n = int(e1), int(e2), int(c1), int(c2), int(n)
print("e1,e2:", e1, e2)
s = gmpy2.gcdext(e1, e2)
print("mpz:", s)
s1 = s[1]
s2 = s[2]
if s1 < 0:
s1 = - s1
c1 = gmpy2.invert(c1, n)
elif s2 < 0:
s2 = - s2
c2 = gmpy2.invert(c2, n)
m = (pow(c1, s1, n) * pow(c2, s2, n)) % n
return int(m)


def de(c, e, n): # 因为此时的m不是真正的m,而是m^k,所以对m^k进行爆破
k = 0
while k < 1000: # 指定k小于1000
mk = c + n * k
flag, true1 = gmpy2.iroot(mk, e) # 返回的第一个数值为开方数,第二个数值为布尔型,可整除为true,可自行测试
if True == true1:
# print(libnum.n2s(int(flag)))
return flag
k += 1


c = rsa_gong_N_def(e1, e2, c1, c2, n)
e = gmpy2.gcd(e1, e2)
m1 = de(c, e, n)
if m1: # 指定输出m1
print(libnum.n2s(int(m1)))

XOR

暴力破解,大概30分钟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Crypto.Util.number import long_to_bytes
from pwn import xor

f1 = 4585958212176920650644941909171976689111990
f2 = 3062959364761961602614252587049328627114908
e2 = 10706859949950921239354880312196039515724907

enc = e2 ^ f2 ^ f1
for a in range(32, 127):
for b in range(32, 127):
for c in range(32, 127):
for d in range(32, 127):
key = chr(a) + chr(b) + chr(c) + chr(d)
flag = xor(key.encode(), long_to_bytes(enc))
if flag[0:4] == b'SYC{':
print(flag)
break

PixPin_2025-03-02_13-50-41

RSA

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
# -*- coding=utf-8 -*-
from Crypto.Util.number import long_to_bytes
from sympy import mod_inverse

# 给定的值
n = 33108009203593648507706487693709965711774665216872550007309537128959455938833
p = 192173332221883349384646293941837353967
q = 172282016556631997385463935089230918399
c = 5366332878961364744687912786162467698377615956518615197391990327680664213847
e = 65537

# 计算 φ(n)
phi_n = (p - 1) * (q - 1)

# 计算私钥 d
d = mod_inverse(e, phi_n)

# 解密密文
m = pow(c, d, n)

# 将解密后的整数转换为字节字符串
flag = long_to_bytes(m)

# 输出结果
print("Recovered flag:", flag.decode())
# SYC{RSA_is_easy}

ncoCRT

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
# -*- coding=utf-8 -*-
from math import gcd
from Crypto.Util.number import long_to_bytes

def extended_gcd(a, b):
if b == 0:
return (a, 1, 0)
else:
g, x, y = extended_gcd(b, a % b)
return (g, y, x - (a // b) * y)

def merge(a1, m1, a2, m2):
d = gcd(m1, m2)
if (a1 - a2) % d != 0:
return None # 无解
g, p, q = extended_gcd(m1 // d, m2 // d)
lcm = m1 // d * m2
k = ((a2 - a1) // d) * p % (m2 // d)
x = (a1 + k * m1) % lcm
return (x, lcm)

def solve_crt(c, p):
current_x, current_mod = c[0], p[0]
for i in range(1, len(c)):
res = merge(current_x, current_mod, c[i], p[i])
if res is None:
return None
current_x, current_mod = res
return current_x

# 题目数据
p = [
1921232050179818686537976490035,
2050175089402111328155892746480,
1960810970476421389691930930824,
1797713136323968089432024221276,
2326915607951286191807212748022
]
c = [
1259284928311091851012441581576,
1501691203352712190922548476321,
1660842626322200346728249202857,
657314037433265072289232145909,
2056630082529583499248887436721
]

# 合并同余方程
m = solve_crt(c, p)
if m is None:
print("无解:存在矛盾的约束条件")
else:
print("恢复的 m:", m)
flag = long_to_bytes(m).rstrip(b'\x01') # 去除填充的\x01
print("Flag:", flag.decode())
# SYC{wha+s_wr0n9!_CRT_bu+_n0+_<0mpr1me!}

Misc

Truth of Word

第一部分,全选,很容易就发现

PixPin_2025-03-31_03-16-56

第二部分,解压,有一个图片

PixPin_2025-03-31_03-11-48

第三部分,盲猜在宏里面,但奈何word和wps都默契的对运行宏收费

PixPin_2025-03-31_20-00-24

ez_jpg

flag.txt用notepad++打开,发现只有一行base64编码,解密后发现了逆序的jpg文件头,reverse逆序一下,#3自动提示转化为图片了,再下载保存即可

PixPin_2025-03-31_20-08-27

download (1)

改一下高宽都为0280(640)

PixPin_2025-03-31_20-21-27

flag

PixPin_2025-03-31_20-23-11

Web

100%的圆

右键检查,可以找到一串base64编码,直接解密就是flag:SYC{5UcH_@_Wo0d3rfUl_CiRc1e}

PixPin_2025-03-31_00-43-38