某某语音auth值生成分析
字数 953 2025-08-22 12:22:54

某某语音Auth值生成分析教学文档

1. 分析背景

在某某语音应用的网络请求中,发现一个关键的校验参数auth,本文档将详细分析该参数的生成过程。

2. 初步定位

2.1 反编译定位

  1. 使用jadx反编译APK
  2. 搜索关键字"auth"定位到相关代码位置
  3. 调用链如下:
    com.dyheart.lib.dylog.network.RequestInterceptor.a(Request request, String str)
    → com.dyheart.lib.dylog.network.RequestInterceptor.a(Context context, String str, Map<String, String> map, Map<String, String> map2, String str2)
    → com.douyu.lib.http.MakeUrlClient.b(Context context, String str, String[] strArr, String[] strArr2, String[] strArr3, String[] strArr4, int i, int i2)
    → com.douyu.lib.http.MakeUrlClient.b(Context context, String str, String[] strArr, String[] strArr2, String[] strArr3, String[] strArr4, int i, int i2)
    → com.douyu.lib.http.JniMakeUrl.native_newmakeUrl
    

2.2 JNI函数定位

  1. native_newmakeUrl是一个JNI函数
  2. makeurl3.3.0.so库中未找到导出函数名,说明是动态注册
  3. 需要Hook RegisterNatives函数来定位

3. Hook RegisterNatives

3.1 Hook脚本

function hook_RegisterNatives() {
    var symbols = Module.enumerateSymbolsSync("libart.so");
    var addrRegisterNatives = null;
    for (var i = 0; i < symbols.length; i++) {
        var symbol = symbols[i];
        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);
        }
    }
    
    if (addrRegisterNatives != null) {
        Interceptor.attach(addrRegisterNatives, {
            onEnter: function(args) {
                console.log("[RegisterNatives] method_count:", args[3]);
                var env = args[0];
                var java_class = args[1];
                var class_name = Java.vm.tryGetEnv().getClassName(java_class);
                var methods_ptr = ptr(args[2]);
                var method_count = parseInt(args[3]);
                
                for (var i = 0; i < method_count; i++) {
                    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));
                    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);
                    
                    console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, 
                              "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, 
                              "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base));
                }
            }
        });
    }
}
setImmediate(hook_RegisterNatives);

3.2 反调试绕过

  1. 发现存在Frida反调试(特征检测和进程附加)
  2. 使用Florida(去除部分特征的server)绕过
  3. 成功打印出native_makeUrl的地址:0x6168

4. 分析native_newmakeUrl

4.1 Hook native_newmakeUrl

Java.perform(() => {
    var JniMakeUrlClass = Java.use("com.douyu.lib.http.JniMakeUrl");
    if (JniMakeUrlClass) {
        JniMakeUrlClass.native_newmakeUrl.implementation = function(context, str, strArr, strArr2, strArr3, strArr4, i, i2) {
            console.log("Context 参数: ", context);
            console.log("字符串参数: ", str);
            
            console.log("字符串数组参数(长度: " + (strArr && strArr.length || 0) + "):");
            if (strArr && Array.isArray(strArr)) {
                for (var j = 0; j < strArr.length; j++) {
                    console.log(" 元素 " + j + ": " + strArr[j]);
                }
            }
            
            console.log("字符串数组参数2(长度: " + (strArr2 && strArr2.length || 0) + "):");
            if (strArr2 && Array.isArray(strArr2)) {
                for (var k = 0; k < strArr2.length; k++) {
                    console.log(" 元素 " + k + ": " + strArr2[k]);
                }
            }
            
            console.log("字符串数组参数3(长度: " + (strArr3 && strArr3.length || 0) + "):");
            if (strArr3 && Array.isArray(strArr3)) {
                for (var l = 0; l < strArr3.length; l++) {
                    console.log(" 元素 " + l + ": " + strArr3[k]);
                }
            }
            
            console.log("字符串数组参数4(长度: " + (strArr4 && strArr4.length || 0) + "):");
            if (strArr4 && Array.isArray(strArr4)) {
                for (var m = 0; m < strArr4.length; m++) {
                    console.log(" 元素 " + m + ": " + strArr4[m]);
                }
            }
            
            console.log("整数参数1: ", i);
            console.log("整数参数2: ", i2);
            
            var retval = this.native_newmakeUrl(context, str, strArr, strArr2, strArr3, strArr4, i, i2);
            console.log("函数返回值: ", retval);
            return retval;
        };
    } else {
        console.log("未能找到 com.douyu.lib.http.JniMakeUrl 类");
    }
});

4.2 参数分析

  1. 多数参数是固定值
  2. 关键变化参数:字符串数组2的元素3,是一个时间戳(System.currentTimeMillis()/1000

5. Auth生成过程分析

5.1 关键函数sub_10898

  1. v33sub_10898赋值
  2. Hook sub_10898
function hook_sub_10898(){
    var addr = Module.findBaseAddress("libmakeurl3.3.0.so");
    console.log("libmakeurl3.3.0.so base address: " + addr);
    var funcAddr = addr.add(0x10898);
    console.log("makeurl3.3.0.so makeurl address: " + funcAddr);
    
    Interceptor.attach(funcAddr, {
        onEnter: function(args) {
            this.args8 = args[8];
            console.log("args0: " + args[0]);
            console.log("args1: " + args[1].toInt32());
            console.log("args2: " + args[2]);
            console.log("args3: " + args[3]);
            console.log("args4: " + Memory.readUtf8String(args[4]));
            console.log("args5: " + (args[5].readPointer()));
            console.log("args6: " + args[6].toInt32());
            console.log("args7: " + args[7].toInt32());
            console.log("args8: " + hexdump(args[8]));
        },
        onLeave: function(retval){
            console.log("retval:" + Memory.readUtf8String(retval));
            console.log("args8:" + hexdump(this.args8));
        }
    })
}

5.2 生成逻辑

  1. sub_10898的返回值是a6a9拼接而成
  2. a9是固定字符串:"vOXGo3ad7hLHyTw4Zgu2blCjEBQDcx6z"
  3. a6是通过MD5计算得到(sub_CC34

5.3 MD5计算部分

  1. sub_CC34函数实现MD5算法
  2. 输入参数包括时间戳等关键信息

6. Auth生成总结

  1. 获取当前时间戳(秒级)
  2. 将时间戳与其他固定参数组合
  3. 对组合后的字符串进行MD5计算
  4. 将MD5结果与固定字符串"vOXGo3ad7hLHyTw4Zgu2blCjEBQDcx6z"拼接
  5. 最终拼接结果即为auth

7. 完整调用链

获取时间戳(System.currentTimeMillis()/1000)
→ 传递到RequestInterceptor
→ 传递到MakeUrlClient
→ 调用JniMakeUrl.native_newmakeUrl
→ 调用libmakeurl3.3.0.so中的sub_10898
→ 调用MD5计算(sub_CC34)
→ 拼接MD5结果与固定字符串
→ 返回auth值

8. 关键点总结

  1. 使用Frida Hook动态注册的JNI函数
  2. 绕过Frida反调试(使用Florida)
  3. 逆向分析so库中的关键函数
  4. 识别MD5算法实现
  5. 确定auth由MD5结果与固定字符串拼接而成
某某语音Auth值生成分析教学文档 1. 分析背景 在某某语音应用的网络请求中,发现一个关键的校验参数 auth ,本文档将详细分析该参数的生成过程。 2. 初步定位 2.1 反编译定位 使用jadx反编译APK 搜索关键字"auth"定位到相关代码位置 调用链如下: 2.2 JNI函数定位 native_newmakeUrl 是一个JNI函数 在 makeurl3.3.0.so 库中未找到导出函数名,说明是动态注册 需要Hook RegisterNatives 函数来定位 3. Hook RegisterNatives 3.1 Hook脚本 3.2 反调试绕过 发现存在Frida反调试(特征检测和进程附加) 使用Florida(去除部分特征的server)绕过 成功打印出 native_makeUrl 的地址:0x6168 4. 分析native_ newmakeUrl 4.1 Hook native_ newmakeUrl 4.2 参数分析 多数参数是固定值 关键变化参数:字符串数组2的元素3,是一个时间戳( System.currentTimeMillis()/1000 ) 5. Auth生成过程分析 5.1 关键函数sub_ 10898 v33 被 sub_10898 赋值 Hook sub_10898 : 5.2 生成逻辑 sub_10898 的返回值是 a6 和 a9 拼接而成 a9 是固定字符串:"vOXGo3ad7hLHyTw4Zgu2blCjEBQDcx6z" a6 是通过MD5计算得到( sub_CC34 ) 5.3 MD5计算部分 sub_CC34 函数实现MD5算法 输入参数包括时间戳等关键信息 6. Auth生成总结 获取当前时间戳(秒级) 将时间戳与其他固定参数组合 对组合后的字符串进行MD5计算 将MD5结果与固定字符串"vOXGo3ad7hLHyTw4Zgu2blCjEBQDcx6z"拼接 最终拼接结果即为 auth 值 7. 完整调用链 8. 关键点总结 使用Frida Hook动态注册的JNI函数 绕过Frida反调试(使用Florida) 逆向分析so库中的关键函数 识别MD5算法实现 确定auth由MD5结果与固定字符串拼接而成