笔者的 app
版本:11.13.408
加密定位
搜索关键字:mtgsig
点进去看看,发现是一个类变量
查找用例看看都有什么地方在使用
发现只有 7 个位置,我们需要重点关注 xxx.put
的位置,这些才是放置参数的代码,所以有以下几个位置:
jSONObject3.put(MTGConfigs.MTG_SIG_HEADER, new JSONObject((Map) this.mRequestSignatureMethodV4.invoke(null, optString, optString2, optString3, optString4, optString5, bArr)));
hashMap.put(MTGConfigs.MTG_SIG_HEADER, commonCandyInterceptor.getRequestSignature(str, uri, str2, str3, str4, bArr));
hashMap.put(MTGConfigs.MTG_SIG_HEADER, makeHeader);
createMap.putString(MTGConfigs.MTG_SIG_HEADER, com.sankuai.waimai.store.util.h.a((Map) this.mRequestSignatureMethodV4.invoke(null, string, a2, string3, string4, string5, bArr)));
逐个去分析,最后是用的第三个找到的,步骤如下:
加密入口为:com.meituan.android.common.mtguard.NBridge.main
使用 Frida
进行验证
let NBridge = Java.use("com.meituan.android.common.mtguard.NBridge");
NBridge["main"].implementation = function (i, objArr) {
let result = this["main"](i, objArr);
HookBox.log(
"NBridge.main",
0,
`i=${i}`,
`objArr=${objArr}`,
`result=${result}`,
)
return result;
};
发现好像没什么问题!
so 文件定位
知道了加密入口,要定位 so
文件就更简单了,我们只需要 hook
一下 registNatives
就可以了,脚本在:https://raw.githubusercontent.com/lasting-yang/frida_hook_libart/master/hook_RegisterNatives.js
不做介绍, so
文件为 libmtguard.so
unidbg
1. 模板搭建
package com.meituan;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import java.io.File;
public class mtgsig extends AbstractJni {
private final AndroidEmulator emulator;
private final VM vm;
private final String dirPath = "unidbg-android/src/test/resources";
private final Module module;
mtgsig() {
// 模拟器,设置进程名为 com.sankuai.meituan
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.sankuai.meituan").build();
// 模拟器的内存操作接口
final Memory memory = emulator.getMemory();
// 23版本的sdk
memory.setLibraryResolver(new AndroidResolver(23));
vm = emulator.createDalvikVM(new File(dirPath + "/mt1113408.apk"));
// 打印日志
vm.setVerbose(true);
// 设置JNI,类必须extends AbstractJni -> 用于补环境
vm.setJni(this);
// 加载 so 文件, 因为传入 app, 所以可以直接写 so 文件的名字, 去掉 开头的 lib 和 结尾的 .so
DalvikModule dm = vm.loadLibrary("mtguard", true);
module = dm.getModule(); // 获取so模块的句柄
dm.callJNI_OnLoad(emulator); // 调用JNI_OnLoad
}
public static void main(String[] args) {
mtgsig ins = new mtgsig();
}
}
搭完架子之后运行,发现其有几个 so
文件依赖,我们可以补一下
![image-20230609104908876](/Users/Kem/Library/Application Support/typora-user-images/image-20230609104908876.png)
new JniGraphics(emulator, vm).register(memory);
new AndroidModule(emulator, vm).register(memory);
补完之后运行,发现还有一个依赖的 so
文件, 先不补了
![image-20230609105207644](/Users/Kem/Library/Application Support/typora-user-images/image-20230609105207644.png)
2. 入参构建
构建很简单,使用 frida hook
一下,分析参数,然后自己构造即可,需要注意的就是里面有一个参数不好打印,可以使用以下脚本
function b2s(array) {
var buffer = Java.array('byte', array);
var result = "";
for (var i = 0; i < buffer.length; ++i) {
result += (String.fromCharCode(buffer[i] & 0xff)); // here!!
}
return result
}
let NBridge = Java.use("com.meituan.android.common.mtguard.NBridge");
NBridge["main"].implementation = function (i, objArr) {
let result = this["main"](i, objArr);
if (i === 2) {
console.log(objArr[1].$className)
HookBox.log(
"NBridge.main",
0,
`i=${i}`,
`objArr[0]=${objArr[0]}`,
`objArr[1]=${b2s(objArr[1])}`,
`objArr[2]=${objArr[2]}`,
`result=${result}`,
)
}
return result;
};
可以看出来参数都是什么
3. 模拟调用
先编写一个调用方法
public void callMain() {
DvmClass clazz = vm.resolveClass("com/meituan/android/common/mtguard/NBridge");
StringObject appkey = new StringObject(vm, "9b69f861-e054-4bc4-9daf-d36ae205ed3e");
vm.addLocalObject(appkey);
String urlObj = "GET /abtest/v1/getAllStrategys __reqTraceID=5a7a93aa-1a0b-4e67-b27c-95f5e3f59c18&app=group&ci=1&msid=&platform=android&userid=-1&utm_campaign=AgroupBgroupC0E0Ghomepage&utm_content=ef9abda42ada49248c5c4c4c45443fbea167994434606354397&utm_medium=android&utm_source=yijia5&utm_term=1100130408&uuid=0000000000000F4D4F832198E4FDA96343456BADD2441A167994434590459712&version_name=11.13.408";
ByteArray urlBytes = new ByteArray(vm, urlObj.getBytes());
DvmObject<?> ret = clazz.callStaticJniMethodObject(
emulator,
"main(I[Ljava/lang/Object;)[Ljava/lang/Object;",
2,
vm.addLocalObject(new ArrayObject(appkey, urlBytes, null))
);
System.out.println("================================================");
System.out.println(ret.getValue());
System.out.println("================================================");
}
调用一下试试,发现直接报错了
果然大厂没有这么容易,猜测可能是有什么初始化的步骤,先分析一下源码看看能不能找到,使用查找用例
发现第一行可能是的,那就先试试,在调用函数之前主动初始化一下,发现结果还是不行!
所以直接用 frida hook
一下,看看在调用 main
之前都做了什么
let NBridge = Java.use("com.meituan.android.common.mtguard.NBridge");
NBridge["main"].implementation = function (i, objArr) {
let result = this["main"](i, objArr);
HookBox.log(
"NBridge.main",
0,
`i=${i}`,
`objArr=${objArr}`,
`result=${result}`,
)
return result;
};
查看日志发现调用了 1 和 3,我们都写上看看
public void callInit() {
DvmClass clazz = vm.resolveClass("com/meituan/android/common/mtguard/NBridge");
clazz.callStaticJniMethodObject(
emulator,
"main(I[Ljava/lang/Object;)[Ljava/lang/Object;",
1,
vm.addLocalObject(new ArrayObject())
);
clazz.callStaticJniMethodObject(
emulator,
"main(I[Ljava/lang/Object;)[Ljava/lang/Object;",
3,
vm.addLocalObject(new ArrayObject())
);
}
再跑一下,发现跑通了,简直不要太 nice!
4. 环境补充
开始枯燥的补环境了!
com/meituan/android/common/mtguard/NBridge->getClassLoader()Ljava/lang/ClassLoader;
要参考一下 jadx
里面的代码来补环境
case "com/meituan/android/common/mtguard/NBridge->getClassLoader()Ljava/lang/ClassLoader;":
return vm.resolveClass("dalvik/system/PathClassLoader").newObject(this.getClass().getClassLoader());
java/lang/ClassLoader->loadClass(Ljava/lang/String;)Ljava/lang/Class;