环境
淘最热点app 2.6.6
pixel2
正文
来到登录点
针对登录点进行抓包
系统代理抓不到包,于是尝试vpn代理,成功抓到了
下面来分析一下sign值是怎么生成的
使用网上大神的java层算法自吐脚本来hook一下,打印调用栈
java层算法自吐脚本:https://blog.51cto.com/u_13389043/6230053
使用jadx反编译apk
不过在分析MhRequestUtil类之前,先来看看wifi代理无法抓包的原因
System.getProperty是java中的一个方法,它用于获取系统属性。系统属性是一些在java虚拟机启动时被设置的属性,它们可以包含关于系统环境的信息。
其中,http.proxyHost这个属性表示HTTP代理服务器的主机名。如果这个属性被设置了,它通常意味着系统或应用程序配置了HTTP代理。http.proxyPort这个属性表示HTTP代理服务器的端口号。如果这个属性被设置了,它通常与http.proxyHost一起使用,以指示代理服务器的完整地址
搜索getProperty关键字
果然这里设置了http代理检测,如果检测到存在代理,那么返回true
通过快捷键X查看交叉引用
进入第一个查看,可以直到这段代码是向服务器发送一个包含设备信息和应用信息的POST请求,并根据服务器的响应来更新应用程序的状态,但是如果CommUtils.a函数(即:代理检测方法)返回true就直接return了
弄清楚代理检测之后,回归到sign追溯分析
搜索 com.maihan.tredian.net.MhRequestUtil类,可以看到sign这个函数
map.put("sign", TreUtil.sign(a(map, false, false)));
先下拉看看a方法
编写代码hook一下
先不重载参数
可以通过报错看到参数类型: .overload('java.util.Map', 'boolean', 'boolean')
最终hook代码
function hook_a(){
Java.perform(function () {
var MhRequestUtil = Java.use("com.maihan.tredian.net.MhRequestUtil");
MhRequestUtil.a.implementation = function (a1, a2, a3) {
var ret = this.a(a1, a2, a3);
console.log("a=======================>:" + ret);
return ret;
}
})
}
function main() {
hook_a();
}
setImmediate(main);
hook结果
sign函数应该是对这个字符串进行处理
android_id=c7d6e3b83a3ea950&app_ver=87&channel=aliapp&code=1234&device_id=3f4cd28edd6f18f9b5e7abf46c806f35&device_name=google Pixel 2&device_udid=aa8665f814b51ac0231cc7960a586c0d&from=app&getui_push_id=722ec4b874f67b5d1f96de6e757da7d4&imei1=&imei2=&mac=9A:E2:8A:ED:F5:A3&nonce=67wqsl1732873801016&os_ver_code=29&phone=11111111111&system=1×tamp=1732873801
追溯这个sign函数发现它是一个jni函数
接着找到libtre.so文件
pull下来,使用ida反编译
然后搜索sign函数
修改第一个参数为JNIEnv*
修改了之后就可以看到一些函数出现了
通过简单分析,可以发现其核心功能是对Java层传入的字符串进行签名处理,具体步骤如下:
-
使用GetStringUTFChars从JNI环境获取Java字符串的C风格字符串表示。
-
将获取的字符串与一个固定的字符串(可能是密钥)拼接。
-
用j_base64_encode_new函数对拼接后的字符串进行处理。
-
使用j_SHA1Input和j_SHA1Result对前面的结果进行处理
-
将前面的结果转换为十六进制字符串。
-
最终将十六进制字符串转换为Java字符串并返回。
由分析可知,sign函数中使用了j_base64_encode_new,j_SHA1Input和j_SHA1Result对字符串进行了处理。通过他们的名字判断可能是base64和sha
接下来我们通过hook来验证我们的判断
网上大神的hook代码
function print_arg(addr){
try {
var module = Process.findRangeByAddress(addr);
if(module!= null){
return hexdump(addr) + "\n";
}
return ptr(addr) + "\n";
} catch (e) {
console.error("Error in print_arg while processing address: ", addr, e);
return "Error processing address" + "\n";
}
}
function hook_native_addr(funcPtr, paramsNum){
var module = Process.findModuleByAddress(funcPtr);
Interceptor.attach(funcPtr, {
onEnter: function(args) {
this.logs = []
this.params = []
this.logs.push("call" + module.name + "!!!!!" + ptr(funcPtr).sub(module.base) + "\n")
for(var i=0; i<paramsNum; i++){
this.params.push(args[i])
this.logs.push("这里是参数----->>>this.arg" + i + "进入:" + print_arg(args[i]))
}
}, onLeave: function(retval) {
for(let i=0; i<paramsNum; i++){
this.logs.push("这里是参数----->>>this.arg" + i + "返回:" + print_arg(this.params[i]))
}
this.logs.push("这里是返回值------>>>retval 离开:" + print_arg(retval) + "\n")
console.log(this.logs)
}
})
}
function hook_sha1result() {
console.log("Searching for module: libtre.so");
var soAddr = Module.findBaseAddress("libtre.so");
if (soAddr === null) {
console.error("Module not found: libtre.so");
} else {
console.log("Module found at address: " + soAddr);
// 继续执行 hook 代码
var SHA1Result = soAddr.add(0x14C8 + 1);
hook_native_addr(SHA1Result, 2);
}
}
function hook_base64(){
console.log("Searching for module: libtre.so");
var soAddr = Module.findBaseAddress("libtre.so");
if (soAddr === null) {
console.error("Module not found: libtre.so");
} else {
console.log("Module found at address: " + soAddr);
// 继续执行 hook 代码
var Base64 = soAddr.add(0x13B4 + 1);
hook_native_addr(Base64, 3);
}
}
setImmediate(hook_sha1result());
hook一下发现确实是这个sign函数产生了我们请求包的sign值,说明我们追溯的函数没错
hook一下base64函数
可以看出是把请求包的基本参数传入作为明文
由于hexdump截断了,我们另写脚本打印arg0和retvar
function main(){
console.log("Searching for module: libtre.so");
var soAddr = Module.findBaseAddress("libtre.so");
if (soAddr === null) {
console.error("Module not found: libtre.so");
} else {
console.log("Module found at address: " + soAddr);
// 继续执行 hook 代码
var Base64 = soAddr.add(0x13B4 + 1);
}
var module = Process.findModuleByAddress(Base64);
Interceptor.attach(Base64, {
onEnter: function(args) {
console.log("明文为----------------->>>",args[0].readCString());
}, onLeave: function(retval) {
console.log("编码后----------------->>>",retval.readCString());
}
})
}
setImmediate(main);
hook结果如下
使用在线解码网站可以确认就是base64编码
同时我们也可以在base64_encode_new中找到字母表
接着来确认一下sha1
通过对比也可以确认是sha1
到此,对请求包中sign值生成的分析就结束了