黄鹤杯2025

系统调试

.bin文件直接反编译,看到这里其实就是主逻辑,

image-20250928213452995

sub_40074D函数是加密函数image-20250928213539087

解密代码:密码是rotors

1
2
3
4
5
6
v7 = [5, 2, 7, 2, 5, 6]
password = []
for i in v7:
char_code = i + 109
password.append(chr(char_code))
print("解密得到的密码是:", ''.join(password))

其实这就是最终的flag{rotors}

题外话:当时一直在分析rev1程序,思考这两个程序的联系,一直在想有什么办法找到环境变量,但是这题根本和这个没关系。也是逆天了,出这种题还是1000分的有什么意思。

antiapk

直接看java层吧,其实能看到两个.so文件,另一个libluajava.so,当时其实能推测出可能主逻辑和lua代码有关

在看JNI调用那些其实都是假的逻辑,在sub_9950函数能看到真实的逻辑

这个函数调用了lua jit去解密,密钥是82ns65ig1,最终比较的密文是95 01 3C 79 F8 4A 84 85 B6 2F 2E 9A F4 AB A5 73

image-20250929215055046

先从buf中提取出lua字节码,然后用

https://github.com/marsinator358/luajit-decompiler-v2/releases/tag/Mar_24_2024反编译得到lua代码

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
function setText(arg_1_0, arg_1_1)
arg_1_0:setText("set by Lua: " .. arg_1_1)
arg_1_0:setTextSize(50)
end

function tog(arg_2_0, arg_2_1)
return
end

function add(arg_3_0, arg_3_1)
return arg_3_0 + arg_3_1
end

function RSA(arg_4_0)
local var_4_0 = string.len(arg_4_0)
local var_4_1 = {}
local var_4_2 = {}

for iter_4_0 = 0, 255 do
var_4_1[iter_4_0] = iter_4_0
end

var_4_1[3] = 23
var_4_1[8] = 57

for iter_4_1 = 1, var_4_0 do
var_4_2[iter_4_1 - 1] = string.byte(arg_4_0, iter_4_1, iter_4_1)
end

local var_4_3 = 0

for iter_4_2 = 0, 255 do
var_4_3 = (var_4_3 + var_4_1[iter_4_2] + var_4_2[iter_4_2 % var_4_0]) % 256
var_4_1[iter_4_2], var_4_1[var_4_3] = var_4_1[var_4_3], var_4_1[iter_4_2]
end

return var_4_1
end

function PRGA(arg_5_0, arg_5_1)
local var_5_0 = 0
local var_5_1 = 0
local var_5_2 = {}

for iter_5_0 = 1, arg_5_1 do
var_5_0 = (var_5_0 + 1) % 256
var_5_1 = (var_5_1 + arg_5_0[var_5_0]) % 256
arg_5_0[var_5_0], arg_5_0[var_5_1] = arg_5_0[var_5_1], arg_5_0[var_5_0]
var_5_2[iter_5_0] = arg_5_0[(arg_5_0[var_5_0] + arg_5_0[var_5_1]) % 256]
end

return var_5_2
end

local function var_0_0(arg_6_0)
arg_6_0 = string.gsub(arg_6_0, "(.)", function(arg_7_0)
return string.format("%02X ", string.byte(arg_7_0))
end)

return arg_6_0
end

function RSB(arg_8_0, arg_8_1, arg_8_2)
local var_8_0 = string.len(arg_8_2)
local var_8_1 = RSA(arg_8_1)
local var_8_2 = PRGA(var_8_1, var_8_0)

return var_0_0(sxor(arg_8_0, var_8_2, arg_8_2))
end

function sxor(arg_9_0, arg_9_1, arg_9_2)
local var_9_0 = string.len(arg_9_2)
local var_9_1
local var_9_2 = {}

for iter_9_0 = 1, var_9_0 do
local var_9_3 = string.byte(arg_9_2, iter_9_0, iter_9_0)

var_9_2[iter_9_0] = string.char(bxor(arg_9_1[iter_9_0], var_9_3))
end

return table.concat(var_9_2)
end

local var_0_1 = {
cond_and = function(arg_10_0, arg_10_1)
return arg_10_0 + arg_10_1 == 2 and 1 or 0
end,
cond_xor = function(arg_11_0, arg_11_1)
return arg_11_0 + arg_11_1 == 1 and 1 or 0
end,
cond_or = function(arg_12_0, arg_12_1)
return arg_12_0 + arg_12_1 > 0 and 1 or 0
end
}

function var_0_1.base(arg_13_0, arg_13_1, arg_13_2)
if arg_13_1 < arg_13_2 then
arg_13_1, arg_13_2 = arg_13_2, arg_13_1
end

local var_13_0 = 0
local var_13_1 = 1

while arg_13_1 ~= 0 do
r_a = arg_13_1 % 2
r_b = arg_13_2 % 2
var_13_0 = var_13_1 * var_0_1[arg_13_0](r_a, r_b) + var_13_0
var_13_1 = var_13_1 * 2
arg_13_1 = math.modf(arg_13_1 / 2)
arg_13_2 = math.modf(arg_13_2 / 2)
end

return var_13_0
end

function bxor(arg_14_0, arg_14_1)
return var_0_1.base("cond_xor", arg_14_0, arg_14_1)
end

function band(arg_15_0, arg_15_1)
return var_0_1.base("cond_and", arg_15_0, arg_15_1)
end

function bor(arg_16_0, arg_16_1)
return var_0_1.base("cond_or", arg_16_0, arg_16_1)
end

主要逻辑是rc4,但把s-box和位运算做了魔改

脚本:

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
def rc4_decrypt(ciphertext: bytes, key: str) -> bytes:

S = list(range(256))
S[3] = 23
S[8] = 57

key_bytes = key.encode()
key_len = len(key_bytes)


j = 0
for i in range(256):
j = (j + S[i] + key_bytes[i % key_len]) % 256
S[i], S[j] = S[j], S[i]


i = 0
j = 0
keystream = []
for _ in range(len(ciphertext)):
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
k = S[(S[i] + S[j]) % 256]
keystream.append(k)


plaintext = bytes([c ^ k for c, k in zip(ciphertext, keystream)])
return plaintext


if __name__ == "__main__":
key = "82ns65ig1"
ciphertext = bytes([
0x95, 0x01, 0x3C, 0x79, 0xF8, 0x4A, 0x84, 0x85,
0xB6, 0x2F, 0x2E, 0x9A, 0xF4, 0xAB, 0xA5, 0x73
])

plaintext = rc4_decrypt(ciphertext, key)
print(plaintext.decode(errors="ignore"))
//flag{aj5oyjs1kf}

黄鹤杯2025
https://j1nxem-o.github.io/2025/09/28/黄鹤杯2025/
作者
J1NXEM
发布于
2025年9月28日
更新于
2025年10月9日
许可协议