-
-
[原创]攻防世界 GFSJ0508「黑客精神」- 逆向分析
-
发表于: 2026-10-17 12:26 809
-
分析机为 macOS(自带 objdump 对 ARM Thumb-2 支持不完善,后面会踩坑),因此准备了一套 Python 逆向环境:
需要用到的核心 Python 库:
结论:
关键线索已经浮现:
这些就是原生函数的真实入口地址,后面反汇编就按这些地址切分函数。

包名 com.gdufs.xman,三个关键类:
MyApp(Application 子类)
MainActivity
RegActivity
push {r0, r1, ...} 在 ARM 指令集里根本不合法 → 说明这些函数是用 Thumb-2 编译的,但 objdump 当成 ARM 解码,整段错位。
由于 macOS 的 objdump 不支持 force-thumb,写 Python 脚本用 Capstone 解码:
反汇编结果片段(n2 开头):
至此确认 n2 就是一个 逐字节 XOR 加密函数:
静态区 .data 中保存了 JNINativeMethod 结构体数组(三要素:{方法名指针, 签名指针, 函数指针})。该数组在文件偏移 0x4004 处,用 Python 解析:
解析结果:
函数指针 (0x13b1/0x11f9/0x14cd) 末尾的 +1 是 ARM/Thumb 调用约定(bit0=1 表示 Thumb 代码)。
逻辑梳理(结合反汇编 + 字面池):
反汇编尾部(从 strcmp 开始,地址 0x1410 起):
字符串指针解析(PC 相对 + 字面池):
核心结论:initSN 把 reg.dat 读出来后,只要 strcmp(content, "EoPAoY62@ElRD") == 0,就把状态置为 m=1(注册成功)。也就是说,写进 reg.dat 的内容必须等于密文 EoPAoY62@ElRD。
work 仅根据 m 显示提示文本,不会直接把 flag 字符串回显——flag 就是能让你通过 initSN 校验的那个输入。
把第 2~6 步得到的关键常量汇总:
关系推导:
按 n2 的索引算法,对密文 EoPAoY62@ElRD(下标 i = 0..12)逐字节逆 XOR:
拼接明文:2 0 1 6 0 8 A m ! 2 3 3 3 → 201608Am!2333
注意:密钥字节 struct[idx] 在 i%3==0/1/2 时分别对应 w/_/a,因此密文每 3 字节循环出现 E o P/A o Y/6 2 @/E l R/D,与 EoPAoY62@ElRDEoP 的周期性完全吻合,进一步佐证算法正确。
用同样的算法正向加密,应当还原出密文:
加密 201608Am!2333 得到密文 EoPAoY62@ElRD,与 initSN 内置目标完全一致 → 注册码正确无误。
说明:原生层 strcmp 比较的是 13 字节密文,因此严格输入 201608Am!2333 即可完成注册;xman{} 是题目在 rodata 提示里指定的 flag 包装格式(输入即是flag,格式为xman{……}!)。
最终 Flag:xman{201608Am!2333}
RegActivity 输入 → saveSN(n2) 用密钥 W3_arE_whO_we_ARE\0 + 计数器 0x7e0 逐字节 XOR 加密 → 写入 /sdcard/reg.dat → 下次启动 initSN(n1) 读回并 strcmp 比对内置目标 EoPAoY62@ElRD → 相等则 m=1 → work(n3) 提示成功。反向 XOR_decrypt("EoPAoY62@ElRD") 即得能注册通过的内容 201608Am!2333,套用 xman{} 即最终 flag。
# 1. 建立隔离 venv(managed python)
VENV=~/.workbuddy/binaries/python/envs/default
python3 -m venv "$VENV"
"$VENV/bin/pip" install capstone pyelftools
# 2. 关键命令
jadx xman.apk # Java 层反编译
file / strings / nm / objdump # 原生库静态信息
python (capstone + pyelftools) # 精确的 Thumb-2 反汇编
$ file libmyjni.so xman.apk
libmyjni.so: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, stripped
xman.apk: Zip archive data (Android package)
$ strings -n 4 libmyjni.so
...
JNI_OnLoad
g_env
native_class
getValue / setValue / callWork
fopen / fread / fputs / strcmp / strlen
/sdcard/reg.dat
W3_arE_whO_we_ARE
EoPAoY62@ElRD
com.gdufs.xman
输入即是flag,格式为xman{……}!
打开文件失败
$ nm -D libmyjni.so
00001320 T getValue
00001364 T setValue
000013b0 T n1
000011f8 T n2
00001444 T callWork
000014cc T n3
00001508 T JNI_OnLoad
$ jadx -d jadx_out -j 4 xman.apk
public class MyApp extends Application {
public static int m; // 0=未注册 1=已注册 其他=状态异常
public static native void initSN();
public static native void saveSN(String sn);
public static native void work();
@Override
public void onCreate() {
super.onCreate();
initSN(); // 启动时读取并校验注册状态
}
}
// 按钮点击逻辑(整理后)
if (MyApp.m == 0) {
// 未注册 → 跳转到输入注册码界面
startActivity(new Intent(this, RegActivity.class));
} else {
work(); // 已注册 → 调用原生 work(),结果通过 MainActivity.work(String) 回调显示
}
// 原生 work() 内部会回调 MainActivity.work(String result)
// 用户在编辑框输入 sn,点确定:
MyApp.saveSN(et.getText().toString()); // 交给原生 saveSN 加密保存
// 然后杀掉进程(下次启动由 initSN 重新判定)
$ objdump -d libmyjni.so | head
000011f8 <n2>:
e92d 6803 push {r0, r1, r11, lr} # 这条在 ARM 模式下是错的
from elftools.elf.elffile import ELFFile
from capstone import Cs, CS_ARCH_ARM, CS_MODE_THUMB
elf = ELFFile(open("libmyjni.so", "rb"))
text = elf.get_section_by_name(".text")
code = text.data()
base = text["sh_addr"]
md = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
# 按符号表给出的函数边界逐函数反汇编(避免整段错位)
for name, addr, end in [("n2",0x11f8,0x13b0), ("n1",0x13b0,0x1444),
("n3",0x14cc,0x1508), ("JNI_OnLoad",0x1508,0x1600)]:
print(f"===== {name} =====")
for insn in md.disasm(code[addr-base:end-base], addr):
print(f"{insn.address:#08x}: {insn.mnemonic} {insn.op_str}")
000011f8: push.w {r4, r5, r6, r7, r8, sb, sl, lr}
000011fc: mov r6, r0
000011fe: ldr r4, [pc, #0x108]
00001200: sub sp, #0x18
00001202: ldr r0, [pc, #0x108]
...
0000125e: mov.w r4, #0x7e0 # ← 关键:运行计数器 r4 初值 0x7e0
...
0000127e: movs r1, #3
00001280: blx #0x16c8 # 调用一个取模运算 (i % 3)
00001284: cmp r1, #1
00001286: bne #0x12a8
... # i%3 == 1 分支
0000128a: adds r4, #5
0000128c: ands r3, r4
...
000012a8: cmp r1, #2
000012aa: bne #0x12c0
... # i%3 == 2 分支
000012ac: adds r0, r4, #7
...
000012c0: adds r0, r4, #3 # i%3 == 0 分支
...
000012d2: ldrb r3, [r5] # 取出明文第 i 字节
000012d6: adds r5, #1
000012d8: eors r3, r2 # ← 与密钥字节 XOR
000012da: strb r3, [r5, #-0x1] # 存回(密文)
from elftools.elf.elffile import ELFFile
elf = ELFFile(open("libmyjni.so", "rb"))
data = open("libmyjni.so", "rb").read()
# JNINativeMethod 数组(3 项,每项 12 字节)
base = 0x4004
arr = data[base:base+3*12]
for k in range(3):
e = arr[k*12:k*12+12]
name_p = int.from_bytes(e[0:4], "little") # 方法名 vaddr
sig_p = int.from_bytes(e[4:8], "little") # 签名 vaddr
fn = int.from_bytes(e[8:12], "little") # 原生函数 vaddr (+1 表示 Thumb)
# 通过 PT_LOAD 把 vaddr 换算成文件偏移,读出字符串
...
print(name, fn)
00001410: ldr r1, [pc, #0x2c] ; 字面池 → 目标字符串指针
00001412: strb.w r8, [r5, r7] ; 在缓冲区末尾补 '\0'
00001416: mov r0, r5
00001418: add r1, pc
0000141a: blx #0x1168 ; ← 调用 strcmp(content, TARGET)
0000141e: cbnz r0, #0x1426 ; 不相等 → 跳 1426
00001420: mov r0, r6
00001422: movs r1, #1 ; 相等 → setValue(obj, 1) ← 注册成功
00001424: b #0x142a
00001426: mov r0, r6
00001428: mov r1, r8 ; 不相等 → setValue(obj, 0)
0000142a: bl #0x1364 ; setValue
000014cc: bl #0x13b0 ; 先调 initSN 刷新 m
000014d4: bl #0x1320 ; getValue(obj) 读取 m
000014da: cbnz r0, #0x14e4 ; m != 0
000014dc: ... string '还不行呢!' (m==0:未注册)
000014e4: cmp r0, #1
000014ea: ... string '输入即是flag,格式为xman{……}!' (m==1:提示 flag 格式)
000014f0: ... string '状态不太对。。' (其他:异常)
struct = b"W3_arE_whO_we_ARE\x00"
def key_index(i, r4):
rem = i % 3
if rem == 1: r4 = (r4 + 5) & 0xF; idx = r4 + 1
elif rem == 2: r4 = (r4 + 7) % 0xF; idx = r4 + 2
else: r4 = (r4 + 3) % 0xD; idx = r4 + 3
return idx, r4
def xor(data):
out = bytearray(len(data)); r4 = 0x7e0
for i in range(len(data)):
idx, r4 = key_index(i, r4)
out[i] = data[i] ^ struct[idx]
return bytes(out)
serial = b"201608Am!2333"
print(xor(serial)) # -> b'EoPAoY62@ElRD'
print(xor(serial) == b"EoPAoY62@ElRD") # True
赞赏
- [原创]攻防世界 GFSJ0508「黑客精神」- 逆向分析 807
- [原创]unidbg补环境实战(某文学网站) 6849
- [原创]CTF自毁程序密码:逆向分析 46065
- [原创]unidbg入门笔记 37637
- [原创]CTF 丛林的秘密:算法分析 14055