TAB 键可以在 F5 与汇编代码来回跳转
so 层的函数参数
// 第一个参数是 env
// 第二个参数,实例方法是 jobject,静态方法是 jclazz,直接填 0,一般用不到。
**在第一个参数上按 y,输入 JNIEnv* **, jni 的 api 就被识别出来了
重命名参数
按 N 在其他参数上重命名
跳转到指定地址
按 G
R0-R3 寄存器
根据 ARM 调用约定,入参前四个分别通过 R0-R3 调用,返回值通过 R0 返回
加密常量转 16 进制
按 H 转为 16 进制
代码中多次出现 sub_3151C,进去看一看。
一个哈希算法,可以简单划分成填充和加密两部分,直接 Hook 加密函数,打印入参和出参,发现 IV 和 block 没有变化,说明填充是没有魔改的。魔改的是加密算法本身。
魔改的是算法本身,因为运算函数的入参是正常的、填充后的明文,所以不存在自定义填充、或者对明文做变换的可能,出参即是输出的结果,所以算法并不是在标准流程之后做了一些自定义步骤,它修改的——就是算法本身。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public void hook_3151C(){ // 获取HookZz对象 IHookZz hookZz = HookZz.getInstance(emulator); // 加载HookZz,支持inline hook,文档看https://github.com/jmpews/HookZz // enable hook hookZz.enable_arm_arm64_b_branch(); // 测试enable_arm_arm64_b_branch,可有可无 // hook MDStringOld hookZz.wrap(module.base + 0x3151C + 1, new WrapCallback<HookZzArm32RegisterContext >() { // inline wrap导出函数 @Override // 方法执行前 public void preCall(Emulator<?> emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) { // 类似于Frida args[0] Pointer input = ctx.getPointerArg(0); byte[] inputhex = input.getByteArray(0, 20); Inspector.inspect(inputhex, "IV"); Pointer text = ctx.getPointerArg(1); byte[] texthex = text.getByteArray(0, 64); Inspector.inspect(texthex, "block"); ctx.push(input); ctx.push(text); }; @Override // 方法执行后 public void postCall(Emulator<?> emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) { Pointer text = ctx.pop(); Pointer IV = ctx.pop(); byte[] IVhex = IV.getByteArray(0, 20); Inspector.inspect(IVhex, "IV"); byte[] outputhex = text.getByteArray(0, 64); Inspector.inspect(outputhex, "block out"); } }); hookZz.disable_arm_arm64_b_branch(); }; </HookZzArm32RegisterContext>
|
那么这个时候就该考虑,SHA1 算法的运算部分是由什么组成?SHA1 和 MD5 采用了相同的结构,每 512 比特分组需要一轮运算,我们的输入长度不超过一个分组的长度,所以只用考虑一轮运算。一轮运算是 80 步,每隔 20 步是一种模式。
进行十数次甚至数十次的 inline hook,这种情况下我们使用 Unidbg 的 console debugger。
emulator.attach().addBreakPoint(module.base+0x3161E);