7619 字
38 分钟
某直聘app逆向分析

样本地址: aHR0cHM6Ly93d3cud2FuZG91amlhLmNvbS9hcHBzLzYyMDIyMjIvaGlzdG9yeV92MTMxNDExMA==

抓包分析#

我的 Charles 不知道怎么抽风了,抓包不对劲,又去换了个 burpsuite 来抓包

随便找一个包,发现请求参数又 sp 和 sig 两个加密参数,返回值是加密状态,同时通过多次抓包,发现 sig 中的 V3.0 是固定的,sp 中的 zwp 是固定的 接着看一下 app 有没有加固

没加固,继续往下面看

静态分析#

看 sig 和 sp 这两个参数,常规的搜索肯定不太好找,hook hashmap 这个方式应该好一点

function hook_map() {
console.log("hook hashMap")
var hashMap = Java.use("java.util.HashMap");
hashMap.put.implementation = function (a, b) {
if (a!=null&&a.equals("sp")) {
console.log('输出-->', a, b)
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
}
if (a!=null&&a.equals("sig")) {
console.log('输出-->', a, b)
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
}
return this.put(a, b)
}
}

hook 之后的结果如下:

发现 sp 和 sig 都是在 dc0.b.r 中生成的,去 jadx 中跟一下

发现也就是 put 进去了两个参数,大概率 put 进去就是 sp 和 sig,hook 一下

var C34104b = Java.use("dc0.b");
C34104b["r"].implementation = function (str, str2) {
console.log(`C34104b.m116030r is called: str=${str}, str2=${str2}`);
this["r"](str, str2);
};


好吧,看一下交叉引用,看一下是谁调用了这个函数,然后把加密参数放进去的

这两个也好跟,跟到后面就发现了

sp 主要是由 nativeEncodeRequest 这个 native 函数生成的
sig 主要是由 nativeSignature 这个 native 函数生成的
hook 一下这两个函数,看一下参数是什么?

// sp参数的生成
var JavaString = Java.use("java.lang.String");
var StandardCharsets = Java.use("java.nio.charset.StandardCharsets");
var YZWG = Java.use("com.twl.signer.YZWG");
YZWG["nativeEncodeRequest"].implementation = function (bArr, str) {
var utf8String = JavaString.$new(bArr, StandardCharsets.UTF_8.value);
console.log("以下是sp参数生成的开始::")
console.log("参数1::", utf8String)
console.log("参数2::", str)
let result = this["nativeEncodeRequest"](bArr, str);
console.log(`YZWG.nativeEncodeRequest result=${result}`);
console.log("sp参数生成完毕")
return result;
};
// sig参数的生成
YZWG["nativeSignature"].implementation = function (bArr, str) {
var utf8String = JavaString.$new(bArr, StandardCharsets.UTF_8.value);
console.log("以下是sig参数生成的开始::")
console.log("参数1::", utf8String)
console.log("参数2::", str)
let result = this["nativeSignature"](bArr, str);
var result_string = JavaString.$new(result, StandardCharsets.UTF_8.value);
console.log(`YZWG.nativeSignature result=${result_string}`);
console.log("sig参数生成完毕")
return result;
};

生成的日志如下:

Terminal window
以下是sp参数生成的开始::
参数1:: client_info=%7B%22version%22%3A%2210%22%2C%22os%22%3A%22Android%22%2C%22start_time%22%3A%221772188217563%22%2C%22resume_time%22%3A%221772188217563%22%2C%22channel%22%3A%2228%22%2C%22model%22%3A%22google%7C%7CPixel+2%22%2C%22dzt%22%3A0%2C%22loc_per%22%3A0%2C%22uniqid%22%3A%22809b4bb9-33e9-4d7c-b59b-0a7d6f0ef0ee%22%2C%22oaid%22%3A%2200000000-0000-0000-0000-000000000000%22%2C%22oaid_honor%22%3A%2200000000-0000-0000-0000-000000000000%22%2C%22did%22%3A%22DUlRWq6jQRU_Glo4HL5qAwwsy7zpPrAM1600RFVsUldxNmpRUlVfR2xvNEhMNXFBd3dzeTd6cFByQU0xNjAwc2h1%22%2C%22tinker_id%22%3A%22Prod-arm64-v8a-release-13.141.1314110_0812-10-06-09%22%2C%22is_bg_req%22%3A0%2C%22network%22%3A%22wifi%22%2C%22operator%22%3A%22UNKNOWN%22%2C%22abi%22%3A1%7D&curidentity=0&data=%5B%7B%22action%22%3A%22app-active%22%2C%22time%22%3A1772188217717%2C%22p4%22%3A%22%22%2C%22p5%22%3A%221%22%2C%22p9%22%3A%22Prod-arm64-v8a-release-13.141.1314110_0812-10-06-09%22%7D%5D&req_time=1772188217741&uniqid=809b4bb9-33e9-4d7c-b59b-0a7d6f0ef0ee&v=13.141
参数2:: null
YZWG.nativeEncodeRequest result=zwp_NCTRJTSwi3DewMDkHB6GvU2BAYbyceqJ44s9CxuyL65jv20XaVYg1XpFLNVaF7LolNJtanuLr-ARNMt36qI3RFO3xe8JyDZtt-TQVeh_TOtTgNw-LeFjEs8hx0tJoj7v9ncKmYZWKNui4mO4EMBKqCp54Wi6L8MQyX1JatkuDl9Ec4gZFqDhg7Z1SBl0Q5EvXDZ9Hpb4hZrrp1qpTkEGTjmODhup8HF6VZa0VcxsMFLprFD40lIaRNgU9G7mUIMTy2f0MKwht_lGzm2bw9GnXzCQyGB8Lr9XYvIqmMmmQ8DHWVid8LshMJ0kV52zxZ2rg44Dxcb_cjVfYQd3k5ucb_MgYgZLVJLM4QfkktgfBS3MfS4m3TyKtzi7q5zNIHc52z2fsjpoSrdhmQzh6Bu6-ZMzHJWJ_nbibMppOwVp0HgqJSYdAgYwSwgrTFyUbxw15pDCp830uxCbGNwcHSQjXxi2QgRWAPNLA5ueAa07KMmuMDBrn66vKAqzehxbIFtE4CcoTGstb34UnWCQZ9CHK-DZwfQSrZu_B5i2KIvvFqQG5Fq-ycwEKSZhUmhQJuvLzl5vd6wwh08SdOWSVXsdhHdJaBhEITwc3qRr9K--K8P1fbqdFwd92GQtBdMYqXao5TbaeXuTB0nPAKVcLm67Itr35_UjlCnqciQ_D1gHezlwHiz9xMIosw5DE4hmRLXi_2s6Qm-ohQH9ooezUsQlaPNOPcYqGYA7kqAwhU1qnbISOAhNHjNNCrD-KeWeWb1UJPyg1CE5GJ5KPfTztHGY6zfoJWP7ZKkxM9aOiLMEueJjsCVuE3BpE8h-EMCAeXo_4EXFkeLihAkaNhC9wsphUwZIIS6wTUNQ8yYBBw~~
sp参数生成完毕
以下是sig参数生成的开始::
参数1:: /api/zpCommon/batch/unloginStatisticsclient_info=%7B%22version%22%3A%2210%22%2C%22os%22%3A%22Android%22%2C%22start_time%22%3A%221772188217563%22%2C%22resume_time%22%3A%221772188217563%22%2C%22channel%22%3A%2228%22%2C%22model%22%3A%22google%7C%7CPixel+2%22%2C%22dzt%22%3A0%2C%22loc_per%22%3A0%2C%22uniqid%22%3A%22809b4bb9-33e9-4d7c-b59b-0a7d6f0ef0ee%22%2C%22oaid%22%3A%2200000000-0000-0000-0000-000000000000%22%2C%22oaid_honor%22%3A%2200000000-0000-0000-0000-000000000000%22%2C%22did%22%3A%22DUlRWq6jQRU_Glo4HL5qAwwsy7zpPrAM1600RFVsUldxNmpRUlVfR2xvNEhMNXFBd3dzeTd6cFByQU0xNjAwc2h1%22%2C%22tinker_id%22%3A%22Prod-arm64-v8a-release-13.141.1314110_0812-10-06-09%22%2C%22is_bg_req%22%3A0%2C%22network%22%3A%22wifi%22%2C%22operator%22%3A%22UNKNOWN%22%2C%22abi%22%3A1%7D&curidentity=0&data=%5B%7B%22action%22%3A%22app-active%22%2C%22time%22%3A1772188217717%2C%22p4%22%3A%22%22%2C%22p5%22%3A%221%22%2C%22p9%22%3A%22Prod-arm64-v8a-release-13.141.1314110_0812-10-06-09%22%7D%5D&req_time=1772188217741&uniqid=809b4bb9-33e9-4d7c-b59b-0a7d6f0ef0ee&v=13.141
参数2:: null
YZWG.nativeSignature result=V3.0f709c133cb9964973c96a2b036376fad
sig参数生成完毕

等会儿就拿这些参数值来进行模拟 接着又继续往下面看,看到了返回值的解密函数

返回值主要是由 nativeDecodeContent 这个 native 函数进行解密的,参数 1 就是返回值的字节数组,只不过我们看到的是字节数组转成的字符串的样子,参数 2 是 null,参数 3 是 0,参数 4 是 1,参数 5 是 0
同时,这三个 native 加载的 so 都是 libyzwg.so

unidbg 模拟#

先一个一个来吧,首先是 sp,先用 unidbg 搭个架子吧

package com.boss;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
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;
//必须继承AbstractJni
public class YZWG extends AbstractJni implements IOResolver {
public static AndroidEmulator emulator; // 静态属性,以后对象和类都可以直接使用
public static Memory memory;
public static VM vm;
public static Module module;
// 1 构造方法--》用来初始化
public YZWG(){
// 1.创建设备(32位或64位模拟器), 具体看so文件在哪个目录。 在armeabi-v7a就选择32位
// 传进设备时,如果是32位,后面so文件就要用32位,同理需要用64位的
// 这个名字可以随便写,一般写成app的包名 以后可能会动
emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.hpbr.bosszhipin").build();
// 2.获取内存对象(可以操作内存)
memory = emulator.getMemory();
//3.设置安卓sdk版本(只支持19、23)
memory.setLibraryResolver(new AndroidResolver(23));
// 4.创建虚拟机(运行安卓代码需要虚拟机,就想运行py代码需要python解释器一样) 以后会动
vm = emulator.createDalvikVM(new File("apks/boss/boss.apk"));
vm.setJni(this); // 后期补环境会用,把要补的环境,写在当前这个类中,执行这个代码即可,但是必须继承AbstractJni
vm.setVerbose(true); //是否展示调用过程的细节
// 5.加载so文件
DalvikModule dm = vm.loadLibrary("yzwg", true); // 以后会动
dm.callJNI_OnLoad(emulator); // jni开发动态注册,会执行JNI_OnLoad,如果是动态注册,需要执行一下这个,如果静态注册,这个不需要执行,车智赢案例是静态注册
// 6.dm代表so文件,dm.getModule()得到module对象,基于module对象可以访问so中的成员。
module = dm.getModule(); // 把so文件加载到内存后,后期可以获取基地址,偏移量等,该变量代指so文件
}
//2 sign 成员方法--》主要用来解密
public void get_sp(){
}
// 3 main方法---》右键直接运行
public static void main(String[] args) {
YZWG yzwg=new YZWG();
}
@Override
public FileResult resolve(Emulator emulator, String pathname, int oflags) {
System.out.println("pathname open:"+pathname);
return null;
}
}

运行,发现直接报错了

Terminal window
java.lang.UnsupportedOperationException: com/twl/signer/YZWG->gContext:Landroid/content/Context;
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:103)
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:53)
at com.github.unidbg.linux.android.dvm.DvmField.getStaticObjectField(DvmField.java:106)

这种,直接补,返回一个 context 实例

@Override
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
switch (signature){
case "com/twl/signer/YZWG->gContext:Landroid/content/Context;":{
return vm.resolveClass("android/content/Context").newObject(null);
}
}
return super.getStaticObjectField(vm, dvmClass, signature);
}

补上之后,又报错

Terminal window
java.lang.UnsupportedOperationException: android/content/pm/PackageManager->getPackagesForUid(I)[Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:933)
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:867)
at com.github.unidbg.linux.android.dvm.DvmMethod.callObjectMethod(DvmMethod.java:69)

这个方法接收一个 UID,返回该 UID 对应的所有报名列表(通常是当前应用的包名),返回值还是返回一个数组类型

@Override
public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
switch (signature) {
case "android/content/pm/PackageManager->getPackagesForUid(I)[Ljava/lang/String;": {
int arg = varArg.getIntArg(0);
System.out.println("uid==>" + arg);
return new ArrayObject(new StringObject(vm, vm.getPackageName()));
}
}
return super.callObjectMethod(vm, dvmObject, signature, varArg);
}

接着,报错

Terminal window
java.lang.UnsupportedOperationException: java/lang/String->hashCode()I
at com.github.unidbg.linux.android.dvm.AbstractJni.callIntMethod(AbstractJni.java:965)
at com.github.unidbg.linux.android.dvm.AbstractJni.callIntMethod(AbstractJni.java:938)
at com.github.unidbg.linux.android.dvm.DvmMethod.callIntMethod(DvmMethod.java:129)

直接补就行了

@Override
public int callIntMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
switch (signature){
case "java/lang/String->hashCode()I":{
String str = (String)dvmObject.getValue();
return str.hashCode();
}
}
return super.callIntMethod(vm, dvmObject, signature, varArg);
}

接着,就没报错了,开始调用参数

public void get_sp() {
ArrayList<Object> list = new ArrayList<>(10);
list.add(vm.getJNIEnv());
list.add(0);
String arg1 = "client_info=%7B%22version%22%3A%2210%22%2C%22os%22%3A%22Android%22%2C%22start_time%22%3A%221772188217563%22%2C%22resume_time%22%3A%221772188217563%22%2C%22channel%22%3A%2228%22%2C%22model%22%3A%22google%7C%7CPixel+2%22%2C%22dzt%22%3A0%2C%22loc_per%22%3A0%2C%22uniqid%22%3A%22809b4bb9-33e9-4d7c-b59b-0a7d6f0ef0ee%22%2C%22oaid%22%3A%2200000000-0000-0000-0000-000000000000%22%2C%22oaid_honor%22%3A%2200000000-0000-0000-0000-000000000000%22%2C%22did%22%3A%22DUlRWq6jQRU_Glo4HL5qAwwsy7zpPrAM1600RFVsUldxNmpRUlVfR2xvNEhMNXFBd3dzeTd6cFByQU0xNjAwc2h1%22%2C%22tinker_id%22%3A%22Prod-arm64-v8a-release-13.141.1314110_0812-10-06-09%22%2C%22is_bg_req%22%3A0%2C%22network%22%3A%22wifi%22%2C%22operator%22%3A%22UNKNOWN%22%2C%22abi%22%3A1%7D&curidentity=0&data=%5B%7B%22action%22%3A%22app-active%22%2C%22time%22%3A1772188217717%2C%22p4%22%3A%22%22%2C%22p5%22%3A%221%22%2C%22p9%22%3A%22Prod-arm64-v8a-release-13.141.1314110_0812-10-06-09%22%7D%5D&req_time=1772188217741&uniqid=809b4bb9-33e9-4d7c-b59b-0a7d6f0ef0ee&v=13.141";
ByteArray byteArray = new ByteArray(vm, arg1.getBytes(StandardCharsets.UTF_8));
list.add(vm.addLocalObject(byteArray));
list.add(vm.addLocalObject(null));
Number number = module.callFunction(emulator, 0x209a4, list.toArray());
String result = vm.getObject(number.intValue()).getValue().toString();
System.out.println(result);
}
public void get_sig() {
ArrayList<Object> list = new ArrayList<>(10);
list.add(vm.getJNIEnv());
list.add(0);
String arg1 = "/api/zpCommon/batch/unloginStatisticsclient_info=%7B%22version%22%3A%2210%22%2C%22os%22%3A%22Android%22%2C%22start_time%22%3A%221772188217563%22%2C%22resume_time%22%3A%221772188217563%22%2C%22channel%22%3A%2228%22%2C%22model%22%3A%22google%7C%7CPixel+2%22%2C%22dzt%22%3A0%2C%22loc_per%22%3A0%2C%22uniqid%22%3A%22809b4bb9-33e9-4d7c-b59b-0a7d6f0ef0ee%22%2C%22oaid%22%3A%2200000000-0000-0000-0000-000000000000%22%2C%22oaid_honor%22%3A%2200000000-0000-0000-0000-000000000000%22%2C%22did%22%3A%22DUlRWq6jQRU_Glo4HL5qAwwsy7zpPrAM1600RFVsUldxNmpRUlVfR2xvNEhMNXFBd3dzeTd6cFByQU0xNjAwc2h1%22%2C%22tinker_id%22%3A%22Prod-arm64-v8a-release-13.141.1314110_0812-10-06-09%22%2C%22is_bg_req%22%3A0%2C%22network%22%3A%22wifi%22%2C%22operator%22%3A%22UNKNOWN%22%2C%22abi%22%3A1%7D&curidentity=0&data=%5B%7B%22action%22%3A%22app-active%22%2C%22time%22%3A1772188217717%2C%22p4%22%3A%22%22%2C%22p5%22%3A%221%22%2C%22p9%22%3A%22Prod-arm64-v8a-release-13.141.1314110_0812-10-06-09%22%7D%5D&req_time=1772188217741&uniqid=809b4bb9-33e9-4d7c-b59b-0a7d6f0ef0ee&v=13.141";
ByteArray byteArray = new ByteArray(vm, arg1.getBytes(StandardCharsets.UTF_8));
list.add(vm.addLocalObject(byteArray));
list.add(vm.addLocalObject(null));
Number number = module.callFunction(emulator, 0x21864, list.toArray());
DvmObject<?> retObj = vm.getObject(number.intValue());
byte[] resultBytes = (byte[]) retObj.getValue();
String result = new String(resultBytes, StandardCharsets.UTF_8);
System.out.println("结果: " + result);
}
public void decodeContent() {
byte[] encryptedData = new byte[]{
-10, 114, 76, 25, 44, -37, 100, 101, -124, -69, 64, -18, -101, -32, -119, 121, -125, -10, -36, 42, 125, 34, -68, -48, 102, 107, 91, 107, 88, -42, -125, -19, 69, -95, 90, -127, 56, -28, -108, -69, -101, -78, 1, -2, -70, -84, 12, -91, -106, 98, 27, 89, 71, -37, -87, -37, 71, 110, 111, -65, -22, 71, -16, 15, 111, -106, -30, -1, 8, 31, 46, -60, 38, -110, -21, 71, 126, 34, -16, 62, -106, -40, 16, -107, 2, 64, -65, -56, 71, -36, 85, 40, -3, 69, -71, -65, 24, -20, 4, 125, -114, -112, 126, 17, -28, -63, -100, 61, 58, -24, 6, -55, 87, -59, 124, -112, -26, 50, -41, 26, 25, -9, 33, -48, 80, 22, 46, -119, 112, 78, 5, 24, 96, -86, 36, 68, -71, -7, -90, -71, 111, 76, 87, 116, 56, -19, 2
};
ArrayList<Object> list = new ArrayList<>(10);
list.add(vm.getJNIEnv());
list.add(0);
ByteArray dataArray = new ByteArray(vm, encryptedData);
list.add(vm.addLocalObject(dataArray));
list.add(null);
list.add(0);
list.add(1);
list.add(0);
Number number = module.callFunction(emulator, 0x24dc8, list.toArray()); // 替换实际地址
DvmObject<?> retObj = vm.getObject(number.intValue());
byte[] resultBytes = (byte[]) retObj.getValue();
String result = new String(resultBytes, StandardCharsets.UTF_8);
System.out.println("解密结果: " + result);
}

发现直接就能够出调用的结果了

跟 hook 的结果是一样的
所有的代码如下:

package com.boss;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.memory.Memory;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
//必须继承AbstractJni
public class YZWG extends AbstractJni implements IOResolver {
public static AndroidEmulator emulator; // 静态属性,以后对象和类都可以直接使用
public static Memory memory;
public static VM vm;
public static Module module;
// 1 构造方法--》用来初始化
public YZWG() {
// 1.创建设备(32位或64位模拟器), 具体看so文件在哪个目录。 在armeabi-v7a就选择32位
// 传进设备时,如果是32位,后面so文件就要用32位,同理需要用64位的
// 这个名字可以随便写,一般写成app的包名 以后可能会动
emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.hpbr.bosszhipin").build();
// 2.获取内存对象(可以操作内存)
memory = emulator.getMemory();
//3.设置安卓sdk版本(只支持19、23)
memory.setLibraryResolver(new AndroidResolver(23));
// 4.创建虚拟机(运行安卓代码需要虚拟机,就想运行py代码需要python解释器一样) 以后会动
vm = emulator.createDalvikVM(new File("apks/boss/boss.apk"));
vm.setJni(this); // 后期补环境会用,把要补的环境,写在当前这个类中,执行这个代码即可,但是必须继承AbstractJni
vm.setVerbose(true); //是否展示调用过程的细节
// 5.加载so文件
DalvikModule dm = vm.loadLibrary("yzwg", true); // 以后会动
dm.callJNI_OnLoad(emulator); // jni开发动态注册,会执行JNI_OnLoad,如果是动态注册,需要执行一下这个,如果静态注册,这个不需要执行,车智赢案例是静态注册
// 6.dm代表so文件,dm.getModule()得到module对象,基于module对象可以访问so中的成员。
module = dm.getModule(); // 把so文件加载到内存后,后期可以获取基地址,偏移量等,该变量代指so文件
}
@Override
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
switch (signature) {
case "com/twl/signer/YZWG->gContext:Landroid/content/Context;": {
return vm.resolveClass("android/content/Context").newObject(null);
}
}
return super.getStaticObjectField(vm, dvmClass, signature);
}
@Override
public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
switch (signature) {
case "android/content/pm/PackageManager->getPackagesForUid(I)[Ljava/lang/String;": {
int arg = varArg.getIntArg(0);
System.out.println("uid==>" + arg);
System.out.println("包名==>" + vm.getPackageName());
return new ArrayObject(new StringObject(vm, vm.getPackageName()));
}
}
return super.callObjectMethod(vm, dvmObject, signature, varArg);
}
@Override
public int callIntMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
switch (signature) {
case "java/lang/String->hashCode()I": {
String str = (String) dvmObject.getValue();
return str.hashCode();
}
}
return super.callIntMethod(vm, dvmObject, signature, varArg);
}
//2 sign 成员方法--》主要用来解密
public void get_sp() {
ArrayList<Object> list = new ArrayList<>(10);
list.add(vm.getJNIEnv());
list.add(0);
String arg1 = "client_info=%7B%22version%22%3A%2210%22%2C%22os%22%3A%22Android%22%2C%22start_time%22%3A%221772188217563%22%2C%22resume_time%22%3A%221772188217563%22%2C%22channel%22%3A%2228%22%2C%22model%22%3A%22google%7C%7CPixel+2%22%2C%22dzt%22%3A0%2C%22loc_per%22%3A0%2C%22uniqid%22%3A%22809b4bb9-33e9-4d7c-b59b-0a7d6f0ef0ee%22%2C%22oaid%22%3A%2200000000-0000-0000-0000-000000000000%22%2C%22oaid_honor%22%3A%2200000000-0000-0000-0000-000000000000%22%2C%22did%22%3A%22DUlRWq6jQRU_Glo4HL5qAwwsy7zpPrAM1600RFVsUldxNmpRUlVfR2xvNEhMNXFBd3dzeTd6cFByQU0xNjAwc2h1%22%2C%22tinker_id%22%3A%22Prod-arm64-v8a-release-13.141.1314110_0812-10-06-09%22%2C%22is_bg_req%22%3A0%2C%22network%22%3A%22wifi%22%2C%22operator%22%3A%22UNKNOWN%22%2C%22abi%22%3A1%7D&curidentity=0&data=%5B%7B%22action%22%3A%22app-active%22%2C%22time%22%3A1772188217717%2C%22p4%22%3A%22%22%2C%22p5%22%3A%221%22%2C%22p9%22%3A%22Prod-arm64-v8a-release-13.141.1314110_0812-10-06-09%22%7D%5D&req_time=1772188217741&uniqid=809b4bb9-33e9-4d7c-b59b-0a7d6f0ef0ee&v=13.141";
ByteArray byteArray = new ByteArray(vm, arg1.getBytes(StandardCharsets.UTF_8));
list.add(vm.addLocalObject(byteArray));
list.add(vm.addLocalObject(null));
Number number = module.callFunction(emulator, 0x209a4, list.toArray());
String result = vm.getObject(number.intValue()).getValue().toString();
System.out.println(result);
}
public void get_sig() {
ArrayList<Object> list = new ArrayList<>(10);
list.add(vm.getJNIEnv());
list.add(0);
String arg1 = "/api/zpCommon/batch/unloginStatisticsclient_info=%7B%22version%22%3A%2210%22%2C%22os%22%3A%22Android%22%2C%22start_time%22%3A%221772188217563%22%2C%22resume_time%22%3A%221772188217563%22%2C%22channel%22%3A%2228%22%2C%22model%22%3A%22google%7C%7CPixel+2%22%2C%22dzt%22%3A0%2C%22loc_per%22%3A0%2C%22uniqid%22%3A%22809b4bb9-33e9-4d7c-b59b-0a7d6f0ef0ee%22%2C%22oaid%22%3A%2200000000-0000-0000-0000-000000000000%22%2C%22oaid_honor%22%3A%2200000000-0000-0000-0000-000000000000%22%2C%22did%22%3A%22DUlRWq6jQRU_Glo4HL5qAwwsy7zpPrAM1600RFVsUldxNmpRUlVfR2xvNEhMNXFBd3dzeTd6cFByQU0xNjAwc2h1%22%2C%22tinker_id%22%3A%22Prod-arm64-v8a-release-13.141.1314110_0812-10-06-09%22%2C%22is_bg_req%22%3A0%2C%22network%22%3A%22wifi%22%2C%22operator%22%3A%22UNKNOWN%22%2C%22abi%22%3A1%7D&curidentity=0&data=%5B%7B%22action%22%3A%22app-active%22%2C%22time%22%3A1772188217717%2C%22p4%22%3A%22%22%2C%22p5%22%3A%221%22%2C%22p9%22%3A%22Prod-arm64-v8a-release-13.141.1314110_0812-10-06-09%22%7D%5D&req_time=1772188217741&uniqid=809b4bb9-33e9-4d7c-b59b-0a7d6f0ef0ee&v=13.141";
ByteArray byteArray = new ByteArray(vm, arg1.getBytes(StandardCharsets.UTF_8));
list.add(vm.addLocalObject(byteArray));
list.add(vm.addLocalObject(null));
Number number = module.callFunction(emulator, 0x21864, list.toArray());
DvmObject<?> retObj = vm.getObject(number.intValue());
byte[] resultBytes = (byte[]) retObj.getValue();
String result = new String(resultBytes, StandardCharsets.UTF_8);
System.out.println("结果: " + result);
}
public void decodeContent() {
byte[] encryptedData = new byte[]{
-10, 114, 76, 25, 44, -37, 100, 101, -124, -69, 64, -18, -101, -32, -119, 121, -125, -10, -36, 42, 125, 34, -68, -48, 102, 107, 91, 107, 88, -42, -125, -19, 69, -95, 90, -127, 56, -28, -108, -69, -101, -78, 1, -2, -70, -84, 12, -91, -106, 98, 27, 89, 71, -37, -87, -37, 71, 110, 111, -65, -22, 71, -16, 15, 111, -106, -30, -1, 8, 31, 46, -60, 38, -110, -21, 71, 126, 34, -16, 62, -106, -40, 16, -107, 2, 64, -65, -56, 71, -36, 85, 40, -3, 69, -71, -65, 24, -20, 4, 125, -114, -112, 126, 17, -28, -63, -100, 61, 58, -24, 6, -55, 87, -59, 124, -112, -26, 50, -41, 26, 25, -9, 33, -48, 80, 22, 46, -119, 112, 78, 5, 24, 96, -86, 36, 68, -71, -7, -90, -71, 111, 76, 87, 116, 56, -19, 2
};
ArrayList<Object> list = new ArrayList<>(10);
list.add(vm.getJNIEnv());
list.add(0);
ByteArray dataArray = new ByteArray(vm, encryptedData);
list.add(vm.addLocalObject(dataArray));
list.add(null);
list.add(0);
list.add(1);
list.add(0);
Number number = module.callFunction(emulator, 0x24dc8, list.toArray());
DvmObject<?> retObj = vm.getObject(number.intValue());
byte[] resultBytes = (byte[]) retObj.getValue();
String result = new String(resultBytes, StandardCharsets.UTF_8);
System.out.println("解密结果: " + result);
}
// 3 main方法---》右键直接运行
public static void main(String[] args) {
YZWG yzwg = new YZWG();
yzwg.get_sp();
yzwg.get_sig();
yzwg.decodeContent();
}
@Override
public FileResult resolve(Emulator emulator, String pathname, int oflags) {
System.out.println("pathname open:" + pathname);
return null;
}
}
// zwp_NCTRJTSwi3DewMDkHB6GvU2BAYbyceqJ44s9CxuyL65jv20XaVYg1XpFLNVaF7LolNJtanuLr-ARNMt36qI3RFO3xe8JyDZtt-TQVeh_TOtTgNw-LeFjEs8hx0tJoj7v9ncKmYZWKNui4mO4EMBKqCp54Wi6L8MQyX1JatkuDl9Ec4gZFqDhg7Z1SBl0Q5EvXDZ9Hpb4hZrrp1qpTkEGTjmODhup8HF6VZa0VcxsMFLprFD40lIaRNgU9G7mUIMTy2f0MKwht_lGzm2bw9GnXzCQyGB8Lr9XYvIqmMmmQ8DHWVid8LshMJ0kV52zxZ2rg44Dxcb_cjVfYQd3k5ucb_MgYgZLVJLM4QfkktgfBS3MfS4m3TyKtzi7q5zNIHc52z2fsjpoSrdhmQzh6Bu6-ZMzHJWJ_nbibMppOwVp0HgqJSYdAgYwSwgrTFyUbxw15pDCp830uxCbGNwcHSQjXxi2QgRWAPNLA5ueAa07KMmuMDBrn66vKAqzehxbIFtE4CcoTGstb34UnWCQZ9CHK-DZwfQSrZu_B5i2KIvvFqQG5Fq-ycwEKSZhUmhQJuvLzl5vd6wwh08SdOWSVXsdhHdJaBhEITwc3qRr9K--K8P1fbqdFwd92GQtBdMYqXao5TbaeXuTB0nPAKVcLm67Itr35_UjlCnqciQ_D1gHezlwHiz9xMIosw5DE4hmRLXi_2s6Qm-ohQH9ooezUsQlaPNOPcYqGYA7kqAwhU1qnbISOAhNHjNNCrD-KeWeWb1UJPyg1CE5GJ5KPfTztHGY6zfoJWP7ZKkxM9aOiLMEueJjsCVuE3BpE8h-EMCAeXo_4EXFkeLihAkaNhC9wsphUwZIIS6wTUNQ8yYBBw~~
// zwp_NCTRJTSwi3DewMDkHB6GvU2BAYbyceqJ44s9CxuyL65jv20XaVYg1XpFLNVaF7LolNJtanuLr-ARNMt36qI3RFO3xe8JyDZtt-TQVeh_TOtTgNw-LeFjEs8hx0tJoj7v9ncKmYZWKNui4mO4EMBKqCp54Wi6L8MQyX1JatkuDl9Ec4gZFqDhg7Z1SBl0Q5EvXDZ9Hpb4hZrrp1qpTkEGTjmODhup8HF6VZa0VcxsMFLprFD40lIaRNgU9G7mUIMTy2f0MKwht_lGzm2bw9GnXzCQyGB8Lr9XYvIqmMmmQ8DHWVid8LshMJ0kV52zxZ2rg44Dxcb_cjVfYQd3k5ucb_MgYgZLVJLM4QfkktgfBS3MfS4m3TyKtzi7q5zNIHc52z2fsjpoSrdhmQzh6Bu6-ZMzHJWJ_nbibMppOwVp0HgqJSYdAgYwSwgrTFyUbxw15pDCp830uxCbGNwcHSQjXxi2QgRWAPNLA5ueAa07KMmuMDBrn66vKAqzehxbIFtE4CcoTGstb34UnWCQZ9CHK-DZwfQSrZu_B5i2KIvvFqQG5Fq-ycwEKSZhUmhQJuvLzl5vd6wwh08SdOWSVXsdhHdJaBhEITwc3qRr9K--K8P1fbqdFwd92GQtBdMYqXao5TbaeXuTB0nPAKVcLm67Itr35_UjlCnqciQ_D1gHezlwHiz9xMIosw5DE4hmRLXi_2s6Qm-ohQH9ooezUsQlaPNOPcYqGYA7kqAwhU1qnbISOAhNHjNNCrD-KeWeWb1UJPyg1CE5GJ5KPfTztHGY6zfoJWP7ZKkxM9aOiLMEueJjsCVuE3BpE8h-EMCAeXo_4EXFkeLihAkaNhC9wsphUwZIIS6wTUNQ8yYBBw~~

算法分析#

首先,trace 一份日志,管他用不用的上,用不上最好,分析不出来了,就看日志

public void traceCode() {
String traceFile = "unidbg-android/src/test/java/com/boss/traceCode.log"; // 输出的路径
PrintStream traceStream = null; // 打印流
try {
traceStream = new PrintStream(new FileOutputStream(traceFile), true);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
// traceCode 对代码进行监控
emulator.traceCode(module.base, module.base + module.size).setRedirect(traceStream);
}

参数sig#

首先先来一个简单的参数吧, V3.0f709c133cb9964973c96a2b036376fad 其中 V3.0 是固定的,后面拼接了一个 32 位的字符串,有极大概率就是一个 md5 算法

他返回的是一个字节数组,跳转到 0x21fdc

最后调用 SetByteArrayRegion 返回数组,回顾一下 JNI 的语法

  • jbyteArray javaArray = (*env)->NewByteArray(env, len); 创建字节数组
  • (*env)->SetByteArrayRegion(env, javaArray, 0, len, (jbyte*)result); 将 C 数据复制到 Java 数组
  • GetByteArrayRegion 获取 Java 数组的数据到 C

所以,在这儿就可以看出: v29 是源数据, v27 是复制到的索引/长度, v4 是目标数组
所以就需要看 v29 是哪儿来的?v29 由 v27 赋值, v27sub_1C38C 函数的返回值,所以就需要 hook 一下这个子函数了

emulator.attach().addBreakPoint(module.base+0x1C38C);

结果如下:

Terminal window
mx0
>-----------------------------------------------------------------------------<
[09:28:54 820]x0=RW@0x123d13c8[libyzwg.so]0x3d13c8, md5=432ae95099f42e1e651cece1e929145a, hex=56332e30000000004241534536345f53495a45203d205b25645d2c20436f6e74656e744c656e203d205b25645d00000000000000000000004465636f64654f7574707574206d616c6c6f63206661696c21000000000000004e6f7420737570706f727420656e6372797074696f6e2074
size: 112
0000: 56 33 2E 30 00 00 00 00 42 41 53 45 36 34 5F 53 V3.0....BASE64_S
0010: 49 5A 45 20 3D 20 5B 25 64 5D 2C 20 43 6F 6E 74 IZE = [%d], Cont
0020: 65 6E 74 4C 65 6E 20 3D 20 5B 25 64 5D 00 00 00 entLen = [%d]...
0030: 00 00 00 00 00 00 00 00 44 65 63 6F 64 65 4F 75 ........DecodeOu
0040: 74 70 75 74 20 6D 61 6C 6C 6F 63 20 66 61 69 6C tput malloc fail
0050: 21 00 00 00 00 00 00 00 4E 6F 74 20 73 75 70 70 !.......Not supp
0060: 6F 72 74 20 65 6E 63 72 79 70 74 69 6F 6E 20 74 ort encryption t
^-----------------------------------------------------------------------------^
mx1
>-----------------------------------------------------------------------------<
[09:28:59 041]x1=RW@0x12713060, md5=6621f88cccba6b7d2442b768b59782f3, hex=66373039633133336362393936343937336339366132623033363337366661640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
size: 112
0000: 66 37 30 39 63 31 33 33 63 62 39 39 36 34 39 37 f709c133cb996497
0010: 33 63 39 36 61 32 62 30 33 36 33 37 36 66 61 64 3c96a2b036376fad
0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
^-----------------------------------------------------------------------------^

x0 我看不出来是个什么东西,x1 我知道就是最后生成的 sig 中 V3.0 后面拼接的字符串,所以,需要分析的就是 x1 寄存器,即 v24 是在哪儿生成的了
v24 是由上面的 sub_1C714 这个子函数生成的,继续 hook 这个子函数
x0 是地址,那么 x1 肯定就是代表 x0 这个寄存器数据的长度了

Terminal window
mx0 0x433
>-----------------------------------------------------------------------------<
[09:42:01 665]x0=RW@0x12719000, hex=2f6170692f7a70436f6d6d6f6e2f62617463682f756e6c6f67696e53746174697374696373636c69656e745f696e666f3d25374225323276657273696f6e25323225334125323231302532322532432532326f73253232253341253232416e64726f696425323225324325323273746172745f74696d6525323225334125323231373732313838323137353633253232253243253232726573756d655f74696d65253232253341253232313737323138383231373536332532322532432532326368616e6e656c25323225334125323232382532322532432532326d6f64656c253232253341253232676f6f676c65253743253743506978656c2b32253232253243253232647a74253232253341302532432532326c6f635f70657225323225334130253243253232756e6971696425323225334125323238303962346262392d333365392d346437632d623539622d3061376436663065663065652532322532432532326f61696425323225334125323230303030303030302d303030302d303030302d303030302d3030303030303030303030302532322532432532326f6169645f686f6e6f7225323225334125323230303030303030302d303030302d303030302d303030302d30303030303030303030303025323225324325323264696425323225334125323244556c525771366a5152555f476c6f34484c35714177777379377a705072414d3136303052465673556c64784e6d7052556c5666523278764e45684d4e5846426433647a6554643663464279515530784e6a41776332683125323225324325323274696e6b65725f696425323225334125323250726f642d61726d36342d7638612d72656c656173652d31332e3134312e313331343131305f303831322d31302d30362d303925323225324325323269735f62675f726571253232253341302532432532326e6574776f726b253232253341253232776966692532322532432532326f70657261746f72253232253341253232554e4b4e4f574e25323225324325323261626925323225334131253744266375726964656e746974793d3026646174613d253542253742253232616374696f6e2532322533412532326170702d61637469766525323225324325323274696d65253232253341313737323138383231373731372532432532327034253232253341253232253232253243253232703525323225334125323231253232253243253232703925323225334125323250726f642d61726d36342d7638612d72656c656173652d31332e3134312e313331343131305f303831322d31302d30362d3039253232253744253544267265715f74696d653d3137373231383832313737343126756e697169643d38303962346262392d333365392d346437632d623539622d30613764366630656630656526763d31332e3134316133303866333632386233663339663764333563646562656236393230653231, md5=f709c133cb9964973c96a2b036376fad
size: 1075
0000: 2F 61 70 69 2F 7A 70 43 6F 6D 6D 6F 6E 2F 62 61 /api/zpCommon/ba
0010: 74 63 68 2F 75 6E 6C 6F 67 69 6E 53 74 61 74 69 tch/unloginStati
0020: 73 74 69 63 73 63 6C 69 65 6E 74 5F 69 6E 66 6F sticsclient_info
0030: 3D 25 37 42 25 32 32 76 65 72 73 69 6F 6E 25 32 =%7B%22version%2
0040: 32 25 33 41 25 32 32 31 30 25 32 32 25 32 43 25 2%3A%2210%22%2C%
0050: 32 32 6F 73 25 32 32 25 33 41 25 32 32 41 6E 64 22os%22%3A%22And
0060: 72 6F 69 64 25 32 32 25 32 43 25 32 32 73 74 61 roid%22%2C%22sta
0070: 72 74 5F 74 69 6D 65 25 32 32 25 33 41 25 32 32 rt_time%22%3A%22
0080: 31 37 37 32 31 38 38 32 31 37 35 36 33 25 32 32 1772188217563%22
0090: 25 32 43 25 32 32 72 65 73 75 6D 65 5F 74 69 6D %2C%22resume_tim
00A0: 65 25 32 32 25 33 41 25 32 32 31 37 37 32 31 38 e%22%3A%22177218
00B0: 38 32 31 37 35 36 33 25 32 32 25 32 43 25 32 32 8217563%22%2C%22
00C0: 63 68 61 6E 6E 65 6C 25 32 32 25 33 41 25 32 32 channel%22%3A%22
00D0: 32 38 25 32 32 25 32 43 25 32 32 6D 6F 64 65 6C 28%22%2C%22model
00E0: 25 32 32 25 33 41 25 32 32 67 6F 6F 67 6C 65 25 %22%3A%22google%
00F0: 37 43 25 37 43 50 69 78 65 6C 2B 32 25 32 32 25 7C%7CPixel+2%22%
0100: 32 43 25 32 32 64 7A 74 25 32 32 25 33 41 30 25 2C%22dzt%22%3A0%
0110: 32 43 25 32 32 6C 6F 63 5F 70 65 72 25 32 32 25 2C%22loc_per%22%
0120: 33 41 30 25 32 43 25 32 32 75 6E 69 71 69 64 25 3A0%2C%22uniqid%
0130: 32 32 25 33 41 25 32 32 38 30 39 62 34 62 62 39 22%3A%22809b4bb9
0140: 2D 33 33 65 39 2D 34 64 37 63 2D 62 35 39 62 2D -33e9-4d7c-b59b-
0150: 30 61 37 64 36 66 30 65 66 30 65 65 25 32 32 25 0a7d6f0ef0ee%22%
0160: 32 43 25 32 32 6F 61 69 64 25 32 32 25 33 41 25 2C%22oaid%22%3A%
0170: 32 32 30 30 30 30 30 30 30 30 2D 30 30 30 30 2D 2200000000-0000-
0180: 30 30 30 30 2D 30 30 30 30 2D 30 30 30 30 30 30 0000-0000-000000
0190: 30 30 30 30 30 30 25 32 32 25 32 43 25 32 32 6F 000000%22%2C%22o
01A0: 61 69 64 5F 68 6F 6E 6F 72 25 32 32 25 33 41 25 aid_honor%22%3A%
01B0: 32 32 30 30 30 30 30 30 30 30 2D 30 30 30 30 2D 2200000000-0000-
01C0: 30 30 30 30 2D 30 30 30 30 2D 30 30 30 30 30 30 0000-0000-000000
01D0: 30 30 30 30 30 30 25 32 32 25 32 43 25 32 32 64 000000%22%2C%22d
01E0: 69 64 25 32 32 25 33 41 25 32 32 44 55 6C 52 57 id%22%3A%22DUlRW
01F0: 71 36 6A 51 52 55 5F 47 6C 6F 34 48 4C 35 71 41 q6jQRU_Glo4HL5qA
0200: 77 77 73 79 37 7A 70 50 72 41 4D 31 36 30 30 52 wwsy7zpPrAM1600R
0210: 46 56 73 55 6C 64 78 4E 6D 70 52 55 6C 56 66 52 FVsUldxNmpRUlVfR
0220: 32 78 76 4E 45 68 4D 4E 58 46 42 64 33 64 7A 65 2xvNEhMNXFBd3dze
0230: 54 64 36 63 46 42 79 51 55 30 78 4E 6A 41 77 63 Td6cFByQU0xNjAwc
0240: 32 68 31 25 32 32 25 32 43 25 32 32 74 69 6E 6B 2h1%22%2C%22tink
0250: 65 72 5F 69 64 25 32 32 25 33 41 25 32 32 50 72 er_id%22%3A%22Pr
0260: 6F 64 2D 61 72 6D 36 34 2D 76 38 61 2D 72 65 6C od-arm64-v8a-rel
0270: 65 61 73 65 2D 31 33 2E 31 34 31 2E 31 33 31 34 ease-13.141.1314
0280: 31 31 30 5F 30 38 31 32 2D 31 30 2D 30 36 2D 30 110_0812-10-06-0
0290: 39 25 32 32 25 32 43 25 32 32 69 73 5F 62 67 5F 9%22%2C%22is_bg_
02A0: 72 65 71 25 32 32 25 33 41 30 25 32 43 25 32 32 req%22%3A0%2C%22
02B0: 6E 65 74 77 6F 72 6B 25 32 32 25 33 41 25 32 32 network%22%3A%22
02C0: 77 69 66 69 25 32 32 25 32 43 25 32 32 6F 70 65 wifi%22%2C%22ope
02D0: 72 61 74 6F 72 25 32 32 25 33 41 25 32 32 55 4E rator%22%3A%22UN
02E0: 4B 4E 4F 57 4E 25 32 32 25 32 43 25 32 32 61 62 KNOWN%22%2C%22ab
02F0: 69 25 32 32 25 33 41 31 25 37 44 26 63 75 72 69 i%22%3A1%7D&curi
0300: 64 65 6E 74 69 74 79 3D 30 26 64 61 74 61 3D 25 dentity=0&data=%
0310: 35 42 25 37 42 25 32 32 61 63 74 69 6F 6E 25 32 5B%7B%22action%2
0320: 32 25 33 41 25 32 32 61 70 70 2D 61 63 74 69 76 2%3A%22app-activ
0330: 65 25 32 32 25 32 43 25 32 32 74 69 6D 65 25 32 e%22%2C%22time%2
0340: 32 25 33 41 31 37 37 32 31 38 38 32 31 37 37 31 2%3A177218821771
0350: 37 25 32 43 25 32 32 70 34 25 32 32 25 33 41 25 7%2C%22p4%22%3A%
0360: 32 32 25 32 32 25 32 43 25 32 32 70 35 25 32 32 22%22%2C%22p5%22
0370: 25 33 41 25 32 32 31 25 32 32 25 32 43 25 32 32 %3A%221%22%2C%22
0380: 70 39 25 32 32 25 33 41 25 32 32 50 72 6F 64 2D p9%22%3A%22Prod-
0390: 61 72 6D 36 34 2D 76 38 61 2D 72 65 6C 65 61 73 arm64-v8a-releas
03A0: 65 2D 31 33 2E 31 34 31 2E 31 33 31 34 31 31 30 e-13.141.1314110
03B0: 5F 30 38 31 32 2D 31 30 2D 30 36 2D 30 39 25 32 _0812-10-06-09%2
03C0: 32 25 37 44 25 35 44 26 72 65 71 5F 74 69 6D 65 2%7D%5D&req_time
03D0: 3D 31 37 37 32 31 38 38 32 31 37 37 34 31 26 75 =1772188217741&u
03E0: 6E 69 71 69 64 3D 38 30 39 62 34 62 62 39 2D 33 niqid=809b4bb9-3
03F0: 33 65 39 2D 34 64 37 63 2D 62 35 39 62 2D 30 61 3e9-4d7c-b59b-0a
0400: 37 64 36 66 30 65 66 30 65 65 26 76 3D 31 33 2E 7d6f0ef0ee&v=13.
0410: 31 34 31 61 33 30 38 66 33 36 32 38 62 33 66 33 141a308f3628b3f3
0420: 39 66 37 64 33 35 63 64 65 62 65 62 36 39 32 30 9f7d35cdebeb6920
0430: 65 32 31 e21
^-----------------------------------------------------------------------------^

他在明文后面拼接了一个字符串,你说这个会不会是 md5 算法的盐值,这完全有可能啊,可以去测试一下

我去,还真是,我经过不断的测试发现这个盐值还是固定的,那就不用继续往里面跟了
总结: sig=V3.0+hashlib.md5(明文+a308f3628b3f39f7d35cdebeb6920e21)

参数 sp#

sig 的部分看完了,下面就来看 sp 这个参数,回到 unidbg 最后生成的代码逻辑中

来到 0x21020 这个偏移

发现最后返回的是 v23 这个字符串,v23 最开始是分配了 v19 长度的字节空间,经过了 sub_29E90sub_1CEB8 这两个子函数,得到了最终的结果,那么这两个子函数就很可疑了,它在里面做了什么就很有探究的必要了
首先来看一下 sub_29E90 这个子函数

在这儿看到了码表,这很有可能就是一个 base64 编码啊,可以去验证一下

emulator.attach().addBreakPoint(module.base+0x29E90);
debugger break at: 0x12029e90 @ Runnable|Function64 address=0x120209a4, arguments=[unidbg@0xfffe1640, 0, 1332668132, 0]
>>> x0=0x1271e000 x1=0x12719000 x2=0x28f x3=0x12702ae0 x4=0x12606000 x5=0x1 x6=0x0 x7=0x0 x8=0x0 x9=0xaaaaaaaaaaaaaaab x10=0x0 x11=0x12702260 x12=0x0 x13=0x0 x14=0x1
>>> x15=0x8 x16=0x9 x17=0x0 x18=0x1e x19=0x449e509f x20=0x0 x21=0xfffe1640 x22=0x4f6ee6e4 x23=0xdb13f0ef x24=0x13dbc278 x25=0x32c11dfa x26=0xe4fff708 x27=0x7a1d9687 x28=0x36d fp=0x1271e000
>>> q0=0x1300000000000000130(1.5E-321, 1.5E-321) q1=0x50000000000000004000(8.0948E-320, 1.01185E-319) q2=0x0(0.0) q3=0x70000000000000006(3.0E-323, 3.5E-323) q4=0x10000000000000001(4.9E-324, 4.9E-324) q5=0x40000000000000004(2.0E-323, 2.0E-323) q6=0x20000000000000002(1.0E-323, 1.0E-323) q7=0x1310000000000000131(1.507E-321, 1.507E-321) q8=0x0(0.0) q9=0x0(0.0) q10=0x0(0.0) q11=0x0(0.0) q12=0x0(0.0) q13=0x0(0.0) q14=0x0(0.0) q15=0x0(0.0)
>>> q16=0x51310000000000004131(8.2455E-320, 1.0269E-319) q17=0x0(0.0) q18=0x51310000000000004131(8.2455E-320, 1.0269E-319) q19=0xfdf1c3ced3ceced9(-4.647269563198482E298) q20=0x7d71434e534e4e59(1.7640457944868906E296) q21=0x0(0.0) q22=0x0(0.0) q23=0x0(0.0) q24=0x0(0.0) q25=0x0(0.0) q26=0x0(0.0) q27=0x0(0.0) q28=0x0(0.0) q29=0x0(0.0) q30=0x0(0.0) q31=0x0(0.0)
LR=RX@0x12020ff8[libyzwg.so]0x20ff8
SP=0xe4fff4f0
PC=RX@0x12029e90[libyzwg.so]0x29e90
nzcv: N=1, Z=0, C=0, V=0, EL0, use SP_EL0
[libyzwg.so 0x029e8c] [c0035fd6] 0x12029e8c: "ret"
=> *[libyzwg.so*0x029e90]*[ffc302d1]*0x12029e90:*"sub sp, sp, #0xb0"
[libyzwg.so 0x029e94] [fc6f05a9] 0x12029e94: "stp x28, x27, [sp, #0x50]"
[libyzwg.so 0x029e98] [fa6706a9] 0x12029e98: "stp x26, x25, [sp, #0x60]"
[libyzwg.so 0x029e9c] [f85f07a9] 0x12029e9c: "stp x24, x23, [sp, #0x70]"
[libyzwg.so 0x029ea0] [f65708a9] 0x12029ea0: "stp x22, x21, [sp, #0x80]"
[libyzwg.so 0x029ea4] [f44f09a9] 0x12029ea4: "stp x20, x19, [sp, #0x90]"
[libyzwg.so 0x029ea8] [fd7b0aa9] 0x12029ea8: "stp x29, x30, [sp, #0xa0]"
[libyzwg.so 0x029eac] [48080051] 0x12029eac: "sub w8, w2, #2"
[libyzwg.so 0x029eb0] [e27f0229] 0x12029eb0: "stp w2, wzr, [sp, #0x10]"
[libyzwg.so 0x029eb4] [e80f00b9] 0x12029eb4: "str w8, [sp, #0xc]"
[libyzwg.so 0x029eb8] [391d00f0] 0x12029eb8: "adrp x25, #0x123d0000"
[libyzwg.so 0x029ebc] [3a1d00f0] 0x12029ebc: "adrp x26, #0x123d0000"
[libyzwg.so 0x029ec0] [39db43f9] 0x12029ec0: "ldr x25, [x25, #0x7b0]"
[libyzwg.so 0x029ec4] [5a6344f9] 0x12029ec4: "ldr x26, [x26, #0x8c0]"
[libyzwg.so 0x029ec8] [ea03024b] 0x12029ec8: "neg w10, w2"
[libyzwg.so 0x029ecc] [f00300aa] 0x12029ecc: "mov x16, x0"
mx0
>-----------------------------------------------------------------------------<
[18:18:49 893]x0=RW@0x1271e000, md5=c20019258ca235d2408334dfbc5e67e3, hex=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
size: 112
0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
^-----------------------------------------------------------------------------^
mx1 0x28f
>-----------------------------------------------------------------------------<
[18:19:02 219]x1=RW@0x12719000, md5=e216af4d82acea61f6ec959b5dc2a25a, hex=cf0a7f3424d12534b08b70dec0c0e41c1e86bd4d810186f271ea89e38b3d0b1bb22fae63bf6d17695620d57a452cd55a17b2e894d26d6a7b8bafe01134cb77eaa2374453b7c5ef09c8366db7e4d055e87f4ceb5380dc3e2de16312cf21c74b49a23eeff6770a99865628dba2e263b810c04aa82a79e168ba2fc310c97d496ad92e0e5f4473881916a0e183b67548197443912f5c367d1e96f8859aeba75aa94e41064e398e0e1ba9f0717a5596b455cc6c3052e9ac50f8d2521a44d814f46ee6508313cb67f430ac21b7f946ce6d9bc3d1a75f3090c8607c2ebf5762f22a98c9a643c0c759589df0bb21309d24579db3c59dab838e03c5c6ff72355f610777939b9c6ff32062064b5492cce107e492d81f052dcc7d2e26dd3c8ab738bbab9ccd207739db3d9fb23a684ab761990ce1e81bbaf993331c9589fe76e26cca693b0569d0782a25261d0206304b082b4c5c946f1c35e690c2a7cdf4bb109b18dc1c1d24235f18b642045600f34b039b9e01ad3b28c9ae30306b9faeaf280ab37a1c5b205b44e027284c6b2d6f7e149d609067d0872be0d9c1f412ad9bbf0798b6288bef16a406e45abec9cc0429266152685026ebcbce5e6f77ac30874f1274e592557b1d847749681844213c1cdea46bf4afbe2bc3f57dba9d17077dd8642d05d318a976a8e536da797b930749cf00a55c2e6ebb22daf7e7f5239429ea72243f0f58077b39701e2cfdc4c228b30e4313886644b5e2ff6b3a426fa88501fda287b352c42568f34e3dc62a19803b92a030854d6a9db21238084d1e334d0ab0fe29e59e59bd5424fca0d42139189e4a3df4f3b47198eb37e82563fb64a93133d68e88b304b9e263b0256e13706913c87e10c080797a3fe045c591e2e284091a3610bdc2ca61530648212eb04d4350f3260107
size: 655
0000: CF 0A 7F 34 24 D1 25 34 B0 8B 70 DE C0 C0 E4 1C ...4$.%4..p.....
0010: 1E 86 BD 4D 81 01 86 F2 71 EA 89 E3 8B 3D 0B 1B ...M....q....=..
0020: B2 2F AE 63 BF 6D 17 69 56 20 D5 7A 45 2C D5 5A ./.c.m.iV .zE,.Z
0030: 17 B2 E8 94 D2 6D 6A 7B 8B AF E0 11 34 CB 77 EA .....mj{....4.w.
0040: A2 37 44 53 B7 C5 EF 09 C8 36 6D B7 E4 D0 55 E8 .7DS.....6m...U.
0050: 7F 4C EB 53 80 DC 3E 2D E1 63 12 CF 21 C7 4B 49 .L.S..>-.c..!.KI
0060: A2 3E EF F6 77 0A 99 86 56 28 DB A2 E2 63 B8 10 .>..w...V(...c..
0070: C0 4A A8 2A 79 E1 68 BA 2F C3 10 C9 7D 49 6A D9 .J.*y.h./...}Ij.
0080: 2E 0E 5F 44 73 88 19 16 A0 E1 83 B6 75 48 19 74 .._Ds.......uH.t
0090: 43 91 2F 5C 36 7D 1E 96 F8 85 9A EB A7 5A A9 4E C./\6}.......Z.N
00A0: 41 06 4E 39 8E 0E 1B A9 F0 71 7A 55 96 B4 55 CC A.N9.....qzU..U.
00B0: 6C 30 52 E9 AC 50 F8 D2 52 1A 44 D8 14 F4 6E E6 l0R..P..R.D...n.
00C0: 50 83 13 CB 67 F4 30 AC 21 B7 F9 46 CE 6D 9B C3 P...g.0.!..F.m..
00D0: D1 A7 5F 30 90 C8 60 7C 2E BF 57 62 F2 2A 98 C9 .._0..`|..Wb.*..
00E0: A6 43 C0 C7 59 58 9D F0 BB 21 30 9D 24 57 9D B3 .C..YX...!0.$W..
00F0: C5 9D AB 83 8E 03 C5 C6 FF 72 35 5F 61 07 77 93 .........r5_a.w.
0100: 9B 9C 6F F3 20 62 06 4B 54 92 CC E1 07 E4 92 D8 ..o. b.KT.......
0110: 1F 05 2D CC 7D 2E 26 DD 3C 8A B7 38 BB AB 9C CD ..-.}.&.<..8....
0120: 20 77 39 DB 3D 9F B2 3A 68 4A B7 61 99 0C E1 E8 w9.=..:hJ.a....
0130: 1B BA F9 93 33 1C 95 89 FE 76 E2 6C CA 69 3B 05 ....3....v.l.i;.
0140: 69 D0 78 2A 25 26 1D 02 06 30 4B 08 2B 4C 5C 94 i.x*%&...0K.+L\.
0150: 6F 1C 35 E6 90 C2 A7 CD F4 BB 10 9B 18 DC 1C 1D o.5.............
0160: 24 23 5F 18 B6 42 04 56 00 F3 4B 03 9B 9E 01 AD $#_..B.V..K.....
0170: 3B 28 C9 AE 30 30 6B 9F AE AF 28 0A B3 7A 1C 5B ;(..00k...(..z.[
0180: 20 5B 44 E0 27 28 4C 6B 2D 6F 7E 14 9D 60 90 67 [D.'(Lk-o~..`.g
0190: D0 87 2B E0 D9 C1 F4 12 AD 9B BF 07 98 B6 28 8B ..+...........(.
01A0: EF 16 A4 06 E4 5A BE C9 CC 04 29 26 61 52 68 50 .....Z....)&aRhP
01B0: 26 EB CB CE 5E 6F 77 AC 30 87 4F 12 74 E5 92 55 &...^ow.0.O.t..U
01C0: 7B 1D 84 77 49 68 18 44 21 3C 1C DE A4 6B F4 AF {..wIh.D!<...k..
01D0: BE 2B C3 F5 7D BA 9D 17 07 7D D8 64 2D 05 D3 18 .+..}....}.d-...
01E0: A9 76 A8 E5 36 DA 79 7B 93 07 49 CF 00 A5 5C 2E .v..6.y{..I...\.
01F0: 6E BB 22 DA F7 E7 F5 23 94 29 EA 72 24 3F 0F 58 n."....#.).r$?.X
0200: 07 7B 39 70 1E 2C FD C4 C2 28 B3 0E 43 13 88 66 .{9p.,...(..C..f
0210: 44 B5 E2 FF 6B 3A 42 6F A8 85 01 FD A2 87 B3 52 D...k:Bo.......R
0220: C4 25 68 F3 4E 3D C6 2A 19 80 3B 92 A0 30 85 4D .%h.N=.*..;..0.M
0230: 6A 9D B2 12 38 08 4D 1E 33 4D 0A B0 FE 29 E5 9E j...8.M.3M...)..
0240: 59 BD 54 24 FC A0 D4 21 39 18 9E 4A 3D F4 F3 B4 Y.T$...!9..J=...
0250: 71 98 EB 37 E8 25 63 FB 64 A9 31 33 D6 8E 88 B3 q..7.%c.d.13....
0260: 04 B9 E2 63 B0 25 6E 13 70 69 13 C8 7E 10 C0 80 ...c.%n.pi..~...
0270: 79 7A 3F E0 45 C5 91 E2 E2 84 09 1A 36 10 BD C2 yz?.E.......6...
0280: CA 61 53 06 48 21 2E B0 4D 43 50 F3 26 01 07 .aS.H!..MCP.&..
^-----------------------------------------------------------------------------^

x0 就是缓冲区,用于存放结果的地方,x1 应该就是明文数据,x2 是明文的长度,去 cyberchef 里面验证一下:

确实能够得到结果,但是貌似跟我们 unidbg 模拟出来的结果不太一样啊

感觉是码表改变了,但是我确实在 sub_29E90 这个函数中发现的就是标准的 base64 编码的字符串啊,那是为什么,或许是不是它是将编码进行替换是在其他函数中
继续运行 sub_29E90 这个函数

发现他确实是标准的 base64 编码,那么很有可能就是在 sub_1CEB8 子函数中做了处理
进去 sub_1CEB8 发现它做了一些单字节的替换
总之,结合上面文本比对的情况,可以推断出:

Terminal window
/ ==> _
+ ==> -
= ==> ~

也就是说,我可以将码表进行替换能达到同样的效果:
码表就可以替换成 A-Za-z0-9-_~,接着,就需要知道 base64 编码中的明文是什么了,这就需要向上进行追溯

发现 sub_2E91C 在对这个参数进行操作,hook 一下这个地址,看一下它做了哪些操作

emulator.attach().addBreakPoint(module.base+0x2E91C);

第一个参数

第二个参数,第三个参数(这两个参数是一样的)

第四个参数是长度,接着我看一下返回值是啥
这个也确实是 base64 编码的明文数据,所以说这个地方是对的,需要分析一下这个函数干了什么
跟进 sub_2E91C ,发现有点像 rc4 算法,先来看一下标准的 rc4 算法缓缓:

def _prga(self):
"""伪随机生成算法(生成一个字节)"""
self.i = (self.i + 1) % 256
self.j = (self.j + self.S[self.i]) % 256
self.S[self.i], self.S[self.j] = self.S[self.j], self.S[self.i]
t = (self.S[self.i] + self.S[self.j]) % 256
return self.S[t]

去源码看到的对应关系如下:
反正有极大概率可以推断出这个就是一个 rc4 的加密算法,rc4 算法,加密/解密是一样的,可能返回值解密的话也会走这个逻辑说不定
既然知道了这个一个 rc4 算法,就需要去找 S 盒和密钥,S 盒不知道有没有魔改,或者说是如何进行魔改的,回到函数外
sub_2E680 这个函数也会处理 S 盒,也许它就是 S 盒的初始化函数,那另外一个 s 这个参数呢?密钥一般是跟 S 盒一起的,v53 应该就是密钥的长度,姑且就论 S 盒没有魔改,s 就是密钥,v53 就是密钥的长度,后面去验证一下就知道了
我去,这个不就是之前 sig 这个参数 md5 的盐值吗?这能一样吗?验证一下就知道了

没毛病,就是这个,好的,还需要继续找这个明文是从哪儿来的,找 text 的交叉引用

来到 sub_1D444 这个子函数,v47 不知道是个啥,这里的 text 才分配内存,进去应该啥都没有,是缓冲区,用于存放结果,v49 应该就是这个明文的长度,size 不知道是啥,目前先猜测这些东西

emulator.attach().addBreakPoint(module.base+0x1D444);

先看一下第一个参数,是明文

第二个参数确实是缓冲区,用于存放结果

第三个参数是明文的长度
第四个参数未知,它也是一个长度值,但是不知道是什么的长度,加密之后结果的长度?这个还需要去进行验证才知道
继续往上面看

size 是经过 LZ4_compressBound 方法,将 v49 也就是明文的长度传进去,这个是做了什么事情啊,而且 LZ4_compressBound 看起来像个压缩算法,这个算法是什么啊,没见过,可以问下 ai,去查一下

所以说这个函数就是给压缩后的数据分配空间的,进去看一下它是什么逻辑
说白了,也就是给明文扩展长度,用于压缩后的数据,既然知道了它的用途,接下来就应该去 sub_1D444 这个子函数中分析一下,但是暂时先按下不表,先看一下标准的 lz4 压缩算法,跟这个 app 得到的压缩算法生成的有什么不同

发现 app 魔改了头部的 24 个字节的数据,其实后面是一样的,如果时间充足的话可以慢慢比对,我目测是一样的
也就是说 sub_1D444 这个子函数魔改了头部的 24 个字节的数据,接下来可以去看看这个子函数的逻辑了

那现在唯一的问题就是需要去看一下 LZ4_compress_limitedOutput 这个函数干了什么,进去之后就变成了 LZ4_compress_fast 这个函数
但是也不是说非要进去看是什么逻辑,我已经猜了是压缩处理后数据的长度,那么我只需要验证就可以了啊

hook 之后,发现是 0x277,转换成十进制就是 631,总的压缩长度是 655,减去单独处理的 24 个字节,剩下的刚好就是 631,即 0x277,所以,sp 所有的逻辑就全都分析完了
下面来总结一下:

  • 明文数据通过魔改的 lz4(魔改了头部的 24 个字节),得到压缩后的数据
  • 将压缩后的数据经过 rc4 加密,密钥是上面 sig 参数,md5 算法的盐值,得到加密后的数据
  • 在将加密后的数据进行 base64 编码(码表是进行魔改的),通过这样编码之后就得到了最终的 sp 参数的数据

返回值解密#

返回值在打印数据之前调用的 JNI 函数是 GetByteArrayElements,这个函数好像不能像 NewStringUTF 一样对结果进行定位

但是之前好像 trace 了一份日志还没用啊,在日志里面搜肯定搜不到中文的字符串,只能搜到 16 进制的一些东西,改一下

Terminal window
>-----------------------------------------------------------------------------<
[11:49:51 464]解密结果, md5=8f22bf8315b3a293b593bc54c3f72d79, hex=7b22636f6465223a343030302c226d657373616765223a22e794b1e4ba8ee682a8e79a84e8b4a6e58fb7e6b689e5ab8ce8bf9de8a784e6938de4bd9cefbc8ce5bd93e5898de8aebee5a487e69a82e4b88de694afe68c81e4bdbfe794a8e38082e5a682e69c89e79691e997aeefbc8ce8afb7e68ba8e68993203430302d3036352d35373939222c227a7044617461223a7b7d7d
size: 147
0000: 7B 22 63 6F 64 65 22 3A 34 30 30 30 2C 22 6D 65 {"code":4000,"me
0010: 73 73 61 67 65 22 3A 22 E7 94 B1 E4 BA 8E E6 82 ssage":"........
0020: A8 E7 9A 84 E8 B4 A6 E5 8F B7 E6 B6 89 E5 AB 8C ................
0030: E8 BF 9D E8 A7 84 E6 93 8D E4 BD 9C EF BC 8C E5 ................
0040: BD 93 E5 89 8D E8 AE BE E5 A4 87 E6 9A 82 E4 B8 ................
0050: 8D E6 94 AF E6 8C 81 E4 BD BF E7 94 A8 E3 80 82 ................
0060: E5 A6 82 E6 9C 89 E7 96 91 E9 97 AE EF BC 8C E8 ................
0070: AF B7 E6 8B A8 E6 89 93 20 34 30 30 2D 30 36 35 ........ 400-065
0080: 2D 35 37 39 39 22 2C 22 7A 70 44 61 74 61 22 3A -5799","zpData":
0090: 7B 7D 7D {}}
^-----------------------------------------------------------------------------^
解密结果: {"code":4000,"message":"由于您的账号涉嫌违规操作,当前设备暂不支持使用。如有疑问,请拨打 400-065-5799","zpData":{}}

但是上面的结果包含了 sp 和 sig 的逻辑,这个很不方便我查找,需要把 get_sig 和 get_sp 的代码逻辑个给注释掉,重新 trace 一份日志,直接搜索字符串,很快就能够定位到相关位置:

跳转到 0x02e9bc

就到这儿了,回忆一下,这个就是之前的 rc4 算法的加密分析过的啊

可以解密一下: 应该算是完成了吧

某直聘app逆向分析
https://819229.xyz/posts/某直聘app逆向分析/
作者
0xnonce
发布于
2026-02-27
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取

评论

正在加载评论...
0/2000

    最后更新于 ,距今已过 184

    部分内容可能已过时