diff --git a/packages/tweak/NetflixEntityAuth/Makefile b/packages/tweak/NetflixEntityAuth/Makefile
new file mode 100644
index 0000000..3285605
--- /dev/null
+++ b/packages/tweak/NetflixEntityAuth/Makefile
@@ -0,0 +1,19 @@
+TARGET := iphone:clang:16.5:15.0
+INSTALL_TARGET_PROCESSES = Argo
+ARCHS = arm64
+THEOS_PACKAGE_SCHEME = rootless
+
+include $(THEOS)/makefiles/common.mk
+
+TWEAK_NAME = NetflixEntityAuth
+
+NetflixEntityAuth_FILES = $(shell find Sources/NetflixEntityAuth -name '*.swift') $(shell find Sources/NetflixEntityAuthC -name '*.m' -o -name '*.c' -o -name '*.mm' -o -name '*.cpp')
+NetflixEntityAuth_SWIFTFLAGS = -ISources/NetflixEntityAuthC/include
+NetflixEntityAuth_CFLAGS = -fobjc-arc -ISources/NetflixEntityAuthC/include -Wno-unused-function
+NetflixEntityAuth_FRAMEWORKS = Foundation
+NetflixEntityAuth_LDFLAGS = -lsubstrate
+
+include $(THEOS_MAKE_PATH)/tweak.mk
+
+after-install::
+ install.exec "chmod 755 /var/jb/Library/MobileSubstrate/DynamicLibraries/NetflixEntityAuth.dylib"
diff --git a/packages/tweak/NetflixEntityAuth/NetflixEntityAuth.plist b/packages/tweak/NetflixEntityAuth/NetflixEntityAuth.plist
new file mode 100644
index 0000000..e94b864
--- /dev/null
+++ b/packages/tweak/NetflixEntityAuth/NetflixEntityAuth.plist
@@ -0,0 +1,13 @@
+
+
+
+
+ Filter
+
+ Bundles
+
+ com.netflix.Netflix
+
+
+
+
diff --git a/packages/tweak/NetflixEntityAuth/Sources/NetflixEntityAuth/Tweak.x.swift b/packages/tweak/NetflixEntityAuth/Sources/NetflixEntityAuth/Tweak.x.swift
new file mode 100644
index 0000000..3c4e135
--- /dev/null
+++ b/packages/tweak/NetflixEntityAuth/Sources/NetflixEntityAuth/Tweak.x.swift
@@ -0,0 +1,2 @@
+import NetflixEntityAuthC
+import Orion
diff --git a/packages/tweak/NetflixEntityAuth/Sources/NetflixEntityAuthC/Tweak.m b/packages/tweak/NetflixEntityAuth/Sources/NetflixEntityAuthC/Tweak.m
new file mode 100644
index 0000000..57a3e08
--- /dev/null
+++ b/packages/tweak/NetflixEntityAuth/Sources/NetflixEntityAuthC/Tweak.m
@@ -0,0 +1,577 @@
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+
+// ---------------------------------------------------------------------------
+// os_log channels
+// ---------------------------------------------------------------------------
+
+static os_log_t g_log_entity = NULL;
+static os_log_t g_log_hmac = NULL;
+static os_log_t g_log_general = NULL;
+
+// ---------------------------------------------------------------------------
+// File log — written to NSTemporaryDirectory() (app-sandboxed temp)
+// also attempted in /var/tmp for root-accessible retrieval
+// ---------------------------------------------------------------------------
+
+static NSString *g_logFile = nil;
+static NSString *g_logFile2 = nil; // secondary log path in /var/tmp
+
+static void file_log_path(NSString *path, NSString *msg) {
+ if (!path) return;
+ @try {
+ NSDateFormatter *df = [[NSDateFormatter alloc] init];
+ df.dateFormat = @"HH:mm:ss.SSS";
+ NSString *line = [NSString stringWithFormat:@"%@ %@\n",
+ [df stringFromDate:[NSDate date]], msg];
+ NSFileHandle *fh = [NSFileHandle fileHandleForWritingAtPath:path];
+ if (!fh) {
+ [line writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];
+ } else {
+ [fh seekToEndOfFile];
+ [fh writeData:[line dataUsingEncoding:NSUTF8StringEncoding]];
+ [fh closeFile];
+ }
+ } @catch (NSException *e) {}
+}
+
+static void file_log(os_log_t channel, NSString *msg) {
+ // Always emit via NSLog so it shows up in oslog output
+ NSLog(@"[NFXEntityAuth] %@", msg);
+ if (channel) {
+ os_log(channel, "%{public}s", msg.UTF8String);
+ }
+ if (g_logFile) file_log_path(g_logFile, msg);
+ if (g_logFile2) file_log_path(g_logFile2, msg);
+}
+
+// ---------------------------------------------------------------------------
+// Helpers
+// ---------------------------------------------------------------------------
+
+static NSString *hexEncode(const uint8_t *data, size_t len) {
+ if (!data || len == 0) return @"(null)";
+ NSMutableString *s = [NSMutableString stringWithCapacity:len * 2];
+ for (size_t i = 0; i < len; i++) {
+ [s appendFormat:@"%02x", data[i]];
+ }
+ return s;
+}
+
+static NSString *hexEncodeShort(const uint8_t *data, size_t len) {
+ if (!data || len == 0) return @"(null)";
+ if (len <= 64) return hexEncode(data, len);
+ NSString *head = hexEncode(data, 32);
+ NSString *tail = hexEncode(data + len - 16, 16);
+ return [NSString stringWithFormat:@"%@...%@ (%zuB)", head, tail, len];
+}
+
+static void writeBinFile(NSString *path, const uint8_t *data, size_t len) {
+ if (!data || len == 0 || !path) return;
+ @try {
+ NSData *d = [NSData dataWithBytes:data length:len];
+ [d writeToFile:path atomically:YES];
+ } @catch (NSException *e) {}
+}
+
+// Reentrancy guard
+static volatile int g_inHook = 0;
+
+// ---------------------------------------------------------------------------
+// Opaque types (OpenSSL)
+// ---------------------------------------------------------------------------
+
+typedef struct evp_md_st EVP_MD;
+
+// ---------------------------------------------------------------------------
+// HOOK 1: HMAC (one-shot) from NFWebCrypto.framework
+//
+// Signature: unsigned char *HMAC(const EVP_MD *evp_md,
+// const void *key, int key_len,
+// const unsigned char *d, size_t n,
+// unsigned char *md, unsigned int *md_len)
+//
+// Filter: 32-byte (SHA-256) outputs only.
+// Logs key_hex, key_len, data_hex, data_len, output_hex.
+// Saves first 32B output to /var/tmp/entityauth_apphmac.bin
+// ---------------------------------------------------------------------------
+
+static unsigned char *(*orig_HMAC)(const EVP_MD *evp_md,
+ const void *key, int key_len,
+ const unsigned char *d, size_t n,
+ unsigned char *md, unsigned int *md_len);
+
+static BOOL g_apphmacSaved = NO;
+
+static unsigned char *hook_HMAC(const EVP_MD *evp_md,
+ const void *key, int key_len,
+ const unsigned char *d, size_t n,
+ unsigned char *md, unsigned int *md_len) {
+ unsigned char *ret = orig_HMAC(evp_md, key, key_len, d, n, md, md_len);
+
+ if (!g_inHook && ret) {
+ unsigned int outLen = (md_len && *md_len > 0) ? *md_len : 0;
+
+ // Determine actual output length: if md_len not set, check EVP_MD output size
+ // For SHA-256 the output is 32 bytes
+ if (outLen == 0 && ret) {
+ // Assume 32B if md_len is not set and key_len is plausible
+ outLen = 32;
+ }
+
+ // Only capture 32-byte (SHA-256) outputs — that is the apphmac
+ if (outLen == 32 && key && key_len > 0 && key_len <= 256) {
+ g_inHook = 1;
+
+ NSString *keyHex = hexEncodeShort((const uint8_t *)key, (size_t)key_len);
+ NSString *dataHex = hexEncodeShort((const uint8_t *)d, n);
+ NSString *outHex = hexEncode(ret, 32);
+
+ file_log(g_log_hmac,
+ [NSString stringWithFormat:
+ @"[NFXEntityAuth][HMAC] key(%dB)=%@ data(%zuB)=%@ digest(32B)=%@",
+ key_len, keyHex, n, dataHex, outHex]);
+
+ // Save first 32B HMAC as candidate apphmac
+ if (!g_apphmacSaved) {
+ NSString *tmpDir = NSTemporaryDirectory();
+ writeBinFile([tmpDir stringByAppendingPathComponent:@"entityauth_apphmac.bin"], ret, 32);
+ writeBinFile([tmpDir stringByAppendingPathComponent:@"entityauth_hmac_key.bin"],
+ (const uint8_t *)key, (size_t)key_len);
+ writeBinFile([tmpDir stringByAppendingPathComponent:@"entityauth_hmac_data.bin"],
+ (const uint8_t *)d, n);
+ file_log(g_log_hmac, @"[NFXEntityAuth][HMAC] apphmac candidate saved to app tmp dir");
+ g_apphmacSaved = YES;
+ }
+
+ g_inHook = 0;
+ }
+ }
+ return ret;
+}
+
+// ---------------------------------------------------------------------------
+// HOOK 2: NSData dataWithContentsOfFile: — catch large file reads (4-10 KB)
+// that could be device_key_data / devicetoken source material.
+// ---------------------------------------------------------------------------
+
+static NSData *(*orig_dataWithContentsOfFile)(id cls, SEL sel, NSString *path);
+
+static NSData *hook_dataWithContentsOfFile(id cls, SEL sel, NSString *path) {
+ NSData *result = orig_dataWithContentsOfFile(cls, sel, path);
+
+ if (!g_inHook && result && path) {
+ NSUInteger len = [result length];
+ // Filter: 4 KB – 10 KB range (likely device key data)
+ if (len >= 4096 && len <= 10240) {
+ g_inHook = 1;
+ file_log(g_log_general,
+ [NSString stringWithFormat:
+ @"[NFXEntityAuth][NSData] dataWithContentsOfFile: path=%@ size=%luB",
+ path, (unsigned long)len]);
+ // Save binary data
+ NSString *safeName = [[path lastPathComponent]
+ stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
+ NSString *savePath = [NSTemporaryDirectory()
+ stringByAppendingPathComponent:
+ [NSString stringWithFormat:@"entityauth_fileread_%@.bin", safeName]];
+ [result writeToFile:savePath atomically:YES];
+ file_log(g_log_general,
+ [NSString stringWithFormat:@"[NFXEntityAuth][NSData] saved to %@", savePath]);
+ g_inHook = 0;
+ }
+ }
+ return result;
+}
+
+// ---------------------------------------------------------------------------
+// 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 (ignored)
+//
+// We hook via MSHookFunction using the resolved symbol address.
+// ---------------------------------------------------------------------------
+
+// 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.
+
+typedef void (*IosMGKAuthData_ctor_t)(id self,
+ NSString *identity,
+ NSString *appid,
+ int64_t appkeyversion,
+ NSString *apphmac,
+ void *webcrypto_ptr);
+
+static IosMGKAuthData_ctor_t orig_IosMGKAuthData_ctor = NULL;
+
+static void hook_IosMGKAuthData_ctor(id self,
+ NSString *identity,
+ NSString *appid,
+ int64_t appkeyversion,
+ NSString *apphmac,
+ void *webcrypto_ptr) {
+ // Call original first
+ if (orig_IosMGKAuthData_ctor) {
+ orig_IosMGKAuthData_ctor(self, identity, appid, appkeyversion, apphmac, webcrypto_ptr);
+ }
+
+ if (!g_inHook) {
+ g_inHook = 1;
+
+ file_log(g_log_entity,
+ [NSString stringWithFormat:
+ @"[NFXEntityAuth][IosMGKAuthData] identity=%@ appid=%@ appkeyversion=%lld apphmac=%@",
+ identity ?: @"(nil)", appid ?: @"(nil)", appkeyversion, apphmac ?: @"(nil)"]);
+
+ 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];
+ // Decode base64 and save raw bytes
+ NSData *hmacData = [[NSData alloc] initWithBase64EncodedString:apphmac 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)=%@",
+ (unsigned long)[hmacData length],
+ hexEncode((const uint8_t *)[hmacData bytes], [hmacData length])]);
+ }
+ }
+
+ g_inHook = 0;
+ }
+}
+
+// ---------------------------------------------------------------------------
+// MslClient.framework: getAuthData method hook for devicetoken
+//
+// The getAuthData method lives at vtable offset +0xe8.
+// We hook it via ObjC message interception on the MslRegistration class or
+// by hooking -[IosMGKAuthenticationData getAuthData] if accessible.
+//
+// Alternative: Hook -[MslRegistration getDeviceTokensWithCallback:]
+// which is in Nbp.framework and passes devicetoken to its callback.
+// We hook this ObjC method via MSHookMessageEx.
+// ---------------------------------------------------------------------------
+
+static void (*orig_getDeviceTokensWithCallback)(id self, SEL sel, id callback) = NULL;
+
+static void hook_getDeviceTokensWithCallback(id self, SEL sel, id callback) {
+ if (!g_inHook) {
+ file_log(g_log_entity,
+ @"[NFXEntityAuth][MslRegistration] getDeviceTokensWithCallback: called");
+ }
+
+ // Wrap the callback to intercept the devicetoken argument
+ // The callback is typically a block: ^(NSString *devicetoken, NSError *error)
+ // We wrap it so we can log the value when it arrives.
+ id wrappedCallback = nil;
+ if (callback) {
+ // Try to wrap as a block taking (id, id)
+ __block id origBlock = [callback copy];
+ wrappedCallback = ^(id arg0, id arg1) {
+ if (!g_inHook) {
+ g_inHook = 1;
+ // arg0 is expected to be NSString* devicetoken
+ if ([arg0 isKindOfClass:[NSString class]]) {
+ NSString *tokenStr = (NSString *)arg0;
+ file_log(g_log_entity,
+ [NSString stringWithFormat:
+ @"[NFXEntityAuth][devicetoken] len=%lu value=%@",
+ (unsigned long)[tokenStr length], tokenStr]);
+ NSString *dtTmpDir = NSTemporaryDirectory();
+ [tokenStr writeToFile:[dtTmpDir stringByAppendingPathComponent:@"entityauth_devicetoken.txt"]
+ atomically:YES
+ encoding:NSUTF8StringEncoding
+ error:nil];
+ // Also save as raw UTF-8 bytes
+ NSData *tokenData = [tokenStr dataUsingEncoding:NSUTF8StringEncoding];
+ if (tokenData) {
+ [tokenData writeToFile:[dtTmpDir stringByAppendingPathComponent:@"entityauth_devicetoken.bin"]
+ atomically:YES];
+ }
+ } else if ([arg0 isKindOfClass:[NSData class]]) {
+ NSData *tokenData = (NSData *)arg0;
+ file_log(g_log_entity,
+ [NSString stringWithFormat:
+ @"[NFXEntityAuth][devicetoken] NSData len=%lu hex=%@",
+ (unsigned long)[tokenData length],
+ hexEncodeShort((const uint8_t *)[tokenData bytes], [tokenData length])]);
+ [tokenData writeToFile:[NSTemporaryDirectory() stringByAppendingPathComponent:@"entityauth_devicetoken.bin"]
+ atomically:YES];
+ } else {
+ file_log(g_log_entity,
+ [NSString stringWithFormat:
+ @"[NFXEntityAuth][devicetoken] arg0 class=%@ value=%@",
+ NSStringFromClass([arg0 class]), arg0]);
+ }
+ g_inHook = 0;
+ }
+ // Call original block
+ if (origBlock) {
+ void (^blk)(id, id) = origBlock;
+ blk(arg0, arg1);
+ }
+ };
+ }
+
+ orig_getDeviceTokensWithCallback(self, sel, wrappedCallback ?: callback);
+}
+
+// ---------------------------------------------------------------------------
+// Delayed MslClient hook installer
+// Called after a short delay to give MslClient.framework time to load.
+// ---------------------------------------------------------------------------
+
+static BOOL g_mslHooked = NO;
+
+static void installMslClientHooks(void) {
+ if (g_mslHooked) return;
+
+ void *msl = dlopen("@rpath/MslClient.framework/MslClient", RTLD_NOLOAD);
+ if (!msl) {
+ // Try common app container path patterns
+ msl = dlopen("/var/containers/Bundle/Application/2A734797-B5EA-4048-B255-C90EA4D50196/Argo.app/Frameworks/MslClient.framework/MslClient", RTLD_NOLOAD);
+ }
+ if (!msl) {
+ file_log(g_log_general, @"[NFXEntityAuth] MslClient not loaded yet");
+ return;
+ }
+
+ file_log(g_log_general, @"[NFXEntityAuth] MslClient.framework found");
+
+ // IosMGKAuthenticationData constructor at offset 0x0000d45c
+ // We need the base address of the MslClient image.
+ // Use dladdr or iterate _dyld_* APIs.
+ // Approach: use dlsym for a known exported symbol to get image base,
+ // then add offset.
+ //
+ // There are no guaranteed exported C symbols in MslClient that we know of,
+ // so we use the image base directly via _dyld APIs.
+
+ uint32_t imgCount = _dyld_image_count();
+ uintptr_t mslBase = 0;
+ for (uint32_t i = 0; i < imgCount; i++) {
+ const char *name = _dyld_get_image_name(i);
+ if (name && strstr(name, "MslClient.framework/MslClient")) {
+ mslBase = (uintptr_t)_dyld_get_image_header(i);
+ file_log(g_log_general,
+ [NSString stringWithFormat:@"[NFXEntityAuth] MslClient base=0x%lx (%s)",
+ (unsigned long)mslBase, name]);
+ break;
+ }
+ }
+
+ if (mslBase == 0) {
+ file_log(g_log_general, @"[NFXEntityAuth] MslClient base not found in dyld image list");
+ 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;
+ file_log(g_log_general,
+ [NSString stringWithFormat:@"[NFXEntityAuth] IosMGKAuthData 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");
+
+ g_mslHooked = YES;
+}
+
+// ---------------------------------------------------------------------------
+// Nbp.framework: hook -[MslRegistration getDeviceTokensWithCallback:]
+// ---------------------------------------------------------------------------
+
+static BOOL g_nbpHooked = NO;
+
+static void installNbpHooks(void) {
+ if (g_nbpHooked) return;
+
+ void *nbp = dlopen("@rpath/Nbp.framework/Nbp", RTLD_NOLOAD);
+ if (!nbp) {
+ nbp = dlopen("/var/containers/Bundle/Application/2A734797-B5EA-4048-B255-C90EA4D50196/Argo.app/Frameworks/Nbp.framework/Nbp", RTLD_NOLOAD);
+ }
+ if (!nbp) {
+ file_log(g_log_general, @"[NFXEntityAuth] Nbp not loaded yet");
+ return;
+ }
+
+ file_log(g_log_general, @"[NFXEntityAuth] Nbp.framework found");
+
+ Class mslRegClass = NSClassFromString(@"MslRegistration");
+ if (!mslRegClass) {
+ file_log(g_log_general, @"[NFXEntityAuth] MslRegistration class not found");
+ return;
+ }
+
+ SEL sel = NSSelectorFromString(@"getDeviceTokensWithCallback:");
+ Method m = class_getInstanceMethod(mslRegClass, sel);
+ if (!m) {
+ file_log(g_log_general, @"[NFXEntityAuth] getDeviceTokensWithCallback: method not found");
+ return;
+ }
+
+ MSHookMessageEx(mslRegClass,
+ sel,
+ (IMP)hook_getDeviceTokensWithCallback,
+ (IMP *)&orig_getDeviceTokensWithCallback);
+ file_log(g_log_general, @"[NFXEntityAuth] getDeviceTokensWithCallback: hooked");
+ g_nbpHooked = YES;
+}
+
+// ---------------------------------------------------------------------------
+// NFWebCrypto HMAC hook installer
+// ---------------------------------------------------------------------------
+
+static BOOL g_nfwcHooked = NO;
+
+static void installNFWebCryptoHooks(void) {
+ if (g_nfwcHooked) return;
+
+ void *nfwc = dlopen("@rpath/NFWebCrypto.framework/NFWebCrypto", RTLD_NOLOAD);
+ if (!nfwc) {
+ nfwc = dlopen("/var/containers/Bundle/Application/2A734797-B5EA-4048-B255-C90EA4D50196/Argo.app/Frameworks/NFWebCrypto.framework/NFWebCrypto", RTLD_NOLOAD);
+ }
+ if (!nfwc) {
+ file_log(g_log_general, @"[NFXEntityAuth] NFWebCrypto not loaded yet — retrying with RTLD_LAZY");
+ nfwc = dlopen("@rpath/NFWebCrypto.framework/NFWebCrypto", RTLD_LAZY);
+ }
+ if (!nfwc) {
+ file_log(g_log_general, @"[NFXEntityAuth] NFWebCrypto not found");
+ return;
+ }
+
+ file_log(g_log_general, @"[NFXEntityAuth] NFWebCrypto.framework found");
+
+ void *sym = dlsym(nfwc, "HMAC");
+ if (sym) {
+ MSHookFunction(sym, (void *)hook_HMAC, (void **)&orig_HMAC);
+ file_log(g_log_general, @"[NFXEntityAuth] HMAC hooked");
+ } else {
+ file_log(g_log_general, @"[NFXEntityAuth] HMAC symbol not found");
+ }
+
+ g_nfwcHooked = YES;
+}
+
+// ---------------------------------------------------------------------------
+// NSData hook installer (global — no framework needed)
+// ---------------------------------------------------------------------------
+
+static void installNSDataHooks(void) {
+ Class nsDataClass = [NSData class];
+ SEL sel = @selector(dataWithContentsOfFile:);
+ Method m = class_getClassMethod(nsDataClass, sel);
+ if (m) {
+ // Hook the class method via MSHookMessageEx on the metaclass
+ MSHookMessageEx(object_getClass(nsDataClass),
+ sel,
+ (IMP)hook_dataWithContentsOfFile,
+ (IMP *)&orig_dataWithContentsOfFile);
+ file_log(g_log_general, @"[NFXEntityAuth] NSData dataWithContentsOfFile: hooked");
+ } else {
+ file_log(g_log_general, @"[NFXEntityAuth] NSData dataWithContentsOfFile: method not found");
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Retry installer — polls for frameworks not yet loaded at constructor time
+// ---------------------------------------------------------------------------
+
+static void retryInstallHooks(void) {
+ if (!g_nfwcHooked) installNFWebCryptoHooks();
+ if (!g_mslHooked) installMslClientHooks();
+ if (!g_nbpHooked) installNbpHooks();
+
+ BOOL allDone = g_nfwcHooked && g_mslHooked && g_nbpHooked;
+ if (!allDone) {
+ // Retry again after 2 seconds
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)),
+ dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ retryInstallHooks();
+ });
+ } else {
+ file_log(g_log_general, @"[NFXEntityAuth] All hooks installed");
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Constructor
+// ---------------------------------------------------------------------------
+
+__attribute__((constructor)) static void init(void) {
+ orion_init();
+
+ g_log_entity = os_log_create("com.netflix.entityauth", "entity");
+ g_log_hmac = os_log_create("com.netflix.entityauth", "hmac");
+ g_log_general = os_log_create("com.netflix.entityauth", "general");
+
+ // Primary log in sandboxed tmp (always writable by mobile user)
+ g_logFile = [NSTemporaryDirectory()
+ stringByAppendingPathComponent:@"entityauth_capture.log"];
+ // Secondary log in /var/tmp (accessible by root for easy retrieval)
+ g_logFile2 = @"/var/tmp/entityauth_capture.log";
+
+ file_log(g_log_general, @"=== NetflixEntityAuth loaded ===");
+
+ // Install NSData hook immediately (Foundation is always loaded)
+ installNSDataHooks();
+
+ // Attempt immediate hook install for frameworks that may already be loaded
+ installNFWebCryptoHooks();
+ installMslClientHooks();
+ installNbpHooks();
+
+ // Schedule retries for frameworks that load after us
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)),
+ dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ retryInstallHooks();
+ });
+
+ file_log(g_log_general, @"=== NetflixEntityAuth constructor done ===");
+}
diff --git a/packages/tweak/NetflixEntityAuth/Sources/NetflixEntityAuthC/include/NetflixEntityAuthC.h b/packages/tweak/NetflixEntityAuth/Sources/NetflixEntityAuthC/include/NetflixEntityAuthC.h
new file mode 100644
index 0000000..2d0a8ca
--- /dev/null
+++ b/packages/tweak/NetflixEntityAuth/Sources/NetflixEntityAuthC/include/NetflixEntityAuthC.h
@@ -0,0 +1,7 @@
+#ifndef NetflixEntityAuthC_h
+#define NetflixEntityAuthC_h
+
+// C module header for NetflixEntityAuth tweak.
+// Orion's Tweak.x.swift imports this module to trigger the constructor in Tweak.m.
+
+#endif /* NetflixEntityAuthC_h */
diff --git a/packages/tweak/NetflixEntityAuth/Sources/NetflixEntityAuthC/include/module.modulemap b/packages/tweak/NetflixEntityAuth/Sources/NetflixEntityAuthC/include/module.modulemap
new file mode 100644
index 0000000..a190ad8
--- /dev/null
+++ b/packages/tweak/NetflixEntityAuth/Sources/NetflixEntityAuthC/include/module.modulemap
@@ -0,0 +1,4 @@
+module NetflixEntityAuthC {
+ header "NetflixEntityAuthC.h"
+ export *
+}
diff --git a/packages/tweak/NetflixEntityAuth/control b/packages/tweak/NetflixEntityAuth/control
new file mode 100644
index 0000000..6958311
--- /dev/null
+++ b/packages/tweak/NetflixEntityAuth/control
@@ -0,0 +1,9 @@
+Package: work.tkgstrator.netflixentityauth
+Name: NetflixEntityAuth
+Version: 1.0.0
+Architecture: iphoneos-arm64
+Description: Netflix iOS entity_auth_data capture (devicetoken, apphmac, ESN, appid, appkeyversion)
+Maintainer: NEVER KNOWS BEST
+Author: tkgstrator
+Section: Tweaks
+Depends: mobilesubstrate, dev.theos.orion14 (>= 1.0.1)