一、目标

一、目标

书接上文 结合AI对WhatsApp的分析(二) ,我们找到了 jnidispatchOO ,下一步就是搞定它的具体算法。

二、步骤

先HOOK

let JniBridge = Java.use("com.whatsapp.wamsys.JniBridge");
JniBridge["jnidispatchOO"].implementation = function (i, obj) {

        let bCls = Java.use('[B')

        if(i == 3){
                let buffer = Java.cast(obj, bCls);
                let rc1 = Java.array('byte', buffer);

                console.log(` ---- JniBridge.jnidispatchOO is called: i=${i}, obj=${buf2hex(rc1)}`);
        }

        let result = this["jnidispatchOO"](i, obj);

        if(i == 3){
                let buffer2 = Java.cast(result, bCls);
                let rc2 = Java.array('byte', buffer2);

                console.log(` ---- JniBridge.jnidispatchOO result = ${buf2hex(rc2)}`);
        }
        return result;
};

跑一下

 ---- JniBridge.jnidispatchOO result = 3045022031c495bb8bf2bbe39124b0c539cc0929e3d6cf1960874ecd019a0e7412160506022100a056754c60d842fd2e12aa06fd00aae06999d8808e213875424e4489ce26ff28

// 对应的 base64 = MEUCIDHElbuL8rvjkSSwxTnMCSnj1s8ZYIdOzQGaDnQSFgUGAiEAoFZ1TGDYQv0uEqoG/QCq4GmZ2ICOITh1Qk5Eic4m/yg=

确认了眼神,这次没错了

上Jadx

public static Object jnidispatchOO(int i, Object obj) {
    ...
     case 3:
        byte[] bArr2 = (byte[]) obj;
        C51032Wh c51032Wh = (C51032Wh) INSTANCE.jniCallbacksIJniCallbacks.A04.get();
        if (!AbstractC17350u8.A01() || bArr2 == null) {
            return null;
        }
        // 这个 A01.A07 应该就是计算H的位置
        return c51032Wh.A01.A07(bArr2, c51032Wh.A00.A0J());
    ...
}

// X.17J
 public byte[] A07(byte[] bArr, byte[] bArr2) {
        byte[] bArr3 = null;
        if (!A06()) {
            return null;
        }
        A02(C00Q.A01, bArr2);
        long elapsedRealtime = SystemClock.elapsedRealtime();
        try {
            try {
                                // 从android的KeyStore拿到密钥
                KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
                keyStore.load(null);
                KeyStore.Entry entry = keyStore.getEntry(A01(), null);
                if (entry instanceof KeyStore.PrivateKeyEntry) {
                    Signature signature = Signature.getInstance(AbstractC15060oI.A01(C15080oK.A02, this.A02, 2075));
                    signature.initSign(((KeyStore.PrivateKeyEntry) entry).getPrivateKey());
                    signature.update(bArr);
                                        // H 就是这里来的
                    bArr3 = signature.sign();
                } else {
                    Log.e("blacknoise/Not an instance of a PKEntry");
                    this.A04.A0H("attestation-nosign", "cannot sign payload, att pair missing", true);
                }
            } catch (Exception e) {
                Log.e("blacknoise/exception on pair sign", e);
                AbstractC213314r abstractC213314r = this.A04;
                StringBuilder sb = new StringBuilder();
                sb.append("unable to sign payload: ");
                sb.append(e.getMessage());
                abstractC213314r.A0F("attestation-nosign", sb.toString(), e);
            }
            return bArr3;
        } finally {
            C17K c17k = new C17K();
            c17k.A00 = Long.valueOf(SystemClock.elapsedRealtime() - elapsedRealtime);
            c17k.A02 = "att-sign";
            this.A06.CG2(c17k);
        }
    }

我们再 Hook一下 java.security.Signature.getInstance

var Signature = Java.use("java.security.Signature");

Signature.getInstance.overload("java.lang.String").implementation = function(algorithm) {
        console.log(`/* TID ${gettid()} */` + "[*] Hooked Signature.getInstance");
        console.log("    Algorithm: " + algorithm);
    return this.getInstance(algorithm);
};

// 打印
/* TID 10091 */[*] Hooked Signature.getInstance
    Algorithm: SHA256withECDSA

这次形势明朗了 SHA256withECDSA

App生成一对ECDSA的公钥和私钥,本地用私钥给数据做sign,把公钥和sign结果传给服务器,服务器用公钥去做verify。

把这些数据扔给AI,AI轻松就把Demo代码写好了。

新的问题,证书不对。

公钥上传的位置只有一种可能,放在请求头的 Authorization

新的问题是,AI给我生成的公钥是91个字节,明显小于 Authorization

继续问AI

ME: MIIFHDCCAwSgAwIBAgIJANUP8luj8tazMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV(完整的Authorization头贴上去) 为什么这个证书这么长,我只需要SHA256withECDSA的公钥证书,应该是91个字节才对

AI: 你贴的是一大串 Base64 编码后的证书链,这其实是**X.509 证书(或多个)**的完整内容,被编码成了一大段字符串,常见于 .pem 或 .crt 文件中。
确实看起来非常长,但这并不是“公钥”的长度,而是整个证书的编码内容。

解答你的两个关键疑问:
❓ 1. 为什么这个证书这么长?
因为它不仅仅包含公钥,而是包含了:

签名算法标识(如 SHA256withECDSA)
公钥(你想要的)
证书所有者信息(issuer, subject, 序列号等)
有效期(not before / not after)
签名值
扩展字段(可能还包含 CA 标识、密钥用法等)
这些内容都被 ASN.1/DER 格式编码,然后再被 base64 编码,所以变得很长。

❓ 2. 我只需要 SHA256withECDSA 的公钥部分,应该是 91 字节左右,怎么提取?
你说的“91字节”是 ECDSA P-256(secp256r1)曲线上的压缩公钥长度:

压缩格式:33 字节
未压缩格式:65 字节
公钥 DER 编码结构,整体会有 91 个字节左右(SEQUENCE -> BIT STRING -> ECPoint)

需要哪种?可以直接发一句:“帮我提取公钥(hex)”之类的,我马上给你。

必须要,帮我写个python代码把里面的 SHA256withECDSA 提取出来,然后写个 verify 的Demo。

三、总结

AI不能直接帮你写好ws的H算法,但是能按照你的思路辅助你把H算法写出来。

ffshow
1:ffshow

AI只是强健的四肢+翅膀,你才是大脑,决定飞向哪里

100

关注微信公众号,最新技术干货实时推送

100