MOBILE-深海金库
ISCC2026 WriteUp 提交模板
MOBILE-深海金库
解题思路
1.假逻辑

这一部分是个假逻辑,无论你输入什么点击后都是输出签名错误

追踪这个函数
内部逻辑:
updateState()每次点击将CLICK_TRIGGER递增prepareContext()检查两个条件,任一成立就直接 return:Debug.isDebuggerConnected()— 检测到调试器则静默退出CLICK_TRIGGER.get() < 3— 点击次数少于 3 次也退出
也就是说只有非调试环境 + 至少点击 3 次按钮后,才会真正调用 CoreProtocolDelegate.dispatchConfiguration(view, onLongClickListener)。


第一层校验,并不复杂


第二层是这个
接着看so层文件,找到对应so的函数是verify_bridge


具体实现要追踪SV函数:
核心函数 SV::v (0x222B0) 的验证逻辑:
- 拷贝输入数据
- 调用
process_fib_stream(dest, length)— 用 Fibonacci 流变换输入 - 调用
encode_b64(&n2, &dest_2)— base64 编码变换后的数据 - 将 base64 结果与硬编码字符串
"cUdltutGeWWIUm+7OE4ce3wi"比较
所以验证链为:
输入 → process_fib_stream → encode_b64 → 比较 "cUdltutGeWWIUm+7OE4ce3wi"
值得注意的是,base64是个自定义的。
运行脚本

ISCC{n2R#7_pQ!9vL*5mWnp}
Exp
from __future__ import annotations
import base64
import sys
import zipfile
from pathlib import Path
DEFAULT_BUNDLE = Path(__file__).with_name("app-release-01.apk")
SO_MEMBER = "lib/arm64-v8a/libsecure_verify.so"
OBFUSCATED_ALPHABET = "zKJUExRaVtM3Ydv5TQIsWD1frnHC78Lckl6euPh9AGoj0SgN4Zp+OwXi2F/ybmBq"
REFERENCE_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
TARGET_SEGMENTS = ("cUdltutGeWWI", "Um+7OE4ce3wi")
SYMBOL_REMAP = str.maketrans(OBFUSCATED_ALPHABET, REFERENCE_ALPHABET)
APK_FINGERPRINT = (
b"nativeVerify",
b"com/example/mobile02/MainActivity",
OBFUSCATED_ALPHABET.encode("ascii"),
*(segment.encode("ascii") for segment in TARGET_SEGMENTS),
)
def read_verifier_image(bundle_file: Path) -> bytes:
with zipfile.ZipFile(bundle_file) as archive:
return archive.read(SO_MEMBER)
def assert_expected_build(so_blob: bytes) -> None:
absent = [needle for needle in APK_FINGERPRINT if needle not in so_blob]
if absent:
detail = ", ".join(repr(needle.decode("ascii", errors="ignore")) for needle in absent)
raise ValueError(f"unexpected verifier image, missing markers: {detail}")
def decode_embedded_goal() -> bytes:
encoded_goal = "".join(TARGET_SEGMENTS)
normalized_goal = encoded_goal.translate(SYMBOL_REMAP)
return base64.b64decode(normalized_goal)
def peel_fibonacci_xor(ciphertext: bytes) -> bytes:
plain = bytearray()
left = 1
right = 1
for cipher_byte in ciphertext:
plain.append(cipher_byte ^ (left & 0xFF))
left, right = right, (left + right) & 0xFF
return bytes(plain)
def replay_java_stage(candidate_inner: str) -> bytes:
emitted = bytearray()
rolling = 0
for slot, raw_char in enumerate(candidate_inner.encode("ascii")):
rotated = ((raw_char - 25) % 95) + 32
bias = 8 if slot % 2 == 0 else -8
rolling = (rotated + bias + (rolling % 4)) & 0xFF
emitted.append(rolling)
return bytes(emitted)
def restore_inner_text(java_bytes: bytes) -> str:
recovered = bytearray()
previous_byte = 0
for slot, output_byte in enumerate(java_bytes):
bias = 8 if slot % 2 == 0 else -8
rotated = (output_byte - bias - (previous_byte % 4)) & 0xFF
original_byte = ((rotated - 32 - 7) % 95) + 32
recovered.append(original_byte)
previous_byte = output_byte
return recovered.decode("ascii")
def derive_flag(bundle_file: Path) -> str:
so_blob = read_verifier_image(bundle_file)
assert_expected_build(so_blob)
native_goal = decode_embedded_goal()
java_goal = peel_fibonacci_xor(native_goal)
inner_text = restore_inner_text(java_goal)
if replay_java_stage(inner_text) != java_goal:
raise ValueError("java stage round-trip check failed")
return f"ISCC{{{inner_text}}}"
def main() -> None:
bundle_file = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else DEFAULT_BUNDLE.resolve()
print(derive_flag(bundle_file))
if __name__ == "__main__":
main()

浙公网安备 33010602011771号