feat(huya): getSDID新值族(36B base64常量证书) + A路径实验定界(APK可载Java不可调) + LoginFlow桥接harness
This commit is contained in:
@@ -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();
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user