unidbg学习

0-前言

之前用ai结合unidbg操作的有点云里雾里,手似懂非懂,脑子也是什么都没学到,所以单独写一个关于unidbg的笔记,学习视频是大佬的黑盒魔法之Unidbg,课件:《安卓逆向这档事》吾爱破解

1-准备

为了更好的trace可以对如下进行更改

1
log4j.logger.com.github.unidbg.linux.file=DEBUG

1>>常用知识储备

①.在IDA Pro里的“Exports

“别人可以直接调用我的哪些函数?”

  • 找关键逻辑函数,有些壳 / 加固会:
    • 隐藏内部函数
    • 导出函数必须保留
②.DT_SONAME 代表库名
  • 在 Android 中,系统加载器(Linker)识别一个库到底叫什么,不是看它的文件名,而是看它 ELF 头部里 DT_SONAME 这个字段。
③.tracejs逆向里相似
  • 可以使用Trace进行汇编的导出,但请注意指令级的 Trace (traceCode) 颗粒度太细了,分分钟会爆炸。
④.Unidbg32位更完善

(随着时代发展可能会变)

  • 所以可以使用apk中的32位的so进行分析

⑤.常用api模块对比
模块 作用
Emulator CPU + 进程 + 执行
Memory 内存 + SO + 指针
VM Java 层
Ⅰ. 常用 Emulator API
方法名(签名) 返回类型 简短描述 unidbg 中是否存在 / 备注
getMemory() Memory 获取内存操作接口(读写/映射/断点等) 示例:Memory memory = emulator.getMemory();
getPid() int 获取模拟进程的 PID 通常用于日志/区分多个 emulator 实例。
createDalvikVM() VMDalvikVM(类名会有变体) 创建一个 Dalvik/Android 虚拟机(无 APK) vm = emulator.createDalvikVM();
createDalvikVM(File apkFile) VM 使用指定 APK 创建虚拟机(会把 APK 的 dex/classPath 加入 VM) 常用:vm = emulator.createDalvikVM(new File("xxx.apk"));
getDalvikVM() VM 获取已创建的虚拟机实例(如果有) vm = emulator.getDalvikVM();(若之前 create 过)
showRegs() void 打印/显示当前寄存器状态(可指定寄存器) (用于调试,输出寄存器信息)。
getBackend() Backend 获取后端 CPU(Unicorn 后端)接口 用于低层操作或获取后端信息。
getProcessName() String 返回模拟的进程名(你在构建时可设置) AndroidEmulatorBuilder.setProcessName(...) 指定。
getContext() RegisterContext 获取当前寄存器上下文(用于读写寄存器) 类型名可能根据版本为 RegisterContext 或具体实现。
traceRead(long begin, long end) void 打开内存读跟踪(指定地址范围) 可用于监测某地址区间被读取。
traceWrite(long begin, long end) void 打开内存写跟踪(指定地址范围) 常用于找写入密钥/解密表的地方。
traceCode(long begin, long end) void 跟踪/打印执行的汇编指令(指定地址范围) 用于动态分析执行流/断点辅助。
isRunning() boolean 判断当前 Emulator 是否正在运行(线程/CPU) 通常用于控制循环或调试。
Ⅱ.Memory 常用API
方法名(签名) 返回类型 作用说明
setLibraryResolver(AndroidResolver resolver) void 设置 Android 系统库解析器(决定如何加载 libc、libm 等)
getStackPoint() long 获取当前栈指针(SP)位置
pointer(long address) UnidbgPointer 将一个地址包装成指针对象(方便读写内存)
getMemoryMap() Collection<MemoryMap> 获取当前内存映射(类似 /proc/self/maps
findModule(String moduleName) Module 根据模块名查找已加载的 SO
findModuleByAddress(long address) Module 根据地址查找所属模块
loadLibrary(File file, boolean forceLoad) ElfModule 手动加载 SO(底层调用 linker)
allocateStack(int size) UnidbgPointer 在栈上分配一段空间
writeStackString(String value) UnidbgPointer 往栈里写字符串(返回指针)
writeStackBytes(byte[] value) UnidbgPointer 往栈写字节数组
malloc(int size, boolean runtime) UnidbgPointer 分配堆内存(类似 C 的 malloc)
Ⅲ.VM 常用 API
方法名(签名) 返回类型 作用说明
createDalvikVM(File apkFile) VM 创建虚拟机并加载 APK(dex)
setVerbose(boolean verbose) void 是否打印 JNI / VM 日志
loadLibrary(File soFile, boolean callInit) DalvikModule 加载 SO 文件(可选择是否调用 init)
setJni(Jni jni) void 设置 JNI 回调接口(你自己实现)
getJNIEnv() Pointer 获取 JNIEnv 指针
getJavaVM() Pointer 获取 JavaVM 指针
callJNI_OnLoad(Emulator emulator, Module module) void 手动调用 JNI_OnLoad
addGlobalObject(DvmObject<?> obj) int 添加全局引用对象
addLocalObject(DvmObject<?> obj) int 添加局部引用对象
getObject(int hash) DvmObject<?> 根据引用 ID 获取对象
resolveClass(String className) DvmClass 解析类(核心 API)
getPackageName() String 获取 APK 包名
getVersionName() String 获取版本名
getVersionCode() String/int 获取版本号
openAsset(String assetName) InputStream 读取 APK assets 文件
getManifestXml() String 获取 AndroidManifest.xml 内容
getSignatures() CertificateMeta[] 获取 APK 签名信息
findClass(String className) DvmClass 查找已加载类
getEmulator() Emulator<?> 获取 emulator 实例

2>>熟悉常见api

1
2
3
4
5
6
7
8
9
10
11
12
//  初始化 64 位模拟器
emulator = AndroidEmulatorBuilder.for64Bit()
//设置包名
.setProcessName("com.xxxx.app")
/*添加后端引擎工厂,Unicorn2Factory 是基于 Unicorn2 的实现(Unicorn 是一个 CPU 模拟器)。参数 true 可能表示当 Unicorn2 不可用时回退到 Unicorn1。*/
.addBackendFactory(new Unicorn2Factory(true))

// 切换为Dynarmic引擎
//.addBackendFactory(new DynarmicFactory(true))


.build();
1
2
//  设置虚拟机的根目录用作与实现io重定向
.setRootDir()
1
2
3
//	找 SO 基址
Module module = memory.findModule("libxxx.so");
long base = module.base;
1
2
3
// 读内存、写内存、dump 数据、看加密结果
UnidbgPointer ptr = memory.pointer(0x12345678);
int value = ptr.getInt(0);
1
2
// 分配 buffer(比如解密输出)
UnidbgPointer buf = memory.malloc(0x100, true);
1
2
3
4
5
6
7
      // 3. 初始化虚拟机
vm = emulator.createDalvikVM(new File(apkPath));
vm.setVerbose(true);
// 是否打印所有细节
vm.setJni(this);
// 补安卓所需要的环境
new AndroidModule(emulator, vm).register(memory);
1
2
3
// 实用操作-减少补环境操作-直接读取整个apk
VM dalvikVM = createDalvikVM(new File("apk file path"))
// 加载后通过openAsset加载APK的assets目录下的文件
1
2
3
4
5
6
7
8
9
10
11
      // 1. 先加载-把指定的 SO 动态库加载到虚拟机(unidbg)中,并返回一个模块对象
DalvikModule dm = vm.loadLibrary("xxx", false);

// 2. hook / 打日志 / 设置断点
hook_xxx();

// 3. 再执行初始化
module.callEntry(emulator);
dm.callJNI_OnLoad(emulator);

// 好处是抓初始化过程、绕反调试、看 key 初始化

待续。。。