使用的工具
- 抓HTTP包:Reqable
- 反编译DEX:Jadx,JEB
- 反编译APK:dex2jar,dex-tools-v2.4
- 反编译SO:ida
一、抓包
Reqable!
抓APP包的时候,有的APP调用HTTP是不走系统代理的,所以 Fiddler、Charles 抓不到
可以选择Charles + Drony 工具来抓包
或者直接使用Reqable来抓包
POST /ydb/app/server/cabinet/queryCabinetDetail.json HTTP/1.1
Host: appserver.zhizukj.com
moduleType: 1
User-Agent: ios/17.4.1/4.3.0
appId: 1
adc:
Cookie: d_id=345918fd417f461a5abb030a6bd99eb83e5aeccb20e60bf44473a8; token=f056f929-e1d1-48bb-bae0-b24e8fdeb5f3; m_no=b6320070e1ef79373cebf5517957e6af; nonce=4b23e0dc0e9545cyje5eb83e5ae3884aac6a50b4b716c45e5e6ce26021921;
vid: e4a334bbc00974b5a2d84b9afbc140dd
fid: e4a334bbc00974b5a2d84b9afbc140dd
platformId: 2
brand: iphone
appVersion: 4.3.0
nonce: 4b23e0dc0e9545cyje5eb83e5ae3884aac6a50b4b716c45e5e6ce26021921
cid: 131
deviceId: 345918fd417f461a5abb030a6bd99eb83e5aeccb20e60bf44473a8
platform: ios
token: f056f929-e1d1-48bb-bae0-b24e8fdeb5f3
sign: 88427f425df6903879a24ca4a85f4d92
timestamp: 1735324410
Accept-Language: zh-Hans-CN;q=1
d_id: 345918fd417f461a5abb030a6bd99eb83e5aeccb20e60bf44473a8
model: iPhone 12 mini
timedifference: 0
versionCode: 4003000
Content-Length: 12
Connection: keep-alive
Accept: */*
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate, br
m_no: b6320070e1ef79373cebf5517957e6af
siteId=24837
- nonce: 4b23e0dc0e9545cyje5eb83e5ae3884aac6a50b4b716c45e5e6ce26021921(61位)
- deviceId: 345918fd417f461a5abb030a6bd99eb83e5aeccb20e60bf44473a8(54位)
- token: f056f929-e1d1-48bb-bae0-b24e8fdeb5f3
- sign: 88427f425df6903879a24ca4a85f4d92(32位)(推测MD5)
二、发现加固了,然后APK脱壳
加壳:将 classes.dex 文件加密 + 混淆
脱壳:将 classes.dex 文件解密 + 反混淆,我一般只会脱简单的壳,apk运行肯定是要解密出来原始的DEX文件的,所以可以使用动态注入的方式,在APP启动后,拿到内存中的DEx文件
Xposed:可以通过框架插件修改和拦截应用的行为,适用于动态脱壳。
Frida:强大的动态分析工具,能够在应用运行时对代码进行修改或提取信息。
我拿到加了壳的APP,我会用nop.gs网址尝试一下脱壳,本次APP直接脱下来了

然后使用dex2jar工具转成jar文件,再用jd-gui工具打开jar文件,就可以看到源码了。
或者使用MT管理器,完成DEX合并也一样的

三、反编译DEX
工具:JEB,JADX,MT管理器
脱完壳会得到几个DEX文件,或者一个jar文件
使用JEB工具打开dex文件,可以看到JAVA源码了
四、找到Sign加密和body加密:
这个反编译得慢慢分析,一点点找,得有JAVA基础和汇编基础
如果你实打实做个服务端 做过APP,那就能把流程串起来
五、反混淆代码得到:

继续一层一层追就行,找到加密的函数
六、追查到加密是libzzcore.so文件

七、ida反编译so



八、解密salt



到此,整个slat加密流程就出来了,可以自己写代码实现了
九、注入HOOK
这个环节需要写合适的HOOK函数,注入到合适的函数中,联合调试,最后拿到值
部分HOOK代码如下:
HOOK全部SO文件列表:
function listModules() {
Process.enumerateModules({
onMatch: function (module) {
console.log(module.name + " @ " + module.base);
},
onComplete: function () {
console.log("Module enumeration complete.");
}
});
}
Java.perform(function () {
listModules();
});
HOOK so文件偏移量为0x00BB50的函数
function hookSubBB50() {
var libzzcoreBase = Module.findBaseAddress("libzzcore.so");
if (libzzcoreBase) {
console.log("libzzcore.so base address: " + libzzcoreBase.toString(16));
var subBB50Offset = 0xBB50; // sub_BB50 的偏移量
var subBB50Address = libzzcoreBase.add(subBB50Offset);
console.log("sub_BB50 address: " + subBB50Address.toString(16));
Interceptor.attach(subBB50Address, {
onEnter: function (args) {
console.log("sub_BB50 called!",args);
},
onLeave: function (retval) {
console.log("sub_BB50 returned: " + retval);
},
});
} else {
console.log("libzzcore.so not found!");
}
}
Java.perform(function () {
hookSubBB50();
});

HOOK Base64解密函数
function hookBase64Decode() {
Java.perform(function () {
// 获取 Base64 类
var Base64 = Java.use("android.util.Base64");
// Hook decode 方法
Base64.decode.overload('java.lang.String', 'int').implementation = function (input, flags) {
// 调用原始方法
var result = this.decode(input, flags);
// 打印解码后的值
console.log("Base64.decode(Api.getKey(), 2) returned (Raw):", result);
console.log("Base64.decode(Api.getKey(), 2) returned (Hex):", bytesToHex(result));
return result;
};
console.log("Base64.decode hooked!");
});
}
// 字节数组转十六进制
function bytesToHex(bytes) {
return Array.from(bytes, function (byte) {
return ('0' + (byte & 0xff).toString(16)).slice(-2);
}).join('');
}
// 主函数
function main() {
// 延迟执行,确保类已加载
setTimeout(function () {
hookBase64Decode();
}, 5000); // 延迟 5 秒
}
// 执行主函数
main();


