feat(huya): getSDID新值族(36B base64常量证书) + A路径实验定界(APK可载Java不可调) + LoginFlow桥接harness

This commit is contained in:
yml2213
2026-08-29 02:10:57 +08:00
parent 87f061ac4f
commit 6bde9beec2
4 changed files with 194 additions and 0 deletions
+16
View File
@@ -780,3 +780,19 @@ UdbAESUtilC1(…) + encrypt ← 后段
- 0x3ba2f4: hdid/ok/adb_ (调试) - 0x3ba2f4: hdid/ok/adb_ (调试)
- OTP mid 长度前缀族 (JCE varint/1/2/4B BE/LE + 消息名前置) = 零命中 → 组合含更深的 cred_packls 字段流 - OTP mid 长度前缀族 (JCE varint/1/2/4B BE/LE + 消息名前置) = 零命中 → 组合含更深的 cred_packls 字段流
- 下轮: 0x3ba484-xref (hdid: 日志的调用者 = 32hex 装配函数) + cred_packls 静态串流 - 下轮: 0x3ba484-xref (hdid: 日志的调用者 = 32hex 装配函数) + cred_packls 静态串流
## §11.29 A路径实测边界 + getSDID 新值族发现 (R6)
### A路径 (unidbg 跑全 Java 登录流) = 实验性定界
- APK 直载成功 (createDalvikVM(apk) ✓), 双库加载 ✓, HuyaAuth 类可解析 ✓
- **纯 Java 方法不可调** (callStaticJniMethod 只走 JNI 符号; unidbg 无 dex 字节码解释器)
- 若要跑 Java 侧 → ProxyClassFactory 主机 JVM 方案 (dex→class 编译登录链 = 重型)
### getSDID 新值族!
- getSDID@0x20ede8 = `*hZrPb62GskrEeTYcLeUTL1fJ1OR8ky3x9qXQq0s6ICJV5v9T` (=36B base64)
- **输入不透明性: 换指纹 GUID/MID 全变, SDID 恒定** = 静态钥材料计算的 native 证书
- SDID 36B: 16B 块 × encode_aes(8钥)/decode_aes(6钥)/md5/sha1/hmac = 全零命中; 帧字段×SDID 签名族 = 零
- 与 ed0db8 关系未定 (golden 帧 = 08-24 态; harness SDID 恒定同值 ⇒ 若关系存在则可验!)
### OTP 消息名差分
hypasswordLogin/MsgLoginReq/LogLoginReq → mid 各异, tail 恒定 8b38f1 → appSign ≠ OTP 任何段
### 下轮: setSafeDeviceId 直调后 getHDID 状态变化 + 0x64670 "hdid: " 装配函数直调
@@ -403,6 +403,18 @@ public class AesProbe {
p.callOtp("1e8bdf7d4f7a01d3" + "13.4.22" + "1.0.80138" + "127.0.0.1" + "xiaomi", "ZMHAVPRaxJ3MtXDjduUnXAKQ"); p.callOtp("1e8bdf7d4f7a01d3" + "13.4.22" + "1.0.80138" + "127.0.0.1" + "xiaomi", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
p.callOtp("5008" + "13.4.22" + "1.0.80138", "ZMHAVPRaxJ3MtXDjduUnXAKQ"); p.callOtp("5008" + "13.4.22" + "1.0.80138", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
p.callOtp("ed0db8334cadd236c00cadf7e11ab5a5", "ZMHAVPRaxJ3MtXDjduUnXAKQ"); p.callOtp("ed0db8334cadd236c00cadf7e11ab5a5", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
// 登录消息名 OTP 对拍 (mid 16B 与 ed0db8 对照)
p.callOtp("hypasswordLogin", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
p.callOtp("hypasswordLogin", "owNMiaCgcHmqoTr3iRamFuHj");
p.callOtp("MsgLoginReq", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
p.callOtp("LogLoginReq", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
// SDID 块直解 (36B 证书的两个16B块 × 全部钥)
p.callDecodeAes("859acf6fad86b24ac479361c2de5132f", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
p.callDecodeAes("859acf6fad86b24ac479361c2de5132f", "owNMiaCgcHmqoTr3iRamFuHj");
p.callDecodeAes("859acf6fad86b24ac479361c2de5132f", "HuyaUdb1928374650qwertyuiop");
p.callDecodeAes("57c9d4e47c932df1f6a5d0ab4b3a2022", "ZMHAVPRaxJ3MtXDjduUnXAKQ");
p.callDecodeAes("57c9d4e47c932df1f6a5d0ab4b3a2022", "owNMiaCgcHmqoTr3iRamFuHj");
p.callDecodeAes("57c9d4e47c932df1f6a5d0ab4b3a2022", "HuyaUdb1928374650qwertyuiop");
p.dumpRegion("0x127ddbe0", 0x127ddf00L, 0x420); p.dumpRegion("0x127ddbe0", 0x127ddf00L, 0x420);
p.dumpRegion("0x127ddf00", 0x127ddf00L, 0x200); p.dumpRegion("0x127ddf00", 0x127ddf00L, 0x200);
p.emulator.close(); p.emulator.close();
@@ -147,6 +147,10 @@ public class HyDeviceId extends AbstractJni {
else if (i == 101 || i == 102) v = System.getProperty("hydev.android_id", "1e8bdf7d4f7a01d3"); else if (i == 101 || i == 102) v = System.getProperty("hydev.android_id", "1e8bdf7d4f7a01d3");
else if (i == 106) v = "a71a0917ab6d0d6a61947a007591cfb97290f88972b047252c0ec7baddcd57df"; else if (i == 106) v = "a71a0917ab6d0d6a61947a007591cfb97290f88972b047252c0ec7baddcd57df";
else if (i == 104 || i == 105) v = ""; else if (i == 104 || i == 105) v = "";
else if (i == 99 || i == 103) v = "7c5387e0539c023c31c4ff0e807e7256117385ee";
else if (i == 5) v = "{\"install\":\"2024-08-24 10:00:00\"}";
else if (i == 107) v = "1e8bdf7d4f7a01d3"; // qimei16
else if (i == 108) v = "0a7dfaa882938a6ab502511452142c571e8bdf7d"; // qimei36 猜测
else if (i == 1) v = "5008"; else if (i == 1) v = "5008";
else if (i == 2) v = "13.4.22"; else if (i == 2) v = "13.4.22";
else if (i == 3) v = "1.14.68"; else if (i == 3) v = "1.14.68";
@@ -350,6 +354,7 @@ public class HyDeviceId extends AbstractJni {
String guid = h.callString(0x20f8c8L, "getGUID"); String guid = h.callString(0x20f8c8L, "getGUID");
String cdid = h.callString(0x20eb08L, "getCDID"); String cdid = h.callString(0x20eb08L, "getCDID");
String hdid = h.callString(0x20f0c8L, "getHDID"); String hdid = h.callString(0x20f0c8L, "getHDID");
String sdid = h.callString(0x20ede8L, "getSDID");
String mid = h.callString(0x20fba8L, "getMID"); String mid = h.callString(0x20fba8L, "getMID");
// 32hex 探针: 再调一轮以观察缓存 (若 init 后 getGUID 重取 = 缓存) // 32hex 探针: 再调一轮以观察缓存 (若 init 后 getGUID 重取 = 缓存)
h.emulator.close(); h.emulator.close();
+161
View File
@@ -0,0 +1,161 @@
package hydev;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.BackendFactory;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.linux.android.dvm.BaseVM;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.DvmObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.memory.MemoryBlock;
import com.github.unidbg.pointer.UnidbgPointer;
import java.io.File;
/**
* A路径: APK 全 dex 加载 + 双 native 库 + 驱动 HuyaAuth 登录流
* 目标: 让 native 真执行 5008 帧编码(含 appSign 签发), 捕获每帧的 32hex
*/
public class LoginFlow extends AbstractJni {
private final AndroidEmulator emulator;
private final VM vm;
private final Module hydev;
private final Module udb;
LoginFlow(String apkPath, String hydevSo, String udbSo) throws Exception {
BackendFactory backend = new Unicorn2Factory(true);
emulator = AndroidEmulatorBuilder.for64Bit()
.setProcessName("com.duowan.kiwi")
.addBackendFactory(backend)
.build();
Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
vm = emulator.createDalvikVM(new File(apkPath));
vm.setJni(this);
vm.setVerbose(false);
DalvikModule dm1 = vm.loadLibrary(new File(hydevSo), false);
dm1.callJNI_OnLoad(emulator);
hydev = dm1.getModule();
System.out.println("[*] hydeviceid base=0x" + Long.toHexString(hydev.base));
DalvikModule dm2 = vm.loadLibrary(new File(udbSo), false);
dm2.callJNI_OnLoad(emulator);
udb = dm2.getModule();
System.out.println("[*] udbauthunify base=0x" + Long.toHexString(udb.base));
}
// ==================== JNI 桩 (保守: 记录 + 回 null/默认) ====================
private static String strArg(Object va, int idx) {
// 兼容: DvmObject 数组或 VaList — 用反射最省事? 不, 用通用 VaList 兼容写法
return null;
}
@Override
public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, com.github.unidbg.linux.android.dvm.VaList vaList) {
System.out.println("[JNI:SObj] " + dvmClass.getClassName() + "->" + signature);
try {
return super.callStaticObjectMethodV(vm, dvmClass, signature, vaList);
} catch (Throwable t) {
// 常见可空兜底
if (signature.endsWith("Ljava/lang/String;") || signature.endsWith("Landroid/content/Context;")
|| signature.endsWith("Ljava/lang/Object;")) {
return null;
}
throw new RuntimeException(t);
}
}
@Override
public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, com.github.unidbg.linux.android.dvm.VaList vaList) {
System.out.println("[JNI:Obj] " + dvmObject.getObjectType() + "->" + signature);
try {
return super.callObjectMethodV(vm, dvmObject, signature, vaList);
} catch (Throwable t) {
if (signature.endsWith("Ljava/lang/String;") || signature.endsWith("Landroid/content/Context;")) return null;
throw new RuntimeException(t);
}
}
@Override
public int callStaticIntMethodV(BaseVM vm, DvmClass dvmClass, String signature, com.github.unidbg.linux.android.dvm.VaList vaList) {
System.out.println("[JNI:SInt] " + dvmClass.getClassName() + "->" + signature);
return super.callStaticIntMethodV(vm, dvmClass, signature, vaList);
}
@Override
public int callIntMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, com.github.unidbg.linux.android.dvm.VaList vaList) {
System.out.println("[JNI:Int] " + dvmObject.getObjectType() + "->" + signature);
return super.callIntMethodV(vm, dvmObject, signature, vaList);
}
@Override
public boolean callStaticBooleanMethodV(BaseVM vm, DvmClass dvmClass, String signature, com.github.unidbg.linux.android.dvm.VaList vaList) {
System.out.println("[JNI:SBool] " + dvmClass.getClassName() + "->" + signature);
return super.callStaticBooleanMethodV(vm, dvmClass, signature, vaList);
}
@Override
public boolean callBooleanMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, com.github.unidbg.linux.android.dvm.VaList vaList) {
System.out.println("[JNI:Bool] " + dvmObject.getObjectType() + "->" + signature);
return super.callBooleanMethodV(vm, dvmObject, signature, vaList);
}
@Override
public long callStaticLongMethodV(BaseVM vm, DvmClass dvmClass, String signature, com.github.unidbg.linux.android.dvm.VaList vaList) {
return super.callStaticLongMethodV(vm, dvmClass, signature, vaList);
}
@Override
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
System.out.println("[JNI:SField] " + dvmClass.getClassName() + " " + signature);
try {
return super.getStaticObjectField(vm, dvmClass, signature);
} catch (Throwable t) {
return null;
}
}
@Override
public void callVoidMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, com.github.unidbg.linux.android.dvm.VaList vaList) {
System.out.println("[JNI:Void] " + dvmObject.getObjectType() + "->" + signature);
super.callVoidMethodV(vm, dvmObject, signature, vaList);
}
@Override
public void callStaticVoidMethodV(BaseVM vm, DvmClass dvmClass, String signature, com.github.unidbg.linux.android.dvm.VaList vaList) {
System.out.println("[JNI:SVoid] " + dvmClass.getClassName() + "->" + signature);
super.callStaticVoidMethodV(vm, dvmClass, signature, vaList);
}
@Override
public DvmObject<?> getObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
System.out.println("[JNI:Field] " + dvmObject.getObjectType() + " " + signature);
try {
return super.getObjectField(vm, dvmObject, signature);
} catch (Throwable t) {
return null;
}
}
public static void main(String[] args) throws Exception {
String apk = "/Users/yml/codes/douyu_login_py/apks/huya-13.4.22-115315-live.apk";
String hydevSo = "/Users/yml/codes/douyu_login_py/tools/unidbg/hydev/so/libhydeviceid_merged_r2.so";
String udbSo = "/Users/yml/codes/douyu_login_py/tools/unidbg/hydev/so/libudbauthunify_merged.so";
LoginFlow lf = new LoginFlow(apk, hydevSo, udbSo);
System.out.println("=== 解析 HuyaAuth ===");
DvmClass ha = lf.vm.resolveClass("com/huyaudbunify/HuyaAuth");
System.out.println("HuyaAuth resolved: " + ha);
ha.callStaticJniMethod(lf.emulator, "getInstance()Lcom/huyaudbunify/HuyaAuth;");
System.out.println("getInstance OK");
lf.emulator.close();
}
}