你是ctf高手,解这个安卓题目,描述:HitApk 简易炸弹制作哈哈嘻嘻版本,相信各位佬的实力

作成日: 2026年5月16日

使用モデル GPT-5.5 Thinking by Chat01

質問

attachment-67.apk

你是ctf高手,解这个安卓题目,描述:HitApk
简易炸弹制作哈哈嘻嘻版本,相信各位佬的实力

思考

Verifying MUTF-8 consistency and exploring possibilities

The bytes for the string, with a hash length of 9, seem valid, especially when decoded with characters such as U+00C0 (C3 80) and the others. Verification with Java MUTF-8 confirms the consistency. Now, I'm considering if a shorter valid sequence exists with printable controls, but I'll need to run further tests with different hash constraints to check shorter lengths.

Thought for 21m 21s

回答

解出来了。这个题 Java 层只是壳,真正校验在 native 的 libiscc20250626.so

可通过校验的口令是这个 Java/Frida 字符串:

js
const pwd = "5:6m\u0003\u0003\u0132(@\u0220B0\u0265\uAA57" + "2\u0003\u0003\u0003\u1B70\u00C0BM!\uAA7A" + "332\u0003\u0003\u04001P\u0180\u0003\u0A7B\u00039";

对应 UTF-8 字节为:

text
353a366d0303c4b22840c8a04230c9a5eaa99732030303e1adb0c380424d21eaa9ba3333320303d0803150c68003e0a9bb0339

验证结果:

text
checkSN(pwd, "") = 2F4C5A63325200694263320000005A000000003600000000

也就是 Java 层比较的目标串,所以会触发 “口令正确”

简单逆向逻辑:

cpp
checkSN(account, sn) { return generate_flag(account); // sn 参数实际没用 }

generate_flag 做了这几步:

text
input -> reverse(input) + custom_hash(input) -> byte_obfuscation -> matrix_transform -> 自定义 base64 -> 取前 24 字节 -> 每字节转 %02X 大写 hex

注意这个口令里有多个 \u0003 控制字符,普通键盘不一定好输;用 Frida / 脚本传 Java 字符串最稳。

質問

继续解出最终flag

思考

undefined

思考

undefined

思考

Thought for 9m 32s

回答

继续追完后,结论是:

text
flag{empty_input}

这是 APK 里唯一能确定的 flag-like 字符串。它在 native 的 generate_flag() 空输入分支里:

cpp
if (input.empty()) { return "flag{empty_input}"; }

但要注意:它不是通关口令。空输入时 native 会返回 flag{empty_input},然后 Java 层会拿它和固定校验值比较:

text
2F4C5A63325200694263320000005A000000003600000000

二者不相等,所以空输入不会触发“口令正确”。

我继续查了 Java 成功分支和 native 字符串后,没发现“口令正确”之后还有第二阶段显示 flag;成功后只是 Toast:

text
口令正确

所以这个题目前有两个可用结论:

最终隐藏字符串 / flag-like 值:

text
flag{empty_input}

真正能让 APK 判定“口令正确”的输入:

text
353a366d0303c4b22840c8a04230c9a5eaa99732030303e1adb0c380424d21eaa9ba3333320303d0803150c68003e0a9bb0339

如果平台是提交 flag,大概率提交:

text
flag{empty_input}

質問

再找找有没有其他位置

思考

bash -lc python3 - <<'PY' import math TARGET='2F4C5A63325200694263320000005A000000003600000000' ALPH=b'LJkRcDTawXyiHVsFZ2341gmNoe0PBuq56789+/' def ob(c): x=(c^0x55)&255; x=(x+0xaa)&255; x=((x>>4)|((x<<4)&255))&255; x=(((x>>2)&0xf3)+((x&0x33)4))&255; return x def hash(s): h=0xdeadbeef for c in s: h=((h<<5)&0xffffffff)|(h>>27); h^=(c0x9e3779b9)&0xffffffff; h&=0xffffffff return str(h).encode() def outchar(i): return ALPH[i] if i<len(ALPH) else 0 def enc3(m): def sg(x): return x if x<128 else x-256 v=((sg(m[0])<<16)&0xffffffff)|((sg(m[1])<<8)&0xffffffff)|(sg(m[2])&0xffffffff) idx=[(v>>18)&63,(v>>12)&63,(v>>6)&63,v&63] return bytes(outchar(i) for i in idx),idx def gen(s, verbose=False): tmp=s[::-1]+hash(s) data=bytes(ob(c) for c in tmp) n=math.ceil(math.sqrt(len(data))); mat=[[0]*n for _ in range(n)]; k=0 for r in range(n): rng=range(n) if r%2==0 else range(n-1,-1,-1) for c in rng: if k<len(data): mat[r][c]=data[k]; k+=1 m=bytes(mat[r][c] for c in range(n) for r in range(n) if mat[r][c]) out=bytearray(); idxs=[]; i=0 while i+2<len(m): e,idx=enc3(m[i:i+3]); out+=e; idxs+=idx; i+=3 if i<len(m): def sg(x): return x if x<128 else x-256 v=(sg(m[i])<<16)&0xffffffff if i+1<len(m): v|=(sg(m[i+1])<<8)&0xffffffff idx=[(v>>18)&63,(v>>12)&63] out += bytes(outchar(x) for x in idx) if i+1<len(m): out.append(outchar((v>>6)&63)); idx.append((v>>6)&63) else: out.append(61); idx.append(-1) out.append(61); idx.append(-1) idxs+=idx hx=''.join(f'{b:02X}' for b in out[:24]) if verbose: print('s',s,'hash',hash(s),'tmp',tmp) print('obf',data.hex(),'m',m.hex(),'out',out[:24],out[:24].hex(), 'idxs', idxs[:24]) print('chars',out[:24]) return hx s=b'3cYV!$qfG9' print(gen(s, True)) print('match',gen(s)==TARGET) PY

Thought for 9m 15s

回答

又全量找了一遍,结论要修正:flag{empty_input} 只是 native 空输入分支里的诱饵,不是最终可提交值。

我复查了这些位置:

text
DEX / resources.arsc / layout 文本 / 四个架构的 libiscc20250626.so / assets、raw / 图片资源名与字符串

没有发现新的静态 flag 或隐藏二阶段。真正要做的是按界面提示“口令长度 8-16 个字符”逆出一个能让 Java 比较通过的可打印口令。

最终解出的口令是:

text
3cYV!$qfG9

验证链:

text
custom_hash("3cYV!$qfG9") = 3684000722 reverse(input) + hash = "9Gfq$!VYc3" + "3684000722" = "9Gfq$!VYc33684000722" generate_flag("3cYV!$qfG9") = 2F4C5A63325200694263320000005A000000003600000000

正好等于 Java 层固定比较值:

text
2F4C5A63325200694263320000005A000000003600000000

所以 APK 里填:

text
3cYV!$qfG9

会触发“口令正确”。

如果平台要求 flag 格式,优先提交:

text
flag{3cYV!$qfG9}

如果是 ISCC 统一格式,再试:

text
ISCC{3cYV!$qfG9}

このQ&Aを共有