这是ctf逆向题目,帮我逆向分析这个apk文件,并给我详细的解题步骤和解题脚本,如果需要爆破就给我爆...
Criado em: 16 de maio de 2026
Respondido usando GPT-5.5 Thinking por Chat01
Criado em: 16 de maio de 2026
Respondido usando GPT-5.5 Thinking por Chat01
这是ctf逆向题目,帮我逆向分析这个apk文件,并给我详细的解题步骤和解题脚本,如果需要爆破就给我爆破脚本,要通用脚本
结论:这个 APK 的正确口令是:
text3cHk0n^VW9
用还原脚本验证后,输出正好等于 APK 里 Java 层硬编码的目标串:
text2F4C5A63325200694263320000005A000000003600000000
我也把脚本整理好了:
APK 解包后能看到多个 dex 和 native so:
bashunzip attachment-67.apk -d apk67 find apk67 -maxdepth 3 -type f | grep -E 'dex|so$'
关键 native 库:
textlib/arm64-v8a/libiscc20250626.so lib/armeabi-v7a/libiscc20250626.so lib/x86/libiscc20250626.so lib/x86_64/libiscc20250626.so
Java 层核心逻辑很薄,MainActivity 加载 native 库:
javaSystem.loadLibrary("iscc20250626");
点击按钮时大致逻辑是:
javaaccount = editTextText.getText().toString().trim(); ret = checkSN(account, ""); if (ret.equals("2F4C5A63325200694263320000005A000000003600000000")) { Toast("口令正确"); }
所以真正校验在 JNI:
textJava_com_example_iscc20250626_MainActivity_checkSN
用下面命令看导出符号:
bashreadelf -Ws lib/x86_64/libiscc20250626.so | c++filt
能看到这些关键函数:
textJava_com_example_iscc20250626_MainActivity_checkSN generate_flag(...) custom_hash(...) byte_obfuscation(...) matrix_transform(...)
checkSN 的逻辑是:
cppinput = GetStringUTFChars(arg1); result = generate_flag(input); return NewStringUTF(result.c_str());
第二个参数虽然传进来了,但最终校验流程里没有实际参与。
pythondef custom_hash(bs): h = 0xdeadbeef for c in bs: h = rol32(h, 5) ^ ((c * 0x9e3779b9) & 0xffffffff) h &= 0xffffffff return str(h).encode()
pythontmp = reverse(account) + custom_hash(account)
例如正确口令:
textaccount = 3cHk0n^VW9 hash = 3624000722 tmp = 9WV^n0kHc33624000722
每个字节做三步变换:
pythonx = ((x ^ 0x55) + 0xaa) & 0xff x = ((x >> 4) | (x << 4)) & 0xff x = ((x >> 2) & 0x33) | ((x & 0x33) << 2)
设:
pythonn = ceil(sqrt(len(tmp)))
然后蛇形填矩阵:
text第 0 行:左 -> 右 第 1 行:右 -> 左 第 2 行:左 -> 右 ...
最后按列读取,跳过 0x00。
native 里有一个很关键的坑:
textLJkRcDTawXyiHVsFZ2341gmNoe0PBuq56789+/
这个 alphabet 长度只有 38,但编码时按 6-bit index 访问,也就是可能访问 0..63。
目标串里有大量:
text00
说明这里必须按“越界读到 0x00”来建模,否则反推不成立。
最后 native 只取编码结果前 24 字节,再转大写 hex。
运行我整理的 Python 脚本:
bashpython3 iscc_apk_keygen.py '3cHk0n^VW9'
输出:
textaccount: 3cHk0n^VW9 hash : 3624000722 s : b'9WV^n0kHc33624000722' hex : 2F4C5A63325200694263320000005A000000003600000000 ok : True
所以输入框填:
text3cHk0n^VW9
即可触发 口令正确。
直接爆破不可取,因为目标是 48 位 hex 字符串,输入长度未知。
我用的是约束反推:
目标 hex 先转回 24 字节:
text2F 4C 5A 63 32 52 00 69 42 63 32 00 00 00 5A 00 00 00 00 36 00 00 00 00
对应编码结果:
text/LZc2R\x00iBc2\x00\x00\x00Z\x00\x00\x00\x006\x00\x00\x00\x00
反推前 18 个 matrix 输出字节后,可以得到输入长度和 hash 位数约束。最终可行路径是:
textlen(account) = 10 len(custom_hash(account)) = 10 hash pattern = 36[h2][h3][h4][h5][h6]722
再结合每 3 字节一组的 base64 约束,把搜索空间大幅压缩,最后用 custom_hash 的可逆性质做 meet-in-the-middle 爆破。
编译并运行爆破脚本:
bashg++ -O3 -std=c++17 iscc_apk_bruteforce.cpp -o brute ./brute
输出:
textFOUND 3cHk0n^VW9 hash=3624000722 hex=2F4C5A63325200694263320000005A000000003600000000
text3cHk0n^VW9
APK 内部没有额外 flag{} 包装逻辑,校验的是这个口令本身。
这个结果不对,题解的输入限制为8-11位字符串,由数字和字母组成。重新分析给我解题步骤和解题脚本
你说得对,上次给出的 3cHk0n^VW9 不符合题目限制,属于“非字母数字碰撞”。按 8–11 位、仅数字/字母 重新约束后,可用输入为:
text3bdMQMB6G9
我还找到一个 11 位可用解:
text33cioUDmvG9
这题因为 native 里的自定义 base64 表越界,存在碰撞,所以解不唯一。
脚本:
解包:
bashunzip attachment-67.apk -d apk_out find apk_out -name "*.so"
核心库:
textlibiscc20250626.so
classes3.dex 里能提取到目标串:
text2F4C5A63325200694263320000005A000000003600000000
注意:直接用 strings 可能看到类似 02F4C...,那个前导 0 是 dex 字符串长度字节的伪影,不是目标串本身。真实 Java 字符串长度是 48。
Java 逻辑大致是:
javaString ret = checkSN(input, ""); if (ret.equals("2F4C5A63325200694263320000005A000000003600000000")) { // 口令正确 }
查看符号:
bashreadelf -Ws lib/x86_64/libiscc20250626.so | c++filt | grep -E "checkSN|generate|hash|matrix|obfuscation"
关键函数:
textJava_com_example_iscc20250626_MainActivity_checkSN generate_flag(...) custom_hash(...) byte_obfuscation(...) matrix_transform(...)
checkSN 只是把 Java 输入传给 generate_flag,第二个参数没有实际参与核心校验。
整体流程:
textinput -> reverse(input) -> 拼接 custom_hash(input) -> byte_obfuscation -> matrix_transform -> 自定义 base64 -> 取前 24 字节 -> 每字节转大写 hex
pythonh = 0xDEADBEEF for c in input: h = rol32(h, 5) ^ (c * 0x9E3779B9)
最后转十进制字符串拼到反转输入后面。
例如:
textinput = 3bdMQMB6G9 hash = 3624010722 merge = 9G6BMQMdb33624010722
每个字节:
pythonx = ((x ^ 0x55) + 0xAA) & 0xff x = ((x >> 4) | (x << 4)) & 0xff x = ((x >> 2) & 0x33) | ((x & 0x33) << 2)
长度为 L,矩阵边长:
pythonn = ceil(sqrt(L))
蛇形填充:
text第 0 行:左到右 第 1 行:右到左 第 2 行:左到右 ...
再按列读取,跳过 0x00。
对于有效解,L = 20,前 18 个输出位置对应原始 merge 位置:
python[0, 9, 10, 19, 1, 8, 11, 18, 2, 7, 12, 17, 3, 6, 13, 16, 4, 5]
这一步能把输入长度强约束到:
textlen(input) = 10, hash 为 10 位 或 len(input) = 11, hash 为 9 位
8、9 位在目标串约束下被排除。
native 里 alphabet 是:
textLJkRcDTawXyiHVsFZ2341gmNoe0PBuq56789+/
长度只有 38,但编码索引是 6 bit,也就是 0..63。
目标 hex 还原成字节后:
text/LZc2R\x00iBc2\x00\x00\x00Z\x00\x00\x00\x006\x00\x00\x00\x00
大量 00 正是越界索引产生的 NUL。爆破脚本里按“索引 >= 38 输出 0”建模。
对于 10 位输入,设:
textinput = a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 merge = a9 a8 a7 a6 a5 a4 a3 a2 a1 a0 h0 h1 ... h9
由目标串反推可得到:
texta0 = '3' a9 = '9' a1 ∈ {'b', 'c'} a8 ∈ {'G', 'W'} a7 ∈ {'6','F','V','Z','f','j','v','z'} hash 形如:36[2389][45][0-9][0-9][0-9]722
然后利用 hash 可逆:
cppstep(h, c) = rol32(h, 5) ^ (c * 0x9e3779b9) unstep(h, c) = ror32(h ^ (c * 0x9e3779b9), 5)
做 meet-in-the-middle:
text前半段正向算 hash state 后半段从目标 hash 反推 state state 相等时拼接验证完整 generate_flag
最终得到:
text3bdMQMB6G9
验证:
bashpython3 iscc_apk_verify.py 3bdMQMB6G9
输出核心部分:
textaccount: 3bdMQMB6G9 length : 10 hash : 3624010722 merged : b'9G6BMQMdb33624010722' hex : 2F4C5A63325200694263320000005A000000003600000000 ok : True
编译:
bashg++ -O3 -std=c++17 iscc_apk_bruteforce_alnum.cpp -o brute
运行:
bash./brute
输出:
textFOUND len=10 3bdMQMB6G9 hash=3624010722 FOUND len=11 33cioUDmvG9 hash=624030722
优先用这个:
text3bdMQMB6G9
另一个同样满足目标串的字母数字解:
text33cioUDmvG9