boss app sig及sp参数,魔改base64

前言

大家好呀,欢迎来到我的博客.2023年12月4日,boss web上线了最新的zp_token,环境检测点又增加了,与此同时app端的关键加密so从32位换成了64位,两者ida反编译so的时候都有反调试,无法直接f5,需要手动调整让ida重新识别.google了一下几乎找不到任何有关boss app的文章,所以这篇文章讲解app端的加密.篇幅较长,坐稳发车啦!

设备 pixel 4xl android10

版本: 11.240

下载地址: aHR0cHM6Ly93d3cud2FuZG91amlhLmNvbS9hcHBzLzYyMDIyMjIvaGlzdG9yeV92MTEyNDAxMA==

工具: charles(抓包) socksdroid(流量转发) jadx(反编译dex) ida(反编译so) frida(注入) frida-trace(还原算法)

目录

前言

声明

抓包

sig分析

sp分析

总结


声明

本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请联系作者立即删除!

抓包

boss app sig及sp参数,魔改base64_第1张图片

反复抓包后确定了是这个包,只不过响应是加密的,params里有sp和sig参数,sp参数是一个长串,sig由V3.0拼接一个32位字符串,猜测是md5

sig分析

可以尝试搜索字符串sig或者hook hashmap等等方法定位,我这里hook hashmap

Java.perform(function (){
    var hashMap = Java.use("java.util.HashMap");
    hashMap.put.implementation = function (a, b) {
        console.log("hashMap.put: ", a, b);
    return this.put(a, b);
}
})

boss app sig及sp参数,魔改base64_第2张图片

boss app sig及sp参数,魔改base64_第3张图片

 boss app sig及sp参数,魔改base64_第4张图片

boss app sig及sp参数,魔改base64_第5张图片 搜索net.bosszhipin.base.m 这个类可以看到sp和sig,八成就是这里了,这里先hook sig

boss app sig及sp参数,魔改base64_第6张图片

从h方法点进去看看

右键复制frida 片段

boss app sig及sp参数,魔改base64_第7张图片

抓个包后确认就是这里了

接着从signature方法点进去

boss app sig及sp参数,魔改base64_第8张图片

接下来方便还原算法我们需要固定好入参,写java层的主动调用,然后配合着ida静态分析来还原算法

function call(){
    Java.perform(function (){
let YZWG = Java.use("com.twl.signer.YZWG");
var str = '/api/batch/batchRunV2batch_method_feed=%5B%22method%3DzpCommon.adActivity.getV2%26dataType%3D0%26expectId%3D802924422%26dataSwitch%3D1%22%2C+%22method%3Dzpgeek.app.f1.newgeek.jobcard%26encryptExpectId%3Def7d7c83e4017a4233R40t-5FFBS%26expectId%3D802924422%22%2C+%22method%3Dzpgeek.app.geek.trait.tip%26encryptExpectId%3Def7d7c83e4017a4233R40t-5FFBS%26expectId%3D802924422%22%2C+%22method%3Dzpgeek.cvapp.applystatus.change.tip%22%2C+%22method%3Dzpinterview.geek.interview.f1.complainTip%22%2C+%22method%3Dzpgeek.cvapp.geek.remind.warnexp%26entrance%3D1%26itemType%3D1%22%2C+%22method%3Dzpgeek.app.f1.banner.query%26encryptExpectId%3Def7d7c83e4017a4233R40t-5FFBS%26expectId%3D802924422%26filterParams%3D%257B%2522cityCode%2522%253A%2522101010100%2522%252C%2522switchCity%2522%253A%25220%2522%257D%26gpsCityCode%3D0%26jobType%3D0%26mixExpectType%3D0%26sortType%3D1%22%2C+%22method%3Dzpinterview.geek.interview.f1%22%2C+%22method%3Dzpgeek.app.f1.recommend.filter%26commute%3D%26distance%3D0%26encryptExpectId%3Def7d7c83e4017a4233R40t-5FFBS%26expectPosition%3D%26filterFlag%3D0%26filterParams%3D%257B%2522cityCode%2522%253A%2522101010100%2522%252C%2522switchCity%2522%253A%25220%2522%257D%26filterValue%3D%26jobType%3D0%26mixExpectType%3D0%26partTimeDirection%3D%26positionCode%3D%26sortType%3D1%22%2C+%22method%3Dzpgeek.app.bluecollar.topic.banner%26encryptExpectId%3Def7d7c83e4017a4233R40t-5FFBS%22%2C+%22method%3Dzpgeek.cvapp.geek.homeexpectaddress.query%26cityCode%3D101010100%22%2C+%22method%3Dzpgeek.app.f1.interview.recjob.tip%26encryptExpectId%3Def7d7c83e4017a4233R40t-5FFBS%26expectId%3D802924422%22%2C+%22method%3Dzpgeek.app.geek.recommend.joblist%26encryptExpectId%3Def7d7c83e4017a4233R40t-5FFBS%26sortType%3D1%26expectPosition%3D100514%26pageSize%3D15%26expectId%3D802924422%26page%3D1%26filterParams%3D%257B%2522cityCode%2522%253A%2522101010100%2522%252C%2522switchCity%2522%253A%25220%2522%257D%22%2C+%22method%3Dzpgeek.app.studyabroad.article.headlines%22%2C+%22method%3Dzpgeek.cvapp.geek.resume.queryquality%22%5D&client_info=%7B%22version%22%3A%2210%22%2C%22os%22%3A%22Android%22%2C%22start_time%22%3A%221703159294618%22%2C%22resume_time%22%3A%221703159294618%22%2C%22channel%22%3A%2227%22%2C%22model%22%3A%22google%7C%7CPixel+4+XL%22%2C%22dzt%22%3A0%2C%22loc_per%22%3A0%2C%22uniqid%22%3A%227fe541f3-a666-4845-9186-cff9d5429f77%22%2C%22oaid%22%3A%22NA%22%2C%22did%22%3A%22DUzpQpzBYtoakGWwhYSfr2VDxKhBVPnGWdbfRFV6cFFwekJZdG9ha0dXd2hZU2ZyMlZEeEtoQlZQbkdXZGJmc2h1%22%2C%22is_bg_req%22%3A0%2C%22network%22%3A%22wifi%22%2C%22operator%22%3A%22UNKNOWN%22%2C%22abi%22%3A0%7D&curidentity=0&req_time=1703162554818&uniqid=7fe541f3-a666-4845-9186-cff9d5429f77&v=11.200'
       var str2 = null
var res = YZWG["signature"](str,str2)
        console.log(res)
    })
}

可以发现return 的值是来着nativeSignature方法,看名字就知道他应该是一个native方法         

boss app sig及sp参数,魔改base64_第9张图片点进去后发现确实是native方法,并且上面还有许多native方法,包含sp的加密方法nativeEncodeRequest(sp的寻找方式后续就不介绍了,和sig差不多),而且从字面上看,里面有解密数据的方法,正好对应响应数据的解密

boss app sig及sp参数,魔改base64_第10张图片 

往上找可以发现加载自yzwg这个so文件  

boss app sig及sp参数,魔改base64_第11张图片

解包后可以看到只有64的so 11.230版本以前都是32的so,找到libyzwg.so并拖到ida64里反编译

boss app sig及sp参数,魔改base64_第12张图片 在导出表里搜索jni发现是动态注册,这里可以直接上脚本找出这个so注册的函数

// 获取 RegisterNatives 函数的内存地址,并赋值给addrRegisterNatives。
var addrRegisterNatives = null;
// 列举 libart.so 中的所有导出函数(成员列表)
var symbols = Module.enumerateSymbolsSync("libart.so");
for (var i = 0; i < symbols.length; i++) {
    var symbol = symbols[i];
    // console.log(symbol.name)
    //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi
    if (symbol.name.indexOf("art") >= 0 &&
        symbol.name.indexOf("JNI") >= 0 &&
        symbol.name.indexOf("RegisterNatives") >= 0 &&
        symbol.name.indexOf("CheckJNI") < 0) {

        addrRegisterNatives = symbol.address;
        console.log("RegisterNatives is at ", symbol.address, symbol.name);
        break
    }
}
if (addrRegisterNatives) {
    // RegisterNatives(env, 类型, Java和C的对应关系,个数)
    Interceptor.attach(addrRegisterNatives, {
        onEnter: function (args) {
            var env = args[0];        // jni对象
            var java_class = args[1]; // 类
            var class_name = Java.vm.tryGetEnv().getClassName(java_class);
            var taget_class = "com.twl.signer.YZWG";   //111 某个类中动态注册的so
            if (class_name === taget_class) {
                //只找我们自己想要类中的动态注册关系
                console.log("\n[RegisterNatives] method_count:", args[3]);
                var methods_ptr = ptr(args[2]);
                var method_count = parseInt(args[3]);
                for (var i = 0; i < method_count; i++) {
                    // Java中函数名字的
                    var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3));
                    // 参数和返回值类型
                    var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize));
                    // C中的函数内存地址
                    var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2));
                    var name = Memory.readCString(name_ptr);
                    var sig = Memory.readCString(sig_ptr);
                    var find_module = Process.findModuleByAddress(fnPtr_ptr);
                    // 地址、偏移量、基地址
                    var offset = ptr(fnPtr_ptr).sub(find_module.base);
                    console.log("name:", name, "sig:", sig,'module_name:',find_module.name ,"offset:", offset);

                }

            }
        }
    });
}
命令 frida -U -f com.hpbr.bosszhipin -l 文件名.js

结果 nativeSignature也就是sig的加密,偏移0x21864 nativeEncodeRequest也就是sp的加密,偏移0x209a4,后续ida中按g就可以跳到制定函数处 boss app sig及sp参数,魔改base64_第13张图片

boss app sig及sp参数,魔改base64_第14张图片 boss app sig及sp参数,魔改base64_第15张图片可以看到text段是金色的,也就是说ida错误的把原本是代码的地方识别成了数据,这个时候需要手动帮ida一把,让他重新把数据识别成代码

boss app sig及sp参数,魔改base64_第16张图片选中金色段按c(code) 转为代码

boss app sig及sp参数,魔改base64_第17张图片转化后出现红色段就按p(create function)

boss app sig及sp参数,魔改base64_第18张图片重复这个过程直到把关键函数转为代码后就可以f5了

boss app sig及sp参数,魔改base64_第19张图片

跳到0x21864位置转为伪c代码

boss app sig及sp参数,魔改base64_第20张图片

点进去300多行代码,不太好分析,可以借助frida trace来跟踪native函数执行的时候调用了哪些函数

使用方法https://github.com/Pr0214/trace_natives

boss app sig及sp参数,魔改base64_第21张图片boss app sig及sp参数,魔改base64_第22张图片然后主动调用上面的java方法

boss app sig及sp参数,魔改base64_第23张图片

把执行的函数复制到notepad里分析一下,调用了挺多函数,这里就没什么特殊的技巧了,只能凭借着经验猜测哪个是关键函数

在hook 1c714函数的时候我发现了结果,hook代码

var soAddr = Module.findBaseAddress("libyzwg.so");
var funcAddr = soAddr.add(0x1c714)  //32位+1

Interceptor.attach(funcAddr,{
            onEnter: function(args){
                console.log('onEnter arg[0]: ',hexdump(args[0],{length:args[1].toInt32()}))
                console.log('onEnter arg[1]: ',args[1])

                this.arg0 = args[0]
            },
            onLeave: function(retval){
                // console.log('onLeave arg[0]: ',hexdump(this.arg0.readPointer()))
                console.log('onLeave result: ',hexdump(retval))

            }
        });

boss app sig及sp参数,魔改base64_第24张图片boss app sig及sp参数,魔改base64_第25张图片

arg0是java传进来的明文,onleave的时候结果出来了,并且明文传进去的时候还加了一个salt

复制到CyberChef里加密一下

boss app sig及sp参数,魔改base64_第26张图片是标准的md5,笔者在分析这个sig的时候当时并没有直接尝试加密,而是hook了他下面的函数2a5b8

boss app sig及sp参数,魔改base64_第27张图片hook 2a5b8代码

// 2A5B8
var soAddr = Module.findBaseAddress("libyzwg.so");
var funcAddr = soAddr.add(0x2A5B8)  //32位+1

var num = 0
Interceptor.attach(funcAddr,{
            onEnter: function(args){
                num+=1
                console.log(`onEnter arg[0]  ${num}次:  ${args[2]} `,hexdump(args[0]))
                console.log('onEnter arg[1]: ',hexdump(args[1],{length:512}))
                // console.log('onEnter arg[2]: ',args[2])

                this.arg0 = args[0]
            },
            onLeave: function(retval){
                console.log('onLeave arg[0]: ',hexdump(this.arg0))

            }
        });

可以看到在执行第8次2a5b8函数后结果也是出现了  

boss app sig及sp参数,魔改base64_第28张图片

 再来看第一次调用,可以看到arg0像是md5的4个初始化魔数,只不过内存中是小端字节续,由于md5的分组处理长度是512bit,所以需要多次压入数据,正好对应调用多次2a5b8函数,这个函数类似c md5中的updata和final过程

boss app sig及sp参数,魔改base64_第29张图片boss app sig及sp参数,魔改base64_第30张图片这里可以修改c++中md5的最后填充数据和附加消息长度来验证是否是标准md5

boss app sig及sp参数,魔改base64_第31张图片可以发现是标准的md5,到此sig参数就分析完毕了.我为什么要提这个2a5b8函数的执行过程,有人会说,我直接把明文拼接salt后md5发现是结果了不就行了吗,是的,但是如果有一天你把明文和salt拼接后md5发现不是你要的结果你该如何处理?你不懂算法细节如何在不准确的伪c代码中分析还原算法?并且还有可能遇到魔改算法你又该如何处理?

sp分析

md5算法是hash算法,不可逆,作用是用来验签的,防止数据包被篡改,那就肯定有一个传递加密前明文的参数,从上面的抓包来看只有可能是sp参数,这就说的通了,明文加密成sp,并且和明文的MD5一起传给后台,后台接受数据包后解密sp得到明文,并再次加密明文和传来的sig对比以防止数据包被篡改

分析sig的时候已经提了sp的分析,和sig差不多,同理可以主动调用

function call(){
    Java.perform(function (){
        let a = Java.use("com.twl.signer.a");
        var str = 'batch_method_feed=%5B%22method%3DzpCommon.adActivity.getV2%26dataType%3D0%26expectId%3D802924422%26dataSwitch%3D1%22%2C+%22method%3Dzpgeek.app.f1.newgeek.jobcard%26encryptExpectId%3Def7d7c83e4017a4233R40t-5FFBS%26expectId%3D802924422%22%2C+%22method%3Dzpgeek.app.geek.trait.tip%26encryptExpectId%3Def7d7c83e4017a4233R40t-5FFBS%26expectId%3D802924422%22%2C+%22method%3Dzpgeek.cvapp.applystatus.change.tip%22%2C+%22method%3Dzpinterview.geek.interview.f1.complainTip%22%2C+%22method%3Dzpgeek.cvapp.geek.remind.warnexp%26entrance%3D1%26itemType%3D1%22%2C+%22method%3Dzpgeek.app.f1.banner.query%26encryptExpectId%3Def7d7c83e4017a4233R40t-5FFBS%26expectId%3D802924422%26filterParams%3D%257B%2522cityCode%2522%253A%2522101010100%2522%252C%2522switchCity%2522%253A%25220%2522%257D%26gpsCityCode%3D0%26jobType%3D0%26mixExpectType%3D0%26sortType%3D1%22%2C+%22method%3Dzpinterview.geek.interview.f1%22%2C+%22method%3Dzpgeek.app.f1.recommend.filter%26commute%3D%26distance%3D0%26encryptExpectId%3Def7d7c83e4017a4233R40t-5FFBS%26expectPosition%3D%26filterFlag%3D0%26filterParams%3D%257B%2522cityCode%2522%253A%2522101010100%2522%252C%2522switchCity%2522%253A%25220%2522%257D%26filterValue%3D%26jobType%3D0%26mixExpectType%3D0%26partTimeDirection%3D%26positionCode%3D%26sortType%3D1%22%2C+%22method%3Dzpgeek.app.bluecollar.topic.banner%26encryptExpectId%3Def7d7c83e4017a4233R40t-5FFBS%22%2C+%22method%3Dzpgeek.cvapp.geek.homeexpectaddress.query%26cityCode%3D101010100%22%2C+%22method%3Dzpgeek.app.f1.interview.recjob.tip%26encryptExpectId%3Def7d7c83e4017a4233R40t-5FFBS%26expectId%3D802924422%22%2C+%22method%3Dzpgeek.app.geek.recommend.joblist%26encryptExpectId%3Def7d7c83e4017a4233R40t-5FFBS%26sortType%3D1%26expectPosition%3D100514%26pageSize%3D15%26expectId%3D802924422%26page%3D1%26filterParams%3D%257B%2522cityCode%2522%253A%2522101010100%2522%252C%2522switchCity%2522%253A%25220%2522%257D%22%2C+%22method%3Dzpgeek.app.studyabroad.article.headlines%22%2C+%22method%3Dzpgeek.cvapp.geek.resume.queryquality%22%5D&client_info=%7B%22version%22%3A%2210%22%2C%22os%22%3A%22Android%22%2C%22start_time%22%3A%221703222473770%22%2C%22resume_time%22%3A%221703222473770%22%2C%22channel%22%3A%2228%22%2C%22model%22%3A%22google%7C%7CPixel+4+XL%22%2C%22dzt%22%3A0%2C%22loc_per%22%3A0%2C%22uniqid%22%3A%22b99e5c38-858b-4097-8b4d-084a6e75ec62%22%2C%22oaid%22%3A%22NA%22%2C%22did%22%3A%22DUzpQpzBYtoakGWwhYSfr2VDxKhBVPnGWdbfRFV6cFFwekJZdG9ha0dXd2hZU2ZyMlZEeEtoQlZQbkdXZGJmc2h1%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&req_time=1703222656138&uniqid=b99e5c38-858b-4097-8b4d-084a6e75ec62&v=11.240'
        var str2 = null
        var res = a["d"](str, str2)
        console.log(res)
    })
}

同sig的frida-trace方法一样,trace后有几千行,这时需要分析哪些函数是关键函数并hook

boss app sig及sp参数,魔改base64_第32张图片最终的sp结果是魔改的base64,码表从A-Za-z0-9+/=替换成了A-Za-z0-9-_~ 并且这个结果是可以被des解密的,并且解密出来的raw也是不可见的,只能转为hex看看,这里埋个坑,尚不清楚传进去的明文和des解密后的密文有什么联系,先写到这里,后续再看看

boss app sig及sp参数,魔改base64_第33张图片

总结

1出于安全考虑,本章未提供完整流程,调试环节省略较多,只提供大致思路,具体细节要你自己还原,相信你也能调试出来.

2本人写作水平有限,如有讲解不到位或者讲解错误的地方,还请各位大佬在评论区多多指教,共同进步,也可加本人微信lyaoyao__i(两个_)

你可能感兴趣的:(安卓逆向,算法,前端)