本文仅供学习交流,只提供关键思路不会给出完整代码,严禁用于非法用途,拒绝转载,若有侵权请联系我删除!
目标app:5Lqs5LicYXBwMTEuMy4y
目标接口:aHR0cHM6Ly9hcGkubS5qZC5jb20vY2xpZW50LmFjdGlvbj9mdW5jdGlvbklkPXNlYXJjaA==
一、引言
接上篇安卓逆向 - 某东body参数算法还原_小馒头yy的博客-CSDN博客 我们抓到了关键字搜索的包,并且成功还原了body参数,今天我们来看看请求url中的sign是如何生成的。
二、分析Java层代码
1、熟练打开Jadx,搜索sign参数发现搜出来的结果太多了15179条,不好分析。过来人的经验是搜索put("sign" "sign"等关键字减小范围。
2、调整搜索策略后,明显少了很多条信息,我们依次点开查看上下文,使用Frida辅助动态分析
最终定位到如下关键行,使用Frida hook发现跟我们抓到的包一模一样。
String signature = a.a().w().signature(a.a().c(), queryParameter, str, deviceUUID, b, versionName);
hook到结果:
st=1694351779782&sign=989c40fed76cc5b05b47825524b34e14&sv=122
跟进该方法,发现是个接口
选中该接口,查看其引用。继续跟进去分析代码,最后我们找到该native方法。说明该算法时调用so层的代码生成,我们后面将尝试使用Unidbg主动调用生成该sign。
public static native String getSignFromJni(Context context, String str, String str2, String str3, String str4, String str5);
三、unidbg入门
1、unidbg介绍:允许您模拟 Android 本机库和实验性 iOS 模拟。unidbg 是一款基于 unicorn 和 dynarmic 的逆向工具, 可以直接调用 Android 和 IOS 的 so 文件,无论是黑盒调用 so 层算法,还是白盒 trace 输出 so 层寄存器值变化都是一把利器。官方github: GitHub - zhkl0228/unidbg: Allows you to emulate an Android native library, and an experimental iOS emulation
2、使用前置:需要有一定的Java基础
配置Java环境:推荐Java8
配置maven环境:https://maven.apache.org/download.cgi
开发工具IDEA:IntelliJ IDEA – 领先的 Java 和 Kotlin IDE
3、unidbg官方github有充足的例子,足够我们了解学习它。建议安装官方的流程都学习一遍,这对我们学习unidbg有极大好处。
四、基于unidbg主动调用生成sign
1、配置unidbg基础环境,clone官方代码,找到生成sign方法的so, 就是下方的libjdbitmapkit.so
2、编写基础代码,路径需要改成自己的,重点是 vm.callJNI_OnLoad(emulator, module);这步,我们期望能打印出动态注册方法的地址偏移
public class JdSignTest extends AbstractJni {
private final AndroidEmulator emulator;
private final VM vm;
private PKCS7 pkcs7;
private DvmClass dvmClass = null;
public JdSignTest(){
emulator = AndroidEmulatorBuilder.for32Bit().build();
Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
// 加载 apk
vm = emulator.createDalvikVM(new File("D:\\resource\\reverse\\jd\\jd_11.3.2.apk"));
vm.setVerbose(true);
vm.setJni(this);
// 加载 so
DalvikModule dalvikModule = vm.loadLibrary(new File("D:\\resource\\reverse\\jd\\so\\libjdbitmapkit.so"), false);
Module module = dalvikModule.getModule();
// 调用 onload
vm.callJNI_OnLoad(emulator, module);
}
}
3、运行以上代码,报错了不要慌,这种一般都是so调用了Java层的方法,我们根据提示补充即可。
如,以上报错需要补充代码:
@Override
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
if ("com/jingdong/common/utils/BitmapkitUtils->a:Landroid/app/Application;".equals(signature)){
DvmClass dvmClass1 = vm.resolveClass("android/app/Activity");
return vm.resolveClass("android/app/Application", dvmClass1).newObject(null);
}
return super.getStaticObjectField(vm, dvmClass, signature);
}
unidbg的缺点就是这个,so层可以调用了很多地方,最多的是通过Jni调用Java方法,我们都需要补充上去,特别的费时间。缺的环境可能是:目标函数中通过 JNI 调用到了某个自己的 JAVA 方法、对linux虚拟文件的读写、对资源文件的读写、系统调用、Android系统库SO。
4、将所有缺的环境补全,成功打印出动态注册信息,注意 RegisterNative关键字,这个指的就是注册Java层的native方法,往后翻看到getSignFromJni(),分析其参数,和我们Java层的分析对应上了。
5、接下来模拟调用该方法,填充参数。
getSignFromJni(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
直接以全类名调用,传的几个参数就是我们在Java层hook到的,都是明文,例:
StringObject str5 = new StringObject(vm,"11.3.2");
DvmClass obj = vm.resolveClass("com/jingdong/common/utils/BitmapkitUtils");
DvmObject<?> context = vm.resolveClass("android/content/Context").newObject(null);
// 通过方法名称去调用
DvmObject<?> dvmObject = obj.callStaticJniMethodObject(emulator, "getSignFromJni(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
context, str, str2,
str3, str4, str5);
调用该方法,再次报错
public static void main(String[] args) {
JdSignTest test = new JdSignTest();
test.getSignFromJni();
}
我们根据报错,继续漫长的补环境。例:
@Override
public DvmObject<?> newObjectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
if ("java/lang/StringBuffer-><init>()V".equals(signature)){
return vm.resolveClass("java/lang/StringBuffer").newObject(new StringBuffer());
}
if ("java/lang/Integer-><init>(I)V".equals(signature)){
Integer integer = vaList.getIntArg(0);
return vm.resolveClass("java/lang/Integer").newObject(integer);
}
return super.newObjectV(vm, dvmClass, signature, vaList);
}
补环境的过程是漫长且痛苦的,最后跑出结果时也是真的爽
收工!