app逆向实战:某瓣7.18.0版本_sig参数抓包与破解

本篇博客旨在记录学习过程,不可用于商用等其它途径

入口

小组里面全部讨论数据列表
在这里插入图片描述

抓包

根据抓包结果,_sig参数是动态生成的,需要破解
在这里插入图片描述

查克

使用PKID查壳工具发现app并没有加固
在这里插入图片描述

参数源码定位

使用jadx打开apk,全局搜索_sig参数,下图是搜索结果
在这里插入图片描述
这个参数使用的地方不多,仔细观察找到具有赋值作用的代码行并逐个进去观察,根据RequestFormBody_ts_sig确定这是具体加密位置
在这里插入图片描述

源码分析

接下来找到a2.first怎么来的,看下图确定a2是通过ApiSignatureHelper.a(request)得到的,双击a进入它的具体实现代码
在这里插入图片描述
大家看下图,实际调用是第一个a,不过它重载了另一个a方法,这个才是具体的加密位置
在这里插入图片描述
第一个a传递的三个参数分别是:
request.url().toString():请求url
request.method():请求方式
header:请求头的Authorization参数,如果有则取前七个字符
其实在抓包时就能知道这三个的内容了,所以这点不难
在这里插入图片描述

第二个a直接看到最后一行,最后返回了new Pair<>(HMACHash1.a(str4, sb.toString())String.valueOf(currentTimeMillis))两个值,其中第二个值是时间戳,对应上_ts,所以_sig是通过new Pair<>(HMACHash1.a(str4, sb.toString())得到的
把代码修整一下方便阅读,其中StringPool.AMPERSAND就是&,可以知道加密文本就是对url, methood, authorization三个参数与时间戳进行判断拼接处理,其中url截取的是路径,不包含后缀参数,按本案例来加密文本就是GET&%2Fapi%2Fv2%2Fgroup%2F721771%2Ftopics&1721737945

public static Pair<String, String> a(url, methood, authorization) {
    String decode;
    String str4 = FrodoApi.a().e.b;
    StringBuilder sb = new StringBuilder();
    sb.append(methood);
    String encodedPath = HttpUrl.parse(str).encodedPath();
    sb.append(StringPool.AMPERSAND);
    sb.append(Uri.encode(decode));
    if (!TextUtils.isEmpty(authorization)) {
        sb.append(StringPool.AMPERSAND);
        sb.append(authorization);
    }
    long currentTimeMillis = System.currentTimeMillis() / 1000;
    sb.append(StringPool.AMPERSAND);
    sb.append(currentTimeMillis);
    return new Pair<>(HMACHash1.a(str4, sb.toString()), String.valueOf(currentTimeMillis));
}

双击进去HMACHash1代码,发生其实就是使用的是原生加密,所以现在就差一个密钥就能得到加密算法了。
回去看第二个a,发现密钥是通过FrodoApi.a().e.b得到的,双击进去后发现并没有在此处定义,那就要找到创建它实例的位置,也就是new ZenoConfig()的位置,这里大家记下b参数是第三个传参赋值的
在这里插入图片描述
全局搜索ZenoConfig,观察代码发现只有一处符合,双击进去
在这里插入图片描述
看下图流程,最终锁定在String d2 = FrodoUtils.d();,双击进去
在这里插入图片描述
在这里就能看到赋值的了,这段代码大致意思,c默认值是bHUvfbiVZUmm2sQRKwiAcw==,但是最终值是AESbHUvfbiVZUmm2sQRKwiAcw==加密之后的结果,密钥是Base64.encodeToString(AppContext.a().getPackageManager().getPackageInfo(AppContext.a().getPackageName(), 64).signatures[0].toByteArray(), 0);,分析一下这段代码,getPackageInfo是获取应用信息,getPackageName是包名,PackageInfo.signatures是App的签名。所以最后的密钥是把App签名通过toByteArray()转换为字节数组流,然后Base64一下。
在这里插入图片描述
一般情况下app签名是不变的,所以这里得到HMACHash1密钥也是不会变的,直接使用frida hook拿到密钥

Java.perform(
    function(){
        var ba = Java.use("com.douban.frodo.utils.crypto.HMACHash1");
        ba.a.overload('java.lang.String', 'java.lang.String').implementation = function (a1,a2) {
            console.log(a1);
            console.log(a2);
            var res = ba.a(a1, a2);
            console.log("计算 result:" + res);
            return res;
        }
    }
)

运行结果:
在这里插入图片描述
拿到密钥,然后构建加密文本就能拿到结果了

def get_sig(url, ts):
    urlpath = urlparse(url).path
    sign = '&'.join(['GET', quote(urlpath, safe=''), ts])
    print(sign)
    sig = hmac.new("bf7dddc7c9cfe6f7".encode(), sign.encode(), hashlib.sha1).digest()
    _sig = base64.b64encode(sig).decode()
    return _sig

在这里插入图片描述

posted @ 2024-07-23 21:01  七夜魔手  阅读(7)  评论(0编辑  收藏  举报  来源