乌篷船的博客

我当然是正态分布下的蠢材
可是啊
人生=δ(x−相信)

unidbg代码记录

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
//打印退出的父类的汇编代码
// 既然 E2E4C 返回后就退出了,我们去分析调用 E2DC8 的父函数逻辑
// 我们直接对 E2DC8 的入口下钩子,打印它的调用栈信息
long base = module.base;
long sub_E2DC8 = base + 0xE2DC8L;

emulator.getBackend().hook_add_new(new com.github.unidbg.arm.backend.CodeHook() {
@Override public void onAttach(UnHook unHook) {}
@Override public void detach() {}

@Override
public void hook(Backend backend, long address, int size, Object user) {
if (address == sub_E2DC8) {
// 获取 LR,看看是谁调用的我
long lr = emulator.getContext().getLR();
System.out.println("\n" + "=========================================================");
System.out.println(">>> [Found] sub_E2DC8 被调用!");
System.out.println(">>> [Caller] 调用者地址 (LR): 0x" + Long.toHexString(lr));
System.out.println(">>> [Offset] 调用者在 SO 中的偏移: 0x" + Long.toHexString(lr - base));

// 打印调用者附近的指令,看看它是怎么处理返回值的
byte[] parentCode = emulator.getBackend().mem_read(lr, 100);
try (Capstone cs = new Capstone(Capstone.CS_ARCH_ARM64, Capstone.CS_MODE_ARM)) {
Capstone.CsInsn[] insns = (Capstone.CsInsn[]) cs.disasm(parentCode, lr);
System.out.println(">>> [Analysis] 调用者后续逻辑:");
for (Capstone.CsInsn insn : insns) {
System.out.printf(" 0x%x: %s %s%n", insn.getAddress(), insn.mnemonic, insn.opStr);
if (insn.mnemonic.startsWith("cb") || insn.mnemonic.startsWith("tb")) {
System.err.println(" [!!!] 发现关键跳转!这可能就是判断自检是否通过的地方");
}
}
} catch (Exception e) {}
System.out.println("============================================================" + "\n");
}
}
}, sub_E2DC8, sub_E2DC8, null);

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

//是否绕过壳加密

Module libart = emulator.getMemory().findModule("libart.so");
if (libart != null) {
Symbol registerNatives = libart.findSymbolByName("_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi");

if (registerNatives != null) {
System.out.println(">>> [验证开始] 正在监控 RegisterNatives,等待壳释放核心逻辑...");

Dobby.getInstance(emulator).replace(registerNatives, new ReplaceCallback() {
@Override
public HookStatus onCall(Emulator<?> emulator, long originFunction) {
// 只要这个方法被调用,就说明壳已经开始注册函数了!
System.out.println("\n#########################################################");
System.out.println("### [通过验证] 壳已成功绕过自检测,正在向系统提交解密后的算法! ###");
System.out.println("#########################################################");

RegisterContext context = emulator.getContext();
long methodsPtr = context.getPointerArg(2).peer;
int methodCount = (int) context.getLongArg(3);

for (int i = 0; i < methodCount; i++) {
long structBase = methodsPtr + (i * 24L);
long namePtr = emulator.getMemory().pointer(structBase).getPointer(0).peer;
long sigPtr = emulator.getMemory().pointer(structBase + 8).getPointer(0).peer;
long fnPtr = emulator.getMemory().pointer(structBase + 16).peer;

String name = emulator.getMemory().pointer(namePtr).getString(0);
String signature = emulator.getMemory().pointer(sigPtr).getString(0);

System.out.println(String.format(">>> [解密成功] 方法名: %s", name));
System.out.println(String.format(" - 签名: %s", signature));
System.out.println(String.format(" - 内存地址: 0x%x", fnPtr));

if (module != null) {
System.out.println(String.format(" - IDA偏移(脱壳后): 0x%x", fnPtr - module.base));
}
}
return HookStatus.RET(emulator, originFunction);
}
});
}
}
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
//打印退出那里的汇编代码


long base = module.base;
long tracePoint = base + 0xE2E4CL;

emulator.getBackend().hook_add_new(new com.github.unidbg.arm.backend.CodeHook() {
@Override
public void onAttach(UnHook unHook) {}

@Override
public void detach() {}

@Override
public void hook(Backend backend, long address, int size, Object user) {
if (address == tracePoint) {
// 兼容性好的分隔线打印
System.out.println("\n************************************************************");
System.out.println(">>> [Critical Trace] 已越过文件读取点 (0x" + Long.toHexString(address) + ")");

// 关键:打印当前的 X0 寄存器,它是 sub_E30B0 的返回值(通常是文件描述符 FD)
// 如果 X0 为负数或 0,说明文件打开失败,这很可能导致后续的退出
UnidbgPointer x0 = emulator.getContext().getPointerArg(0);
System.out.println(">>> [Context] X0 (Return Value): " + x0);

// 读取内存中的机器码
byte[] code = emulator.getBackend().mem_read(address, 200);

// 使用 try-with-resources 自动释放 Capstone 资源
try (Capstone cs = new Capstone(Capstone.CS_ARCH_ARM64, Capstone.CS_MODE_ARM)) {
// 反汇编获取指令数组
Capstone.CsInsn[] insns = (Capstone.CsInsn[]) cs.disasm(code, address);

System.out.println(">>> [Disasm] 后续 50 条指令预览:");
for (Capstone.CsInsn insn : insns) {
// 使用 insn.getAddress (兼容性最广)
String addrHex = Long.toHexString(insn.getAddress());
System.out.printf(" 0x%s: %s %s%n", addrHex, insn.mnemonic, insn.opStr);

// 1. 发现 SVC 说明它在跳过 libc 直接调内核
if ("svc".equals(insn.mnemonic)) {
System.err.println(" [!] 警告: 发现 SVC 系统调用,可能正在计算 Checksum 或检查调试器");
}
// 2. 发现分支判断
if (insn.mnemonic.startsWith("cb") || insn.mnemonic.startsWith("b.")) {
System.out.println(" [?] 发现逻辑跳转,可能是校验结果的分支");
}
// 3. 到达函数返回则停止,避免打印过多无用数据
if ("ret".equals(insn.mnemonic)) {
System.out.println(" [*] 函数逻辑结束点");
break;
}
}
} catch (Exception e) {
System.err.println(">>> 反汇编失败: " + e.getMessage());
}
System.out.println("************************************************************\n");
}
}
}, tracePoint, tracePoint, null);


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
//查看RET的下一个地址

long sub_E2DC8_Addr = module.base + 0xE2DC8L;

emulator.getBackend().hook_add_new(new com.github.unidbg.arm.backend.CodeHook() {
@Override
public void onAttach(UnHook unHook) {}
@Override
public void detach() {}

@Override
public void hook(Backend backend, long address, int size, Object user) {
if (address == sub_E2DC8_Addr) {
// 1. 获取进入函数时的 LR 寄存器值
com.github.unidbg.arm.context.RegisterContext context = emulator.getContext();
long lr = context.getLR();

// 2. 计算在 SO 中的相对偏移
long offset = lr - module.base;

System.out.println("\n" + "================ [RET Tracker] ================");
System.out.println(">>> 目标函数 sub_E2DC8 被调用!");
System.out.println(">>> 它执行完 RET 后,会返回到地址: 0x" + Long.toHexString(lr));
System.out.println(">>> 对应的 SO 偏移 (即调用者的下一条指令): 0x" + Long.toHexString(offset));
System.out.println("===============================================" + "\n");

// 3. (可选) 打印调用栈,看看到底经过了哪些函数
// emulator.getStackTrace().print(System.out);
}
}
}, sub_E2DC8_Addr, sub_E2DC8_Addr, null);
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
//检查自检测


long base = module.base;

// --- 监控点 2:读取内容 (0xE8A8C) ---
// 使用断点代替 Dobby Hook
emulator.attach().addBreakPoint(base + 0xE8A8CL, new BreakPointCallback() {
// 成员变量存地址
private UnidbgPointer bufferPtr;

@Override
public boolean onHit(Emulator<?> emulator, long address) {
// 在函数执行前 (pre-call 逻辑)
// 使用你的接口获取 X0
RegisterContext ctx = emulator.getContext();
if (ctx instanceof Arm64RegisterContext) {
this.bufferPtr = ((Arm64RegisterContext) ctx).getXPointer(0);
}

// 设置一个单次断点在 LR (返回地址) 处,模拟 post-call
emulator.attach().addBreakPoint(ctx.getLR(), new BreakPointCallback() {
@Override
public boolean onHit(Emulator<?> emulator, long address) {
if (bufferPtr != null) {
String content = bufferPtr.getString(0).trim();
if (!content.isEmpty()) {
System.out.println("[Content Read] 读取到行: " + content);
}
}
return true; // 继续执行
}
});

return true; // 继续执行
}
});

// --- 监控点 3:比对逻辑 (0xE8AC8) ---
emulator.attach().addBreakPoint(base + 0xE8AC8L, new BreakPointCallback() {
@Override
public boolean onHit(Emulator<?> emulator, long address) {
Arm64RegisterContext ctx = emulator.getContext();
UnidbgPointer arg0 = ctx.getXPointer(0); // 行内容
UnidbgPointer arg1 = ctx.getXPointer(1); // 关键字
try {
System.out.println("[Match Check] 比对: '" + arg0.getString(0) + "' VS '" + arg1.getString(0) + "'");
} catch (Exception e) {
System.out.println("[Match Check] 原始地址: X0=" + arg0 + ", X1=" + arg1);
}
return true;
}
});

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
public test(String apkPath, String libName) throws Exception {
this.apkPath = apkPath;

// 1. 初始化 64 位模拟器
emulator = AndroidEmulatorBuilder.for64Bit()
.setProcessName("com.kaoshibaodian.app")
.build();

// 2. 开启系统调用日志并添加文件重定向拦截
emulator.getSyscallHandler().setVerbose(true);
emulator.getSyscallHandler().addIOResolver(this);

final Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));

emulator.getMemory().addModuleListener(new ModuleListener() {
}
});

// 3. 初始化虚拟机
vm = emulator.createDalvikVM(new File(apkPath));
vm.setVerbose(true);
vm.setJni(this);
new AndroidModule(emulator, vm).register(memory);
}
0%