feat(tweak): FpsMgkAppIdAuthData (0x28038) をフック — レガシー IosMGKAuthData から移行

- IosMGKAuthenticationData (0xd45c) は LEGACY で appboot 時に呼ばれないことを確認
- 実際のアクティブクラスは FpsMgkAppIdAuthData (MslClient @ 0x28038)
- コンストラクタ引数: mgkid, devtype, appid, appkeyversion, apphmac
- libc++ std::string SSO レイアウト対応の readStdString() ヘルパー追加
- ログイン後の appboot で apphmac 値を直接キャプチャ可能

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
tkgstrator
2026-04-09 12:19:53 +00:00
co-authored by Claude Opus 4.6
parent 7ca4183a08
commit aa9ec14bde
14 changed files with 88 additions and 75 deletions
@@ -6,5 +6,5 @@ Author: tkgstrator
Section: Tweaks
Depends: mobilesubstrate, dev.theos.orion14 (>= 1.0.1)
Architecture: iphoneos-arm64
Version: 1.0.0-9+debug
Installed-Size: 140
Version: 1.0.0-10+debug
Installed-Size: 144
@@ -1 +1 @@
./packages/work.tkgstrator.netflixentityauth_1.0.0-9+debug_iphoneos-arm64.deb
./packages/work.tkgstrator.netflixentityauth_1.0.0-10+debug_iphoneos-arm64.deb
@@ -1,7 +1,7 @@
version: "Swift version 5.8 (swift-5.8-RELEASE)"
options: "46ac4fc8a6fab716165f87c6eadba55bec7c9ab0850730661a3e566653e8c94c"
build_start_time: [1775731749, 928379000]
build_end_time: [1775731750, 103601000]
build_start_time: [1775737093, 614918000]
build_end_time: [1775737093, 745030000]
inputs:
"/home/vscode/app/packages/tweak/NetflixEntityAuth/.theos/obj/debug/arm64/generated/NetflixEntityAuth.xc.swift": [1775731749, 0]
"/home/vscode/app/packages/tweak/NetflixEntityAuth/.theos/obj/debug/arm64/generated/NetflixEntityAuth.xc.swift": [1775737093, 0]
"Sources/NetflixEntityAuth/Tweak.x.swift": [1775724899, 0]
@@ -1 +1 @@
{"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/generated\/NetflixEntityAuth.xc.swift":{"object":"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/generated\/NetflixEntityAuth.xc.swift.276b477d.o","dependencies":"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/generated\/NetflixEntityAuth.xc.swift.276b477d.Td","swift-dependencies":"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/generated\/NetflixEntityAuth.xc.swift.276b477d.swiftdeps"},"Sources\/NetflixEntityAuth\/Tweak.x.swift":{"dependencies":"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/Sources\/NetflixEntityAuth\/Tweak.x.swift.276b477d.Td","object":"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/Sources\/NetflixEntityAuth\/Tweak.x.swift.276b477d.o","swift-dependencies":"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/Sources\/NetflixEntityAuth\/Tweak.x.swift.276b477d.swiftdeps"},"":{"dependencies":"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/master.276b477d.Td","swift-dependencies":"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/master.276b477d.swiftdeps"}}
{"Sources\/NetflixEntityAuth\/Tweak.x.swift":{"object":"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/Sources\/NetflixEntityAuth\/Tweak.x.swift.276b477d.o","swift-dependencies":"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/Sources\/NetflixEntityAuth\/Tweak.x.swift.276b477d.swiftdeps","dependencies":"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/Sources\/NetflixEntityAuth\/Tweak.x.swift.276b477d.Td"},"":{"dependencies":"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/master.276b477d.Td","swift-dependencies":"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/master.276b477d.swiftdeps"},"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/generated\/NetflixEntityAuth.xc.swift":{"swift-dependencies":"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/generated\/NetflixEntityAuth.xc.swift.276b477d.swiftdeps","dependencies":"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/generated\/NetflixEntityAuth.xc.swift.276b477d.Td","object":"\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/\/home\/vscode\/app\/packages\/tweak\/NetflixEntityAuth\/.theos\/obj\/debug\/arm64\/generated\/NetflixEntityAuth.xc.swift.276b477d.o"}}
@@ -801,83 +801,99 @@ static NSData *hook_dataWithContentsOfFile(id cls, SEL sel, NSString *path) {
// ---------------------------------------------------------------------------
// MslClient.framework hook setup
//
// IosMGKAuthenticationData constructor (offset 0x0000d45c):
// x0: self
// x1: identity (NSString* — ESN)
// x2: appid (NSString*)
// x3: appkeyversion (int64)
// x4: apphmac (NSString* — base64)
// x5: shared_ptr<AppleWebCrypto> (ignored)
// FpsMgkAppIdAuthData constructor (offset 0x00028038):
// x0: self (this pointer)
// x1: &mgkid (std::string const&)
// x2: &devtype (std::string const&)
// x3: &appid (std::string const&)
// w4: appkeyversion (int)
// x5: &apphmac (std::string const&)
// x6: AppleWebCrypto*
// x7: SynchronizedCdmAuthGeneration*
//
// We hook via MSHookFunction using the resolved symbol address.
// Note: IosMGKAuthenticationData at 0xd45c is LEGACY and never fires.
// The active scheme is FpsMgkAppIdAuthData.
// ---------------------------------------------------------------------------
// ARM64 calling convention: ObjC id args are in x0..x7 registers.
// The constructor is a plain C++ function (not ObjC method), so we model
// it as a plain C function pointer with the matching argument layout.
// Helper: read libc++ std::string (SSO layout)
// If byte at [str+0x17] (sign bit) is non-negative => short string, data inline at str
// Otherwise => long string, pointer at [str], length at [str+8]
static NSString *readStdString(const void *strPtr) {
if (!strPtr) return @"(nil)";
const uint8_t *raw = (const uint8_t *)strPtr;
int8_t signByte = (int8_t)raw[0x17];
if (signByte >= 0) {
// Short (SSO) string: length = signByte, data at raw[0]
size_t len = (size_t)(uint8_t)signByte;
return [[NSString alloc] initWithBytes:raw length:len encoding:NSUTF8StringEncoding] ?: @"(unreadable)";
} else {
// Long string: pointer at offset 0, length at offset 8
const char *data = *(const char **)raw;
size_t len = *(const size_t *)(raw + 8);
if (!data || len == 0 || len > 65536) return @"(long/invalid)";
return [[NSString alloc] initWithBytes:data length:len encoding:NSUTF8StringEncoding] ?: @"(unreadable)";
}
}
typedef void (*IosMGKAuthData_ctor_t)(id self,
NSString *identity,
NSString *appid,
int64_t appkeyversion,
NSString *apphmac,
void *webcrypto_ptr);
typedef void (*FpsMgkAppIdAuthData_ctor_t)(void *self,
const void *mgkid,
const void *devtype,
const void *appid,
int appkeyversion,
const void *apphmac,
void *webCrypto,
void *authGen);
static IosMGKAuthData_ctor_t orig_IosMGKAuthData_ctor = NULL;
static FpsMgkAppIdAuthData_ctor_t orig_FpsMgkAppIdAuthData_ctor = NULL;
static void hook_IosMGKAuthData_ctor(id self,
NSString *identity,
NSString *appid,
int64_t appkeyversion,
NSString *apphmac,
void *webcrypto_ptr) {
static void hook_FpsMgkAppIdAuthData_ctor(void *self,
const void *mgkid,
const void *devtype,
const void *appid,
int appkeyversion,
const void *apphmac,
void *webCrypto,
void *authGen) {
// Call original first
if (orig_IosMGKAuthData_ctor) {
orig_IosMGKAuthData_ctor(self, identity, appid, appkeyversion, apphmac, webcrypto_ptr);
if (orig_FpsMgkAppIdAuthData_ctor) {
orig_FpsMgkAppIdAuthData_ctor(self, mgkid, devtype, appid, appkeyversion, apphmac, webCrypto, authGen);
}
if (!g_inHook) {
g_inHook = 1;
NSString *mgkidStr = readStdString(mgkid);
NSString *devtypeStr = readStdString(devtype);
NSString *appidStr = readStdString(appid);
NSString *apphmacStr = readStdString(apphmac);
file_log(g_log_entity,
[NSString stringWithFormat:
@"[NFXEntityAuth][IosMGKAuthData] identity=%@ appid=%@ appkeyversion=%lld apphmac=%@",
identity ?: @"(nil)", appid ?: @"(nil)", appkeyversion, apphmac ?: @"(nil)"]);
@"[NFXEntityAuth][FpsMgkAppIdAuthData] mgkid=%@ devtype=%@ appid=%@ appkeyversion=%d apphmac=%@",
mgkidStr, devtypeStr, appidStr, appkeyversion, apphmacStr]);
NSString *tmpDir = NSTemporaryDirectory();
// Write identity (ESN) to file
if (identity) {
[identity writeToFile:[tmpDir stringByAppendingPathComponent:@"entityauth_identity.txt"]
atomically:YES
encoding:NSUTF8StringEncoding
error:nil];
}
// Write appid to file
if (appid) {
[appid writeToFile:[tmpDir stringByAppendingPathComponent:@"entityauth_appid.txt"]
atomically:YES
encoding:NSUTF8StringEncoding
error:nil];
}
// Write appkeyversion
NSString *akvStr = [NSString stringWithFormat:@"%lld", appkeyversion];
[akvStr writeToFile:[tmpDir stringByAppendingPathComponent:@"entityauth_appkeyversion.txt"]
atomically:YES
encoding:NSUTF8StringEncoding
error:nil];
// Write apphmac base64
if (apphmac) {
[apphmac writeToFile:[tmpDir stringByAppendingPathComponent:@"entityauth_apphmac_b64.txt"]
atomically:YES
encoding:NSUTF8StringEncoding
error:nil];
// Save all fields
[mgkidStr writeToFile:[tmpDir stringByAppendingPathComponent:@"entityauth_mgkid.txt"]
atomically:YES encoding:NSUTF8StringEncoding error:nil];
[devtypeStr writeToFile:[tmpDir stringByAppendingPathComponent:@"entityauth_devtype.txt"]
atomically:YES encoding:NSUTF8StringEncoding error:nil];
[appidStr writeToFile:[tmpDir stringByAppendingPathComponent:@"entityauth_appid.txt"]
atomically:YES encoding:NSUTF8StringEncoding error:nil];
[[NSString stringWithFormat:@"%d", appkeyversion]
writeToFile:[tmpDir stringByAppendingPathComponent:@"entityauth_appkeyversion.txt"]
atomically:YES encoding:NSUTF8StringEncoding error:nil];
if (apphmacStr && ![apphmacStr isEqualToString:@"(nil)"]) {
[apphmacStr writeToFile:[tmpDir stringByAppendingPathComponent:@"entityauth_apphmac_b64.txt"]
atomically:YES encoding:NSUTF8StringEncoding error:nil];
// Decode base64 and save raw bytes
NSData *hmacData = [[NSData alloc] initWithBase64EncodedString:apphmac options:0];
NSData *hmacData = [[NSData alloc] initWithBase64EncodedString:apphmacStr options:0];
if (hmacData) {
[hmacData writeToFile:[tmpDir stringByAppendingPathComponent:@"entityauth_apphmac_raw.bin"]
atomically:YES];
file_log(g_log_entity,
[NSString stringWithFormat:@"[NFXEntityAuth][IosMGKAuthData] apphmac_raw(%luB)=%@",
[NSString stringWithFormat:@"[NFXEntityAuth][FpsMgkAppIdAuthData] apphmac_raw(%luB)=%@",
(unsigned long)[hmacData length],
hexEncode((const uint8_t *)[hmacData bytes], [hmacData length])]);
}
@@ -1012,21 +1028,18 @@ static void installMslClientHooks(void) {
return;
}
// IosMGKAuthenticationData constructor offset 0x0000d45c
// Note: on arm64 with ASLR the actual address = base + slide + text_offset
// _dyld_get_image_header returns the load address (includes ASLR slide).
// For a __TEXT segment starting at offset 0, the load address IS the base.
// The offset 0xd45c is from the start of the binary (Mach-O header), which
// equals the load address for arm64 dylibs.
uintptr_t ctorAddr = mslBase + 0xd45c;
// FpsMgkAppIdAuthData constructor offset 0x00028038
// This is the ACTIVE entity_auth_data class for appboot.
// (IosMGKAuthenticationData at 0xd45c is LEGACY and never fires.)
uintptr_t ctorAddr = mslBase + 0x28038;
file_log(g_log_general,
[NSString stringWithFormat:@"[NFXEntityAuth] IosMGKAuthData ctor addr=0x%lx",
[NSString stringWithFormat:@"[NFXEntityAuth] FpsMgkAppIdAuthData ctor addr=0x%lx",
(unsigned long)ctorAddr]);
MSHookFunction((void *)ctorAddr,
(void *)hook_IosMGKAuthData_ctor,
(void **)&orig_IosMGKAuthData_ctor);
file_log(g_log_general, @"[NFXEntityAuth] IosMGKAuthData ctor hooked");
(void *)hook_FpsMgkAppIdAuthData_ctor,
(void **)&orig_FpsMgkAppIdAuthData_ctor);
file_log(g_log_general, @"[NFXEntityAuth] FpsMgkAppIdAuthData ctor hooked");
g_mslHooked = YES;
}