⚠️前言⚠️
本文仅用于学术交流。
 学习探讨逆向知识,欢迎私信共享学习心得。
 如有侵权,联系博主删除。
 请勿商用,否则后果自负。
接口网址
app 版本: 8.10.0
aHR0cHM6Ly93d3cuemhpaHUuY29tL2FwaS92NC9zZWFyY2hfdjM=

加密位置分析
> 老规矩,jadx打开,先全局检索一下。

- 居然没有搜到任何结果,应该做了混淆
> 由于 X-Zse-96 参数存在于headers中,全局检索一下 addHeader 看有没有啥线索

- 发现在添加请求头信息的时候,字段名貌似通过 H.d 做了转换,这也可能是我们搜不到关键词的原因
> frida hook H.d,通过判断 result 减少日志打印,方便我们查看
Java.perform(function () {
    var h = Java.use('com.secneo.apkwrapper.H');
    h.d.implementation = function(str){
        var result = this.d(str);
        if(result == 'X-Zse-96'){
            send('arg: ' + str);
            send('result :' + result);
        }
        return result;
    };
})

- 果然是通过H.d方法转换的,那我们可以直接检索一下 G51CEEF09BA7DF27F 来看一下
> 全局检索 G51CEEF09BA7DF27F,这结果就很少了,来看一下这个 addHeader

 
- 就是这里了,那 H.d(“G38CD8525”) + new String(this.f61339c.encode(encrypt)) 有可能就是我们想要的最终结果
> H.d(“G38CD8525”) 的结果是什么?我们通过 frida hook 固定参数的形式来看一下
Java.perform(function () {
    var h = Java.use('com.secneo.apkwrapper.H');
    h.d.implementation = function(str){
        str = "G38CD8525";
        var result = this.d(str);
        send('arg: ' + str);
        send('result :' + result);
        return result;
    };
})

- 这不就是 X-Zse-96 的特征值嘛,那我们只需要搞定 new String(this.f61339c.encode(encrypt)) 就可以拿到加密值了
加密逻辑分析
> 首先来看一下这个 a3 是怎么生成,hook 一下这个 a 方法

 
- 通过 hook 的结果可以看出它参数由4部分组成
- 101_1_1.0+接口路径及参数+app版本号+Bearer 2.1rDb…
- 最后一部分经多次测试也是固定不变的,所以对于我们来说唯一需要传递的参数就是 接口的路径及参数
>>这个 a 是一个单独的方法不需要hook,我们直接扣代码即可,代码如下:

private static String a(String str) throws Exception{
        return String.format("%032X", new BigInteger(1, MessageDigest.getInstance("MD5").digest(str.getBytes())));
    }
- 到这里我们就得到了 a3 的值
> 下面来看一下 encrypt 是怎么生成的

- 发现生成 encrypt 值的方法存在于接口类b中,那么一定会在某个地方有一个类继承了这个b,并实现了 encrypt 方法
- 全局检索一下关键词 接口类 b 所在包名, 这就很明了了。 
- 最终就会找到这个方法
  
>> frida hook 一下这个a方法

- 三个参数:参数1 - a3.toLowerCase().getBytes()
- 参数 2,3 固定值,不要问为什么,一步步方法跟过来你就知道了。😀 😀 😀
>> 参数搞明白之后我们再回头看这个 a 方法

- 我们需要搞定三个方法就可以得到 encrypt
- 首先 b.b, 这个方法直接扣代码就可以了
  
-  
  - CryptoTool.laesEncryptByteArr,此方法存在于 native 层,可以 hook 得到
  
 
- CryptoTool.laesEncryptByteArr,此方法存在于 native 层,可以 hook 得到
-  
  - b.a,和 b.b 一样也是直接扣代码即可
  
 
- b.a,和 b.b 一样也是直接扣代码即可
- 至此,encrypt 值就搞定了。
> 下面进入最后一步,发现这个 encode 的方法也是一个接口类


- 同样的方法看一下这个 a 是在哪里复现的。。。这里 encrypt 传过来之后通过 j.a 生成 a2,并返回
  
- j.a 其实就是做了一个 Base64 加密
  
- 至此,X-Zse-96 加密逻辑就全部分析结束了。
Xposed hook
关键代码:  这里只保留关键性逻辑
@Action("x-zse-96")
public class ZhihuHandler implements RequestHandler {
    @AutoBind
    private String p1; // 以base64的形式传入
    
    @Override
    public void handleRequest(SekiroRequest sekiroRequest, SekiroResponse sekiroResponse) {
        try {
            log("进来了。。。 开始生成 x_zse_96。。。");
            String p1_decode = new String(Base64.decode(p1, 2));
            log(p1_decode);
            // System.out.println("******************helloword*****************************");
            StringBuilder sb = new StringBuilder();
            String temp_str = "101_1_1.0+" + p1_decode + "+8.10.0+Bearer 2.1...";
            sb.append(temp_str);
            String result = sb.toString().substring(0, sb.length() - 1);
            String a3 = a(result);
            byte[] barr_a3 = a3.toLowerCase().getBytes();
            // 生成变量 encrypt   b.b(CryptoTool.laesEncryptByteArr(b.a(bArr, str, bArr2), str, bArr2), str, bArr2);
            String arg2_str = "541a3a5896...";
            byte[] arg3_barr = new byte[]{ ... };
            byte[] result_ba = b_a(barr_a3, arg2_str, arg3_barr);
            // hook so层方法 aesEncryptByteArr
            final Class<?> clazz = XposedHelpers.findClass("com.bangcle.CryptoTool", HookZhihu.loadPackageParam.classLoader);
            // 注意标明返回值类型 (byte[])  静态方法可以直接使用类名调用
            byte[] result_CryptoTool = (byte[]) XposedHelpers.callStaticMethod(clazz,"laesEncryptByteArr",new Object[]{result_ba,arg2_str,arg3_barr});
            byte[] encrypt = b_b(result_CryptoTool, arg2_str, arg3_barr);
            String x_zse_96 = "1.0_" + new String(Base64.encodeToString(encrypt, 2).getBytes());
            sekiroResponse.success(x_zse_96);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    // throws Exception 表示的是本方法不处理异常,交给被调用处处理
    private static String a(String str) throws Exception{
        return String.format("%032X", new BigInteger(1, MessageDigest.getInstance("MD5").digest(str.getBytes())));
    }
    public static byte[] b_a(byte[] bArr, String str, byte[] bArr2) {
       	...
    }
    public static byte[] b_b(byte[] bArr, String str, byte[] bArr2) {
        ...
     }
}
最终成果展示




















