浙江省省赛2025决赛wp

封榜前rank11最终圆梦省一,感谢队友带飞

Crypto

简单数学题

给了p和q和与差,直接相加相减得到p和q,后面直接解rsa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from Crypto.Util.number import *
sum_pq = 15870713655456272818998868095126610389501417235762009793315127525027164306871912572802442396878309282140184445917718237547340279497682840149930939938364752
diff_pq = 836877201325346306269647062252443025692393860257609240213263622058769344319275021861627524327674665653956022396760961371531780934904914806513684926008590
e = 65537
c = 24161337439375469726924397660125738582989340535865292626109110404205047138648291988394300469831314677804449487707306159537988907383165388647811395995713768215918986950780552907040433887058197369446944754008620731946047814491450890197003594397567524722975778515304899628035385825818809556412246258855782770070
p=(sum_pq+diff_pq)//2
q=(sum_pq-diff_pq)//2
n=p*q
phi = (p-1)*(q-1)
d = inverse(e, phi)
m = pow(c, d, n)

print(long_to_bytes(m))
#b'DASCTF{ok_1+1_1s_ez_so_try2goldbachs}'

AES

cbc加密,给了key生成方法,密文和iv

md5生成key

Base64

直接大厨解密

brainRSA-1

对于第一个hint可以这样变换

后面可以通过coppersmith解出p

如果m<p,那么mod n和mod p等价,所以后面phi可以用p-1来替代,这样假设能求出flag,但是求不出q无法验证

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

n = 5896603616629976388681343986666930963006582028163928714145532756203237227730274711487003166019427105897965945120232260215410992579837484059292611073419586380525003988344574914431029827063806077732189986757809721088722985286036959879820708133146403409430195581622055470667222891220979774304289695570081825139756219411812254290165809196979501734849253222466549260913814647528326035080523330064602920600175311072318677328858357572118378221037611447468353988765857728693723209366913388728379914720462400719453373674212484217
e = 65537
c = 4221793406745090448159839492516441941495929321499567123472000442699162146372220161860225184236927618558037325910707307459975466187269605134233560519157016842737834824730633694720444565009425747524144994992594172304437752960220406217437575205487944977177237131899857115110278358933279454006801350405232784421011170963118967018535823980784090051685902395650907846245302374549113152846903151411198578675291462184512223571295652757739539096560166939391938607962013244587459259461981764347874215325536867806205636381420076263
h1 = 86682368885754481246073975629273374009118678612781484142142233010648189936701991035424570227832428961089155099087480306450429844896945882196738935354601757929219259054136973233403527177389212490240890908590489235856630191642125591379880282065718229563065862910111151238228617993062409710807546317753676418582796716091984804706084632849079312032895792218093262044435
h2 = 507302396516541130096005191243501282527086130677192353031178107965055262527263846570902262566241833462389458229472609073835612242905629592637875830766904


PR.<x> = PolynomialRing(Zmod(n))
f = x^3 - h1*x^2+x
p = int(f.small_roots(X=2^512,beta=0.4,epsilon=0.01)[1])
print(p)

d = inverse(e,p-1)
m = pow(c,d,p)
print(long_to_bytes(m))
# b'flag{V#@Ry_@ASSy_Pr013L@m-for_RsA!}'

Reverse

你是我的天命人吗

第一个函数是VirtualProtect自解密

第二个函数进入之后创建函数

输入先异或i再转成hex

直接逆向

1
2
3
4
5
s='4440514050437D3E386C3A3F3E6F386D232124202D7420742C2F2E282525782D124344471015405A'
print(len(s))
for i in range(0,len(s),2):
print(chr(int(s[i:i+2],16)^(i//2)),end="")
#DASCTF{90e042b6b30639a6c464398f22bfd40f}

androidtest

jadx反编译不完全,用jeb打开发现base32的表

so层下存在密文

先base32解密

比赛期间通过测试之后猜测再xor44能得到flag

1
2
3
4
a=[0x68 ,0x6d ,0x7f ,0x6f ,0x78 ,0x6a ,0x57 ,0x4d ,0x42 ,0x48 ,0x5e ,0x43 ,0x45 ,0x48 ,0x73 ,0x4d ,0x42 ,0x58 ,0x45 ,0x73 ,0x4d ,0x42 ,0x48 ,0x73 ,0x58 ,0x49 ,0x5f ,0x58 ,0x73 ,0x45 ,0x5f ,0x73 ,0x45 ,0x42 ,0x58 ,0x49 ,0x5e ,0x49 ,0x5f ,0x58 ,0x45 ,0x42 ,0x4b ,0x51]
for i in range(len(a)):
print(chr(a[i]^44),end="")
#DASCTF{android_anti_and_test_is_interesting}

交叉引用ret_str方法找到关键加密,Onclick里面先执行xor 44再base32加密后和密文比较

Warning-1

输入作为参数传入

调试的时候参数放到parameters里

trace xor

trace cmp

上述trace后得到如下内容

根据0x100猜测可能是rc4,第一步是魔改的地方,xor 下标

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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
XOR: 0x31 ^ 0x0 = 0x31
CMP: 0x2e == 0x1
XOR: 0x31 ^ 0x1 = 0x30
CMP: 0x2e == 0x2
XOR: 0x31 ^ 0x2 = 0x33
CMP: 0x2e == 0x3
XOR: 0x31 ^ 0x3 = 0x32
CMP: 0x2e == 0x4
XOR: 0x31 ^ 0x4 = 0x35
CMP: 0x2e == 0x5
XOR: 0x31 ^ 0x5 = 0x34
CMP: 0x2e == 0x6
XOR: 0x31 ^ 0x6 = 0x37
CMP: 0x2e == 0x7
XOR: 0x31 ^ 0x7 = 0x36
CMP: 0x2e == 0x8
XOR: 0x31 ^ 0x8 = 0x39
CMP: 0x2e == 0x9
XOR: 0x31 ^ 0x9 = 0x38
CMP: 0x2e == 0xa
XOR: 0x31 ^ 0xa = 0x3b
CMP: 0x2e == 0xb
XOR: 0x31 ^ 0xb = 0x3a
CMP: 0x2e == 0xc
XOR: 0x31 ^ 0xc = 0x3d
CMP: 0x2e == 0xd
XOR: 0x31 ^ 0xd = 0x3c
CMP: 0x2e == 0xe
XOR: 0x31 ^ 0xe = 0x3f
CMP: 0x2e == 0xf
XOR: 0x31 ^ 0xf = 0x3e
CMP: 0x2e == 0x10
XOR: 0x31 ^ 0x10 = 0x21
CMP: 0x2e == 0x11
XOR: 0x31 ^ 0x11 = 0x20
CMP: 0x2e == 0x12
XOR: 0x31 ^ 0x12 = 0x23
CMP: 0x2e == 0x13
XOR: 0x31 ^ 0x13 = 0x22
CMP: 0x2e == 0x14
XOR: 0x31 ^ 0x14 = 0x25
CMP: 0x2e == 0x15
XOR: 0x31 ^ 0x15 = 0x24
CMP: 0x2e == 0x16
XOR: 0x31 ^ 0x16 = 0x27
CMP: 0x2e == 0x17
XOR: 0x31 ^ 0x17 = 0x26
CMP: 0x2e == 0x18
XOR: 0x31 ^ 0x18 = 0x29
CMP: 0x2e == 0x19
XOR: 0x31 ^ 0x19 = 0x28
CMP: 0x2e == 0x1a
XOR: 0x31 ^ 0x1a = 0x2b
CMP: 0x2e == 0x1b
XOR: 0x31 ^ 0x1b = 0x2a
CMP: 0x2e == 0x1c
XOR: 0x31 ^ 0x1c = 0x2d
CMP: 0x2e == 0x1d
XOR: 0x31 ^ 0x1d = 0x2c
CMP: 0x2e == 0x1e
XOR: 0x31 ^ 0x1e = 0x2f
CMP: 0x2e == 0x1f
XOR: 0x31 ^ 0x1f = 0x2e
CMP: 0x2e == 0x20
XOR: 0x31 ^ 0x20 = 0x11
CMP: 0x2e == 0x21
XOR: 0x31 ^ 0x21 = 0x10
CMP: 0x2e == 0x22
XOR: 0x31 ^ 0x22 = 0x13
CMP: 0x2e == 0x23
XOR: 0x31 ^ 0x23 = 0x12
CMP: 0x2e == 0x24
XOR: 0x31 ^ 0x24 = 0x15
CMP: 0x2e == 0x25
XOR: 0x31 ^ 0x25 = 0x14
CMP: 0x2e == 0x26
XOR: 0x31 ^ 0x26 = 0x17
CMP: 0x2e == 0x27
XOR: 0x31 ^ 0x27 = 0x16
CMP: 0x2e == 0x28
XOR: 0x31 ^ 0x28 = 0x19
CMP: 0x2e == 0x29
XOR: 0x31 ^ 0x29 = 0x18
CMP: 0x2e == 0x2a
XOR: 0x31 ^ 0x2a = 0x1b
CMP: 0x2e == 0x2b
XOR: 0x31 ^ 0x2b = 0x1a
CMP: 0x2e == 0x2c
XOR: 0x31 ^ 0x2c = 0x1d
CMP: 0x2e == 0x2d
XOR: 0x31 ^ 0x2d = 0x1c
CMP: 0x2e == 0x2e
CMP: 0x100 == 0x1
CMP: 0x100 == 0x2
CMP: 0x100 == 0x3
…………………………………………
CMP: 0x100 == 0x100
XOR: 0x31 ^ 0x50 = 0x61
CMP: 0x1 == 0x2e
XOR: 0x30 ^ 0x67 = 0x57
CMP: 0x2 == 0x2e
XOR: 0x33 ^ 0x21 = 0x12
CMP: 0x3 == 0x2e
XOR: 0x32 ^ 0x2b = 0x19
CMP: 0x4 == 0x2e
XOR: 0x35 ^ 0xce = 0xfb
CMP: 0x5 == 0x2e
XOR: 0x34 ^ 0xd7 = 0xe3
CMP: 0x6 == 0x2e
XOR: 0x37 ^ 0x84 = 0xb3
CMP: 0x7 == 0x2e
XOR: 0x36 ^ 0x3a = 0xc
CMP: 0x8 == 0x2e
XOR: 0x39 ^ 0xf5 = 0xcc
CMP: 0x9 == 0x2e
XOR: 0x38 ^ 0xc2 = 0xfa
CMP: 0xa == 0x2e
XOR: 0x3b ^ 0xc2 = 0xf9
CMP: 0xb == 0x2e
XOR: 0x3a ^ 0x22 = 0x18
CMP: 0xc == 0x2e
XOR: 0x3d ^ 0x48 = 0x75
CMP: 0xd == 0x2e
XOR: 0x3c ^ 0x1d = 0x21
CMP: 0xe == 0x2e
XOR: 0x3f ^ 0x14 = 0x2b
CMP: 0xf == 0x2e
XOR: 0x3e ^ 0x2a = 0x14
CMP: 0x10 == 0x2e
XOR: 0x21 ^ 0x71 = 0x50
CMP: 0x11 == 0x2e
XOR: 0x20 ^ 0x25 = 0x5
CMP: 0x12 == 0x2e
XOR: 0x23 ^ 0xa7 = 0x84
CMP: 0x13 == 0x2e
XOR: 0x22 ^ 0x73 = 0x51
CMP: 0x14 == 0x2e
XOR: 0x25 ^ 0x3c = 0x19
CMP: 0x15 == 0x2e
XOR: 0x24 ^ 0x9c = 0xb8
CMP: 0x16 == 0x2e
XOR: 0x27 ^ 0x72 = 0x55
CMP: 0x17 == 0x2e
XOR: 0x26 ^ 0xfe = 0xd8
CMP: 0x18 == 0x2e
XOR: 0x29 ^ 0x3c = 0x15
CMP: 0x19 == 0x2e
XOR: 0x28 ^ 0xb7 = 0x9f
CMP: 0x1a == 0x2e
XOR: 0x2b ^ 0xad = 0x86
CMP: 0x1b == 0x2e
XOR: 0x2a ^ 0x80 = 0xaa
CMP: 0x1c == 0x2e
XOR: 0x2d ^ 0xa9 = 0x84
CMP: 0x1d == 0x2e
XOR: 0x2c ^ 0x6f = 0x43
CMP: 0x1e == 0x2e
XOR: 0x2f ^ 0x37 = 0x18
CMP: 0x1f == 0x2e
XOR: 0x2e ^ 0x46 = 0x68
CMP: 0x20 == 0x2e
XOR: 0x11 ^ 0x91 = 0x80
CMP: 0x21 == 0x2e
XOR: 0x10 ^ 0x32 = 0x22
CMP: 0x22 == 0x2e
XOR: 0x13 ^ 0xb4 = 0xa7
CMP: 0x23 == 0x2e
XOR: 0x12 ^ 0xf7 = 0xe5
CMP: 0x24 == 0x2e
XOR: 0x15 ^ 0xa5 = 0xb0
CMP: 0x25 == 0x2e
XOR: 0x14 ^ 0xad = 0xb9
CMP: 0x26 == 0x2e
XOR: 0x17 ^ 0xd8 = 0xcf
CMP: 0x27 == 0x2e
XOR: 0x16 ^ 0x6b = 0x7d
CMP: 0x28 == 0x2e
XOR: 0x19 ^ 0x35 = 0x2c
CMP: 0x29 == 0x2e
XOR: 0x18 ^ 0x8c = 0x94
CMP: 0x2a == 0x2e
XOR: 0x1b ^ 0xe4 = 0xff
CMP: 0x2b == 0x2e
XOR: 0x1a ^ 0x0 = 0x1a
CMP: 0x2c == 0x2e
XOR: 0x1d ^ 0x20 = 0x3d
CMP: 0x2d == 0x2e
XOR: 0x1c ^ 0x2e = 0x32
CMP: 0x2e == 0x2e

按照思路直接写脚本,发现就是flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
key=[0x50, 0x67, 0x21, 0x2b, 0xce, 0xd7, 0x84, 0x3a, 0xf5, 0xc2,
0xc2, 0x22, 0x48, 0x1d, 0x14, 0x2a, 0x71, 0x25, 0xa7, 0x73,
0x3c, 0x9c, 0x72, 0xfe, 0x3c, 0xb7, 0xad, 0x80, 0xa9, 0x6f,
0x37, 0x46, 0x91, 0x32, 0xb4, 0xf7, 0xa5, 0xad, 0xd8, 0x6b,
0x35, 0x8c, 0xe4, 0x0, 0x20, 0x2e]

enc=[0x14,0x27,0x70,0x6B,0x9E,0x94,0xF9,0x51,0x8E,0x9F,
0xB2,0x51,0x69,0x73,0x2F,0x46,0x53,0x5,0xDC,0x36,
0x69,0xA4,0x3,0x86,0x4B,0xC4,0xC6,0xD5,0xE6,0x5F,
0x50,0x37,0xF7,0x5C,0xC6,0x86,0xF9,0xA5,0xCA,0x74,
0x24,0xCC,0xBA,0x7E,0x64,0x7E]

flag=''
for i in range(len(enc)):
tmp=enc[i]^i^key[i]
flag+=chr(tmp)
print(flag)
# DASCTF{lsTzx-c5c21iVA-goojqNS-ynFOPRx-489itUh}

正常做法应该写脚本模拟所有操作并trace,这里小小猜测一手,算是特解

信创安全

X1-1

两次解包hap文件得到moudles.abc文件,反编译后找到Index类,发现关键加密

arg0是input,arg1是3*3矩阵,arg2是theSecondKey,arg3是arc4Key

分别进行矩阵加密(输入和arg1),异或,rc4,自定义码表的 base64

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
import base64
from Crypto.Cipher import ARC4
import numpy as np

cipher = "37L9UF8uNl1TSgYMLIW/RosGPMxVYXNcUoTTQXihX8ZyaQVgxY9Ywz/0fIwRzI4H"
key2 = bytes([90, 60, 231, 145, 47])
rc4_key = b"HarMonyOS_S3cur3_K3y!2025"

custom_b64 = "3GHIJKLMzxy01245PQRSTUFabcdefghijklmnopqrstuv6789+/NOVWXYZABCDEw"
std_b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
translate_table = str.maketrans(custom_b64, std_b64)


def custom_b64_decode(s):
std_s = s.translate(translate_table)
return base64.b64decode(std_s)

def rc4_decrypt(data, key):
cipher = ARC4.new(key)
return cipher.decrypt(data)

def xor_decrypt(data, key):
result = bytearray()
for i, byte in enumerate(data):
result.append(byte ^ key[i % len(key)])
return bytes(result)


def extended_gcd(a, b):
"""扩展欧几里得算法,返回 (gcd, x, y) 使得 ax + by = gcd(a, b)"""
if a == 0:
return b, 0, 1
gcd, x1, y1 = extended_gcd(b % a, a)
x = y1 - (b // a) * x1
y = x1
return gcd, x, y


def mod_inverse(a, m):
"""计算 a 在模 m 下的逆元"""
gcd, x, _ = extended_gcd(a, m)
if gcd != 1:
raise ValueError(f"{a} 在模 {m} 下没有逆元")
return x % m


def matrix_inverse_mod(matrix, mod=256):
"""计算矩阵在模 mod 下的逆矩阵(使用伴随矩阵法)"""
n = len(matrix)

# 计算行列式(模 mod)
det = int(np.round(np.linalg.det(matrix))) % mod
if det == 0:
raise ValueError("矩阵行列式为0,不可逆")

# 计算行列式的模逆
det_inv = mod_inverse(det, mod)

# 计算伴随矩阵
adj = np.zeros((n, n), dtype=int)

for i in range(n):
for j in range(n):
# 计算代数余子式
# 删除第i行第j列
minor = np.delete(np.delete(matrix, i, axis=0), j, axis=1)
# 计算余子式
cofactor = (-1) ** (i + j) * int(np.round(np.linalg.det(minor)))
# 伴随矩阵是代数余子式矩阵的转置
adj[j][i] = cofactor % mod

# 计算逆矩阵:伴随矩阵 × 行列式逆元 (模 mod)
inv_matrix = (adj * det_inv) % mod
return inv_matrix

def matrix_decrypt(data):
M = [[1, 2, 3],
[0, 1, 4],
[0, 0, 1]]

inv_M = matrix_inverse_mod(M, 256)

decrypted = bytearray()
for i in range(0, len(data), 3):
block = data[i:i + 3]

# 矩阵乘法:P = M^{-1} * C (mod 256)
x, y, z = block
x_dec = (inv_M[0][0] * x + inv_M[0][1] * y + inv_M[0][2] * z) % 256
y_dec = (inv_M[1][0] * x + inv_M[1][1] * y + inv_M[1][2] * z) % 256
z_dec = (inv_M[2][0] * x + inv_M[2][1] * y + inv_M[2][2] * z) % 256
decrypted.extend([x_dec, y_dec, z_dec])

return bytes(decrypted)


step1 = custom_b64_decode(cipher)
step2 = rc4_decrypt(step1, rc4_key)
step3 = xor_decrypt(step2, key2)
step4 = matrix_decrypt(step3)
print("flag:",step4)
#DASCTF{H4rmOny0S_Mult1_L4y3r_Crypt0_M4st3r!}

也可以先base64和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
import numpy as np
import binascii
from sympy import Matrix

A = [
[1, 2, 3],
[0, 1, 4],
[0, 0, 1],
]

out = bytearray(binascii.unhexlify("5a3ce7e6671be974c53bc174b8b742cc6e9e62e00547c2fddcf763f889566ed2b8037923d4d3a1ba470813d11c71299a"))

xor_key = [90, 60, 231, 145, 47]

for i in range(len(out)):
out[i] ^= xor_key[i % len(xor_key)]

n = len(A)

cipther = np.frombuffer(bytes(out), dtype=np.uint8)

inv = Matrix(np.array(A, dtype=int)).inv_mod(256)

out = bytearray()
for block in cipther.reshape(-1, n):#-1 表示自动计算行数
plain = (inv @ block) % 256
out.extend(plain.astype(np.uint8))

print(out)

整体逻辑很清晰,就是比赛期间写不出矩阵相关代码🤔


浙江省省赛2025决赛wp
https://j1ya-22.github.io/2025/11/16/浙江省省赛2025决赛wp/
作者
j1ya
发布于
2025年11月16日
许可协议