diff --git a/ios-change-info/Makefile b/ios-change-info/Makefile index e75aeef..244fcab 100644 --- a/ios-change-info/Makefile +++ b/ios-change-info/Makefile @@ -14,7 +14,7 @@ TWEAK_NAME = OhNo OhNo_FRAMEWORKS = CoreLocation CFNetwork CoreTelephony Security -OhNo_FILES = Tweak.x XSLog.m +OhNo_FILES = Tweak.x XSLog.m XHookSettingsManager.m OhNo_CFLAGS = -Wno-error -Wno-module-import-in-extern-c -fobjc-arc diff --git a/ios-change-info/MyTweak.h b/ios-change-info/MyTweak.h deleted file mode 100644 index 62457ed..0000000 --- a/ios-change-info/MyTweak.h +++ /dev/null @@ -1,28 +0,0 @@ -#import -#import - -@interface CustomMTLDevice : NSObject -@property (nonatomic, strong) id originalDevice; -@property (nonatomic, strong) NSString *customName; -- (instancetype)initWithOriginalDevice:(id)device customName:(NSString *)name; -@end - -@implementation CustomMTLDevice - -- (instancetype)initWithOriginalDevice:(id)device customName:(NSString *)name { - if (self = [super init]) { - _originalDevice = device; - _customName = name; - } - return self; -} - -- (NSString *)name { - return self.customName ?: [self.originalDevice name]; -} - -- (id)forwardingTargetForSelector:(SEL)aSelector { - return self.originalDevice; -} - -@end \ No newline at end of file diff --git a/ios-change-info/Tweak.x b/ios-change-info/Tweak.x index 887ec48..ad7ccac 100644 --- a/ios-change-info/Tweak.x +++ b/ios-change-info/Tweak.x @@ -1,231 +1,112 @@ +// +// Tweak.x +// 主入口文件 - 包含所有 Hooks +// + #import -#import -#include -#import #import #import -#import // 获取ip -#import // 获取ip -#import // 获取ip -#import -#import +#import #import #import #import -// #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000 #import -// #endif -#import -#import +#import +#import +#include +#import +#import +#import #import #import "XSLog.h" +#import "XHookSettingsManager.h" +// 设置日志 void SetupXSLog() { XSLogSetupLogWithFilePath(@"/var/mobile/Documents/App.log", 50 * 1024 * 1024); XSLogRedirectNSLog(); } +#pragma mark - ============== HOOKS GROUP ============== -typedef void (^tryGetValCallback)(id parameter); +%group XHooks - -// iOS版本检查宏 -#define iOS14_OR_LATER() (@available(iOS 14, *)) - - -@interface SBLockScreenManager : NSObject -- (BOOL)unlockUIFromSource:(int)source withOptions:(id)options; -- (void)lockUIFromSource:(int)source withOptions:(id)options; -@end - - - -NSDictionary *settings; - - - -NSUUID* str2uuid(NSString *str) { - return [[NSUUID alloc] initWithUUIDString:str]; -} - -NSInteger str2int(NSString *str) { - return [str integerValue]; -} -float str2float(NSString *str) { - return [str floatValue]; -} -double str2double(NSString *str) { - return [str doubleValue]; -} - -unsigned long long str2ull(NSString *str) { - return [str longLongValue]; -} - -void tryGetVal(NSString *key, tryGetValCallback block) { - @try { - if (!key || !block || !settings) return; - - id data = settings[key]; - if (data) { - block(data); - } - } @catch (NSException *exception) { - // NSLog(@"Error in tryGetVal for key %@: %@", key, exception); - } -} - -const char* str2chars(NSString *str) { - const char *cString = [str UTF8String]; - return cString; -} - - -%group ohno - -// #if __IPHONE_OS_VERSION_MAX_ALLOWED < 140000 - - -// #endif - -// group ohho start - - -// #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000 +#pragma mark - ATTrackingManager Hook %hook ATTrackingManager + (ATTrackingManagerAuthorizationStatus)trackingAuthorizationStatus { - __block ATTrackingManagerAuthorizationStatus originalStatus = %orig; - // NSLog(@"start hook trackingAuthorizationStatus"); - // NSLog(@"[ATTrackingManager Hook] Original trackingAuthorizationStatus: %lu", (unsigned long)originalStatus); - - // 你可以在这里修改返回值 - // 例如,总是返回已授权状态: - // return 3; // ATTrackingManagerAuthorizationStatusAuthorized - tryGetVal(@"trackingStatus", ^(id parameter) { - // NSLog(@"[ATTrackingManager Hook] API trackingAuthorizationStatus: %@", parameter); - originalStatus = str2int(parameter); - }); - - // NSLog(@"[ATTrackingManager Hook] New trackingAuthorizationStatus: %lu", (unsigned long)originalStatus); - // 或者,你可以直接返回原始值 - return originalStatus; - + ATTrackingManagerAuthorizationStatus originalStatus = %orig; + NSUInteger status = [XHookSettings trackingStatus]; + return (ATTrackingManagerAuthorizationStatus)status; } + (void)requestTrackingAuthorizationWithCompletionHandler:(void (^)(ATTrackingManagerAuthorizationStatus status))completion { - // NSLog(@"[ATTrackingManager Hook] requestTrackingAuthorizationWithCompletionHandler called"); - __block ATTrackingManagerAuthorizationStatus attStatus = 3; - tryGetVal(@"trackingStatus", ^(id parameter) { - // NSLog(@"[ATTrackingManager Hook] API trackingAuthorizationStatus: %@", parameter); - attStatus = str2int(parameter); - }); - // 模拟延迟(真实的授权对话框有延迟) + ATTrackingManagerAuthorizationStatus attStatus = (ATTrackingManagerAuthorizationStatus)[XHookSettings trackingStatus]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ if (completion) { completion(attStatus); } }); - - // 调用原始方法 - // %orig(completion); } %end -// #endif + +#pragma mark - ASIdentifierManager Hook %hook ASIdentifierManager --(NSUUID*)advertisingIdentifier -{ - // 修改IDFA - // NSLog(@"start hook idfa"); - __block NSUUID *idfa = %orig; - // reloadConfig(); - tryGetVal(@"idfa", ^(id parameter) { - idfa = str2uuid(parameter); - }); - // NSLog(@"hook idfa: %@", idfa); - return idfa; +- (NSUUID *)advertisingIdentifier { + NSUUID *idfa = [XHookSettings idfa]; + return idfa ?: %orig; } --(BOOL) isAdvertisingTrackingEnabled { - // NSLog(@"[ATTrackingManager Hook] isAdvertisingTrackingEnabled called"); - __block BOOL res = %orig; - tryGetVal(@"trackingStatus", ^(id parameter) { - // NSLog(@"[ATTrackingManager Hook] API isAdvertisingTrackingEnabled: %@", parameter); - NSInteger status = str2int(parameter); - res = (status >= 3 ? YES : NO); - }); - return res; + +- (BOOL)isAdvertisingTrackingEnabled { + NSUInteger status = [XHookSettings trackingStatus]; + return (status >= 3); } %end -// static BOOL orientationMonitoringEnabled = NO; +#pragma mark - UIDevice Hook %hook UIDevice -// IDFV --(NSUUID*)identifierForVendor{ - // NSLog(@"start hook idfv"); - __block NSUUID *idfv = %orig; - tryGetVal(@"idfv", ^(id parameter) { - idfv = str2uuid(parameter); - }); - // NSLog(@"hook idfv: %@", idfv); - return idfv; + +- (NSUUID *)identifierForVendor { + NSUUID *idfv = [XHookSettings idfv]; + return idfv ?: %orig; } -- (NSString *)name{ - // NSLog(@"start hook device name"); - __block NSString *name = %orig; - tryGetVal(@"deviceName", ^(id parameter) { - name = parameter; - }); - // NSLog(@"hook device name: %@", name); - return name; +- (NSString *)name { + NSString *name = [XHookSettings deviceName]; + return name ?: %orig; } -- (NSString *)model{ +- (NSString *)model { return %orig; } -- (NSString *)localizedModel{ +- (NSString *)localizedModel { return %orig; } -- (NSString *)systemName{ +- (NSString *)systemName { return %orig; } -- (NSString *)systemVersion{ - // NSLog(@"start hook system version"); - __block NSString *systemVersion = %orig; - tryGetVal(@"osVersion", ^(id parameter) { - systemVersion = parameter; - }); - // NSLog(@"hook system version: %@", systemVersion); - return systemVersion; +- (NSString *)systemVersion { + NSString *version = [XHookSettings osVersion]; + return version ?: %orig; } - (UIDeviceBatteryState)batteryState { - // NSLog(@"start hook batteryState"); - __block UIDeviceBatteryState res = %orig; - tryGetVal(@"batteryState", ^(id parameter) { - NSInteger intVal = str2int(parameter); - res = (UIDeviceBatteryState)intVal; - }); - return res; + UIDeviceBatteryState state = [XHookSettings batteryState]; + return (state != UIDeviceBatteryStateUnknown) ? state : %orig; } - (float)batteryLevel { - // 随机电量 - // NSLog(@"start hook batteryLevel"); - __block float res = %orig; - tryGetVal(@"batteryLevel", ^(id parameter) { - res = str2float(parameter); - }); - return res; + float level = [XHookSettings batteryLevel]; + return (level > -1.0f) ? level : %orig; } - (UIDeviceOrientation)orientation { @@ -234,53 +115,32 @@ const char* str2chars(NSString *str) { %end +#pragma mark - NSProcessInfo Hook + %hook NSProcessInfo -- (NSTimeInterval) systemUptime { - // 开机时间 - // NSLog(@"start hook systemUptime"); - __block NSTimeInterval res = %orig; - tryGetVal(@"systemUptime", ^(id parameter) { - double v = str2double(parameter); - if(v > -1.0f) { - res = v + res; - } - }); +- (NSTimeInterval)systemUptime { + NSTimeInterval res = %orig; + double offset = [XHookSettings systemUptimeOffset]; + if (offset > -1.0) { + res += offset; + } return res; } -- (unsigned long long) physicalMemory { - // 内存 - // NSLog(@"start hook physicalMemory"); - __block unsigned long long memory = %orig; - tryGetVal(@"physicalMemory", ^(id parameter) { - unsigned long long t = str2ull(parameter); - if(t > 1000) { - memory = t; - } - }); - // NSLog(@"hook physicalMemory: %llu", memory); - return memory; +- (unsigned long long)physicalMemory { + unsigned long long memory = [XHookSettings physicalMemory]; + return (memory > 1000) ? memory : %orig; } -- (NSUInteger) processorCount { - // NSLog(@"start hook processorCount"); - __block NSUInteger res = %orig; - tryGetVal(@"cpuCore", ^(id parameter) { - NSUInteger t = str2int(parameter); - if(t > 0) { - res = t; - } - }); - return res; +- (NSUInteger)processorCount { + NSUInteger cores = [XHookSettings cpuCore]; + return (cores > 0) ? cores : %orig; } - (NSOperatingSystemVersion)operatingSystemVersion { NSOperatingSystemVersion version = %orig; - __block NSString *versionString = @""; - tryGetVal(@"osVersion", ^(id parameter) { - versionString = parameter; - }); + NSString *versionString = [XHookSettings osVersion]; if (!versionString || ![versionString length]) { return version; @@ -291,7 +151,6 @@ const char* str2chars(NSString *str) { return version; } - // 验证每个组件是否为有效数字 for (NSString *component in components) { if (![[NSScanner scannerWithString:component] scanInteger:NULL]) { return version; @@ -302,170 +161,144 @@ const char* str2chars(NSString *str) { version.minorVersion = components.count > 1 ? [components[1] integerValue] : 0; version.patchVersion = components.count > 2 ? [components[2] integerValue] : 0; - return version; + return version; } - %end +#pragma mark - UIWindow Hook + %hook UIWindow -- (UIEdgeInsets) safeAreaInsets { - // NSLog(@"start hook safeAreaInsets"); - __block UIEdgeInsets originalInsets = %orig; - %log(@"Original safeAreaInsets: %@", NSStringFromUIEdgeInsets(originalInsets)); + +- (UIEdgeInsets)safeAreaInsets { + UIEdgeInsets originalInsets = %orig; + NSDictionary *dic = [XHookSettings safeAreaInsets]; - tryGetVal(@"safeAreaInsets", ^(id parameter) { - NSDictionary *dic = parameter; - // 修改 safeAreaInsets - UIEdgeInsets modifiedInsets = UIEdgeInsetsMake( - originalInsets.top = str2double(dic[@"top"]), // 增加顶部安全区域 - originalInsets.left = 0, // 增加左侧安全区域 - originalInsets.bottom = str2double(dic[@"bottom"]), // 增加底部安全区域 - originalInsets.right = 0 // 增加右侧安全区域 - ); - originalInsets = modifiedInsets; - }); + if (dic) { + CGFloat top = [dic[@"top"] doubleValue]; + CGFloat bottom = [dic[@"bottom"] doubleValue]; + originalInsets = UIEdgeInsetsMake(top, 0, bottom, 0); + } - %log(@"Modified safeAreaInsets: %@", NSStringFromUIEdgeInsets(originalInsets)); return originalInsets; } + %end +#pragma mark - UIScreen Hook + %hook UIScreen -- (CGFloat) brightness { - // 随机亮度 - // NSLog(@"start hook brightness"); - __block CGFloat originalBrightness = %orig; - tryGetVal(@"screenBrightness", ^(id parameter) { - double fat = str2double(parameter); - if(fat > -1.0f) { - originalBrightness = fat; - } - }); - - return originalBrightness; + +- (CGFloat)brightness { + CGFloat brightness = [XHookSettings screenBrightness]; + return (brightness > -1.0) ? brightness : %orig; } + - (CGRect)bounds { - // NSLog(@"start hook bounds"); - __block CGRect originalBounds = %orig; - tryGetVal(@"screenBounds", ^(id parameter) { - NSDictionary *dic = parameter; - double w = str2double(dic[@"x"]); - double h = str2double(dic[@"y"]); - CGRect newBounds = { - .origin = {.x = 0, .y = 0}, - .size = {.width = w, .height = h}, - }; - originalBounds = newBounds; - }); - return originalBounds; + NSDictionary *dic = [XHookSettings screenBounds]; + if (dic) { + double w = [dic[@"x"] doubleValue]; + double h = [dic[@"y"] doubleValue]; + return CGRectMake(0, 0, w, h); + } + return %orig; } -- (CGFloat) scale { - // NSLog(@"start hook scale"); - __block CGFloat originalScale = %orig; - tryGetVal(@"screenScale", ^(id parameter) { - double fat = str2double(parameter); - if(fat > -1.0f) { - originalScale = fat; - } - }); - return originalScale; + +- (CGFloat)scale { + CGFloat scale = [XHookSettings screenScale]; + return (scale > -1.0) ? scale : %orig; } + - (CGRect)nativeBounds { - // NSLog(@"start hook nativeBounds"); - __block CGRect originalBounds = %orig; - tryGetVal(@"screenBounds", ^(id parameter) { - NSDictionary *dic = parameter; - double w = str2double(dic[@"nx"]); - double h = str2double(dic[@"ny"]); - CGRect newBounds = { - .origin = {.x = 0, .y = 0}, - .size = {.width = w, .height = h}, - }; - originalBounds = newBounds; - }); - return originalBounds; + NSDictionary *dic = [XHookSettings screenBounds]; + if (dic) { + double w = [dic[@"nx"] doubleValue]; + double h = [dic[@"ny"] doubleValue]; + return CGRectMake(0, 0, w, h); + } + return %orig; } - (BOOL)isCaptured { - // 总是返回NO,表示屏幕没有被捕获(镜像) return NO; } - (UIScreen *)mirroredScreen { - // 总是返回nil,表示没有镜像屏幕 return nil; } %end -%hook AVAudioSession -- (float) outputVolume { - // 音量 - // NSLog(@"start hook outputVolume"); - __block float originalVolume = %orig; - tryGetVal(@"outputVolume", ^(id parameter) { - float fat = str2float(parameter); - if(fat > -1.0f) { - originalVolume = fat; - } - }); - return originalVolume; -} -%end +#pragma mark - UIScreenMode Hook %hook UIScreenMode - (CGSize)size { - // 修改屏幕大小信息 - // NSLog(@"start hook bounds"); - __block CGSize originalSize = %orig; - tryGetVal(@"screenBounds", ^(id parameter) { - NSDictionary *dic = parameter; - double w = str2double(dic[@"x"]); - double h = str2double(dic[@"y"]); - CGSize newBounds = { - .width = w, - .height = h - }; - originalSize = newBounds; - }); - return originalSize; + NSDictionary *dic = [XHookSettings screenBounds]; + if (dic) { + double w = [dic[@"x"] doubleValue]; + double h = [dic[@"y"] doubleValue]; + return CGSizeMake(w, h); + } + return %orig; } %end +#pragma mark - UIStatusBarManager Hook + +%hook UIStatusBarManager + +- (CGRect)statusBarFrame { + CGRect originalFrame = %orig; + NSDictionary *dic = [XHookSettings safeAreaInsets]; + if (dic && dic[@"height"]) { + double barHeight = [dic[@"height"] doubleValue]; + originalFrame.size.height = barHeight; + } + return originalFrame; +} + +%end + +#pragma mark - AVAudioSession Hook + +%hook AVAudioSession + +- (float)outputVolume { + float volume = [XHookSettings outputVolume]; + return (volume > -1.0f) ? volume : %orig; +} + +%end + +#pragma mark - NSFileManager Hook + %hook NSFileManager -- (NSDictionary *) attributesOfFileSystemForPath:(NSString *)path error:(NSError **)error { - // 磁盘信息 - // NSLog(@"start hook disk info"); + +- (NSDictionary *)attributesOfFileSystemForPath:(NSString *)path error:(NSError **)error { NSDictionary *res = %orig; - // int64_t size = [info[NSFileSystemSize] longLongValue]; // 总大小 - // int64_t space = [[info objectForKey:NSFileSystemFreeSize] longLongValue]; + if ([path isEqualToString:NSHomeDirectory()]) { NSMutableDictionary *modifiedAttributes = [res mutableCopy]; - // 修改或添加属性 - tryGetVal(@"diskSize", ^(id parameter) { - int64_t fat = str2ull(parameter); - if(fat > 0) { - modifiedAttributes[NSFileSystemSize] = @(fat); // 1 GB - } - }); - tryGetVal(@"diskFreeSize", ^(id parameter) { - int64_t fat = str2ull(parameter); - if(fat > 0) { - modifiedAttributes[NSFileSystemFreeSize] = @(fat); // 1 GB - } - }); - + + unsigned long long diskSize = [XHookSettings diskSize]; + if (diskSize > 0) { + modifiedAttributes[NSFileSystemSize] = @(diskSize); + } + + unsigned long long diskFreeSize = [XHookSettings diskFreeSize]; + if (diskFreeSize > 0) { + modifiedAttributes[NSFileSystemFreeSize] = @(diskFreeSize); + } + return [modifiedAttributes copy]; } return res; } -// 反越狱检测 + - (BOOL)fileExistsAtPath:(NSString *)event { - // NSLog(@"start hook fileExistsAtPath"); NSArray *array = @[@"/Application/Cydia.app", @"/Application/Sileo.app", @"/usr/lib/TweakInject", @@ -515,296 +348,237 @@ const char* str2chars(NSString *str) { return ret; }]; if (index != NSNotFound) { - // NSLog(@"9999999==%@=",event); return NO; } - return %orig; + return %orig; } + %end +#pragma mark - NSLocale Hook + %hook NSLocale -+ (NSArray *) preferredLanguages { - // 语言 - // NSLog(@"start hook preferredLanguages"); + ++ (NSArray *)preferredLanguages { return @[@"en-US"]; } + + (NSLocale *)currentLocale { - // 如果你想修改返回的 locale,可以在这里进行修改 - // 例如,强制返回一个特定的 locale: - // NSLog(@"start hook currentLocale"); - __block NSLocale *res = %orig; - tryGetVal(@"locale", ^(id parameter) { - NSString *loc = parameter; - res = [[NSLocale alloc] initWithLocaleIdentifier:loc]; - }); - return res; + NSString *loc = [XHookSettings locale]; + if (loc) { + return [[NSLocale alloc] initWithLocaleIdentifier:loc]; + } + return %orig; } + + (NSLocale *)systemLocale { - // NSLog(@"start hook systemLocale"); - __block NSLocale *res = %orig; - tryGetVal(@"locale", ^(id parameter) { - NSString *loc = parameter; - res = [[NSLocale alloc] initWithLocaleIdentifier:loc]; - }); - return res; + NSString *loc = [XHookSettings locale]; + if (loc) { + return [[NSLocale alloc] initWithLocaleIdentifier:loc]; + } + return %orig; } %end +#pragma mark - NSTimeZone Hook + %hook NSTimeZone - + (NSTimeZone *)localTimeZone { - // NSTimeZone *originalTimeZone = %orig; - // // NSLog(@"[HOOK] Local time zone accessed: %@", [originalTimeZone name]); - - // 如果你想修改返回的时区,可以在这里进行修改 - // 例如,强制返回一个特定的时区: - // NSLog(@"start hook localTimeZone"); - __block NSTimeZone *res = %orig; - tryGetVal(@"timeZone", ^(id parameter) { - NSString *loc = parameter; - res = [NSTimeZone timeZoneWithName:loc]; - }); - return res; - // return originalTimeZone; + NSString *tz = [XHookSettings timeZone]; + if (tz) { + return [NSTimeZone timeZoneWithName:tz]; + } + return %orig; } + (NSTimeZone *)systemTimeZone { - // NSLog(@"start hook systemTimeZone"); - __block NSTimeZone *res = %orig; - tryGetVal(@"timeZone", ^(id parameter) { - NSString *loc = parameter; - res = [NSTimeZone timeZoneWithName:loc]; - }); - return res; + NSString *tz = [XHookSettings timeZone]; + if (tz) { + return [NSTimeZone timeZoneWithName:tz]; + } + return %orig; } + (NSTimeZone *)defaultTimeZone { - // NSLog(@"start hook defaultTimeZone"); - __block NSTimeZone *res = %orig; - tryGetVal(@"timeZone", ^(id parameter) { - NSString *loc = parameter; - res = [NSTimeZone timeZoneWithName:loc]; - }); - return res; + NSString *tz = [XHookSettings timeZone]; + if (tz) { + return [NSTimeZone timeZoneWithName:tz]; + } + return %orig; } - %end -// 状态栏 -%hook UIStatusBarManager -- (CGRect) statusBarFrame { - // NSLog(@"start hook statusBarFrame"); - __block CGRect originalFrame = %orig; - tryGetVal(@"safeAreaInsets", ^(id parameter) { - NSDictionary *dic = parameter; - double barHeight = str2double(dic[@"height"]); - CGSize tempSize = {.width = originalFrame.size.width, .height = barHeight}; - originalFrame.size = tempSize; - }); - return originalFrame; -} -%end +#pragma mark - NSMutableURLRequest Hook %hook NSMutableURLRequest -// Maybe server side checks the user agent? + - (instancetype)initWithURL:(NSURL *)URL { self = %orig(URL); if (self) { - tryGetVal(@"userAgent", ^(id parameter) { - [self setValue:parameter forHTTPHeaderField:@"User-Agent"]; - }); + NSString *userAgent = [XHookSettings userAgent]; + if (userAgent) { + [self setValue:userAgent forHTTPHeaderField:@"User-Agent"]; + } } return self; } - (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field { - __block NSString *_userAgent = value; + NSString *finalValue = value; if ([field isEqualToString:@"User-Agent"]) { - // 修改User Agent - tryGetVal(@"userAgent", ^(id parameter) { - _userAgent = parameter; - }); + NSString *userAgent = [XHookSettings userAgent]; + if (userAgent) { + finalValue = userAgent; + } } - %orig(_userAgent, field); + %orig(finalValue, field); } %end +#pragma mark - NSURLSession Hook + %hook NSURLSession + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration { - // NSLog(@"start hook sessionWithConfiguration"); - //if (configuration.HTTPAdditionalHeaders[@"User-Agent"]) { - tryGetVal(@"userAgent", ^(id parameter) { - configuration.HTTPAdditionalHeaders = @{@"User-Agent": parameter}; - }); - //} + NSString *userAgent = [XHookSettings userAgent]; + if (userAgent) { + configuration.HTTPAdditionalHeaders = @{@"User-Agent": userAgent}; + } return %orig; } - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler { NSMutableURLRequest *mutableRequest = [request mutableCopy]; - tryGetVal(@"userAgent", ^(id parameter) { - [mutableRequest setValue:parameter forHTTPHeaderField:@"User-Agent"]; - }); - + NSString *userAgent = [XHookSettings userAgent]; + if (userAgent) { + [mutableRequest setValue:userAgent forHTTPHeaderField:@"User-Agent"]; + } return %orig(mutableRequest, completionHandler); } %end +#pragma mark - WKWebView Hook + %hook WKWebView - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *error))completionHandler { - // NSLog(@"start hook evaluateJavaScript"); - %log(@"Evaluating JavaScript: %@", javaScriptString); - __block NSString *userAgent = nil; - tryGetVal(@"userAgent", ^(id parameter) { - userAgent = parameter; - }); - if(userAgent) { - [self setCustomUserAgent:userAgent]; + NSString *userAgent = [XHookSettings userAgent]; + if (userAgent) { + [self setCustomUserAgent:userAgent]; } %orig(javaScriptString, completionHandler); } - (void)setCustomUserAgent:(NSString *)userAgent { - __block NSString *_userAgent = userAgent; - tryGetVal(@"userAgent", ^(id parameter) { - _userAgent = parameter; - }); - %orig(_userAgent); + NSString *customUA = [XHookSettings userAgent]; + %orig(customUA ?: userAgent); } - (NSString *)customUserAgent { - // 获取原来的 User-Agent - __block NSString *userAgent = %orig; - - tryGetVal(@"userAgent", ^(id parameter) { - userAgent = parameter; - }); - - // 返回修改后的 User-Agent - return userAgent; + NSString *userAgent = [XHookSettings userAgent]; + return userAgent ?: %orig; } %end - +#pragma mark - CTTelephonyNetworkInfo Hook %hook CTTelephonyNetworkInfo -- (NSDictionary *) serviceCurrentRadioAccessTechnology { - // NSLog(@"start hook serviceCurrentRadioAccessTechnology"); - NSDictionary *originalTechnologies = %orig; - %log(@"Original Radio Access Technologies: %@", originalTechnologies); - NSMutableDictionary *modifiedProviders = [[NSMutableDictionary alloc] init]; - - tryGetVal(@"simInfo", ^(id parameter) { - NSDictionary *simInfoDic = parameter; - for(NSString *service in simInfoDic.allKeys) { - NSDictionary *dic = simInfoDic[service]; - NSString *wwan = dic[@"wwan"]; - if([wwan isEqual:@"5G"]) { - [modifiedProviders setValue:@"CTRadioAccessTechnologyNR" forKey:service]; - } else { - [modifiedProviders setValue:@"CTRadioAccessTechnologyLTE" forKey:service]; - } - } - }); - - if([modifiedProviders count] > 0) { - return [modifiedProviders copy]; +- (NSDictionary *)serviceCurrentRadioAccessTechnology { + NSDictionary *originalTechnologies = %orig; + NSDictionary *simInfoDic = [XHookSettings simInfo]; + + if (!simInfoDic) { + return originalTechnologies; } - return originalTechnologies; + + NSMutableDictionary *modifiedProviders = [[NSMutableDictionary alloc] init]; + + for (NSString *service in simInfoDic.allKeys) { + NSDictionary *dic = simInfoDic[service]; + NSString *wwan = dic[@"wwan"]; + if ([wwan isEqual:@"5G"]) { + modifiedProviders[service] = @"CTRadioAccessTechnologyNR"; + } else { + modifiedProviders[service] = @"CTRadioAccessTechnologyLTE"; + } + } + + return [modifiedProviders count] > 0 ? [modifiedProviders copy] : originalTechnologies; } + - (NSDictionary *)serviceSubscriberCellularProviders { - // NSLog(@"start hook serviceSubscriberCellularProviders"); NSDictionary *originalProviders = %orig; - %log(@"Original Cellular Providers: %@", originalProviders); - - NSMutableDictionary *modifiedProviders = [[NSMutableDictionary alloc] init]; - - tryGetVal(@"simInfo", ^(id parameter) { - NSDictionary *simInfoDic = parameter; - for(NSString *service in simInfoDic.allKeys) { - NSDictionary *dic = simInfoDic[service]; - if (!dic) { - continue; - } - if (![dic[@"id"] isEqual:[NSNull null]]) { - [[NSUserDefaults standardUserDefaults] setValue:dic[@"id"] forKey:@"lux-carrier-id"]; - } - CTCarrier *carrier = [[CTCarrier alloc] init]; - [carrier setValue:dic[@"carrierName"] forKey:@"carrierName"]; - [carrier setValue:dic[@"mobileCountryCode"] forKey:@"mobileCountryCode"]; - [carrier setValue:dic[@"mobileNetworkCode"] forKey:@"mobileNetworkCode"]; - [carrier setValue:dic[@"isoCountryCode"] forKey:@"isoCountryCode"]; - [modifiedProviders setValue:carrier forKey:service]; - } - }); - - if([modifiedProviders count] > 0) { - return [modifiedProviders copy]; + NSDictionary *simInfoDic = [XHookSettings simInfo]; + + if (!simInfoDic) { + return originalProviders; } - return originalProviders; + + NSMutableDictionary *modifiedProviders = [[NSMutableDictionary alloc] init]; + + for (NSString *service in simInfoDic.allKeys) { + NSDictionary *dic = simInfoDic[service]; + if (!dic) continue; + + if (dic[@"id"] && ![dic[@"id"] isEqual:[NSNull null]]) { + [[NSUserDefaults standardUserDefaults] setValue:dic[@"id"] forKey:@"XHook-carrier-id"]; + } + + CTCarrier *carrier = [[CTCarrier alloc] init]; + [carrier setValue:dic[@"carrierName"] forKey:@"carrierName"]; + [carrier setValue:dic[@"mobileCountryCode"] forKey:@"mobileCountryCode"]; + [carrier setValue:dic[@"mobileNetworkCode"] forKey:@"mobileNetworkCode"]; + [carrier setValue:dic[@"isoCountryCode"] forKey:@"isoCountryCode"]; + modifiedProviders[service] = carrier; + } + + return [modifiedProviders count] > 0 ? [modifiedProviders copy] : originalProviders; } + %end -// 添加随机函数 +#pragma mark - CMMotionManager Hook (陀螺仪) + static double randomRadiansPerSecond() { double randomValue = (double)arc4random() / UINT32_MAX; return (randomValue * 20.0) - 10.0; } -// 创建 CMGyroData 的子类 + @interface CustomCMGyroData : CMGyroData @property (nonatomic, assign) CMRotationRate customRotationRate; @end @implementation CustomCMGyroData - - (CMRotationRate)rotationRate { return self.customRotationRate; } - @end - - -// 陀螺仪 %hook CMMotionManager - (void)startGyroUpdatesToQueue:(NSOperationQueue *)queue withHandler:(CMGyroHandler)handler { if (!queue || !handler) return %orig; + CMGyroHandler newHandler = ^(CMGyroData *gyroData, NSError *error) { + CMRotationRate randomRate; + randomRate.x = randomRadiansPerSecond(); + randomRate.y = randomRadiansPerSecond(); + randomRate.z = randomRadiansPerSecond(); - if(gyroData) { - CMRotationRate originalRate = gyroData.rotationRate; - - // 在这里,你可以修改或记录 rotationRate - // NSLog(@"Gyro RotationRate - x: %f, y: %f, z: %f", - // originalRate.x, originalRate.y, originalRate.z); + CustomCMGyroData *customGyroData = (CustomCMGyroData *)[[objc_getClass("CMGyroData") alloc] init]; + customGyroData.customRotationRate = randomRate; + + if (handler) { + handler((CMGyroData *)customGyroData, error); } - // 设置随机的旋转速率 - CMRotationRate randomRate; - randomRate.x = randomRadiansPerSecond(); - randomRate.y = randomRadiansPerSecond(); - randomRate.z = randomRadiansPerSecond(); - - // 如果你想修改值,可以这样做: - // 创建自定义的 CMGyroData 对象 - CustomCMGyroData *customGyroData = (CustomCMGyroData *)[[objc_getClass("CMGyroData") alloc] init]; - // Objective-C 没有直接的方法来修改 CMGyroData 的值,所以这里我们只是记录 - // 设置自定义数据 - customGyroData.customRotationRate = randomRate; - // 调用原始处理程序,但使用我们的自定义数据 - if (handler) { - handler((CMGyroData *)customGyroData, error); - } }; %orig(queue, newHandler); @@ -812,180 +586,114 @@ static double randomRadiansPerSecond() { %end +#pragma mark - C Function Hooks -// int uname(struct utsname *); -%hookf(int, uname, struct utsname * systemInfo) { - // NSLog(@"start hook uname"); - int nRet = %orig; - tryGetVal(@"productStr", ^(id parameter) { +%hookf(int, uname, struct utsname *systemInfo) { + int nRet = %orig; + NSString *productStr = [XHookSettings productStr]; + if (productStr) { char str_machine_name[100]; - [parameter getCString:str_machine_name maxLength:100 encoding:NSUTF8StringEncoding]; - strcpy(systemInfo->machine,str_machine_name); - }); - return nRet; // Call the original implementation of this function - + [productStr getCString:str_machine_name maxLength:100 encoding:NSUTF8StringEncoding]; + strcpy(systemInfo->machine, str_machine_name); + } + return nRet; } %hookf(int, sysctl, const int *name, u_int namelen, void *oldp, size_t *oldlenp, const void *newp, size_t newlen) { - // NSLog(@"start hook sysctl"); - - if(namelen < 2) { + if (namelen < 2) { return %orig; } if (name[0] == CTL_KERN && name[1] == KERN_BOOTTIME) { int nRet = %orig; if (nRet == 0 && oldp && oldlenp && *oldlenp >= sizeof(struct timeval)) { - struct timeval *a = (struct timeval*)oldp; - tryGetVal(@"kernBootTime", ^(id parameter) { - int kernBootTime = str2int(parameter); - a->tv_sec = kernBootTime + a->tv_sec; - }); + struct timeval *a = (struct timeval *)oldp; + NSInteger kernBootTimeOffset = [XHookSettings kernBootTimeOffset]; + if (kernBootTimeOffset != 0) { + a->tv_sec += kernBootTimeOffset; + } } return nRet; - } - else if(name[0] == CTL_HW && name[1] == HW_MACHINE) { - __block NSString *modelName = @"iPhone14,2"; - tryGetVal(@"productStr", ^(id parameter) { - if(parameter) { - modelName = parameter; - } - }); - + } + else if (name[0] == CTL_HW && name[1] == HW_MACHINE) { + NSString *modelName = [XHookSettings productStr]; const char *spoofedModel = [modelName UTF8String]; if (!spoofedModel) { return %orig; } - size_t modelLen = strlen(spoofedModel) + 1; // +1 for null terminator + size_t modelLen = strlen(spoofedModel) + 1; - // 查询缓冲区大小 if (!oldp && oldlenp) { *oldlenp = modelLen; return 0; } - // 复制数据 if (oldp && oldlenp) { if (*oldlenp < modelLen) { - // 缓冲区太小,返回错误 return ENOMEM; } - - // 安全复制字符串 - strlcpy((char*)oldp, spoofedModel, *oldlenp); + strlcpy((char *)oldp, spoofedModel, *oldlenp); *oldlenp = modelLen; - // NSLog(@"sysctl model copied: %@", modelName); return 0; } return EINVAL; } - // 其他情况调用原函数 return %orig; } -// 安全的字符串复制函数 -static BOOL safe_copy_string(void *dst, size_t *dstlen, const char *src) { - if (!dst || !dstlen || !src) return NO; - - size_t srclen = strlen(src); - size_t needed = srclen + 1; - - if (*dstlen < needed) { - *dstlen = needed; - return NO; - } - - strlcpy(dst, src, *dstlen); - *dstlen = srclen; - return YES; -} - %hookf(int, sysctlbyname, const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - // NSLog(@"start hook sysctlbyname"); - if (strcmp(name, "hw.machine") == 0) { - // NSLog(@"start hook sysctlbyname new machine"); + if (strcmp(name, "hw.machine") == 0) { int ret = %orig; - __block NSString *machine = nil; - tryGetVal(@"productStr", ^(id parameter) { - if (parameter) { - NSString *temp = parameter; - machine = [temp copy]; - // NSLog(@"machine,%@", temp); - } - }); + NSString *machine = [XHookSettings productStr]; if (oldp && machine) { - // NSLog(@"machine,1"); - // char str_machine_name[100]; const char *hwMachineCh = [machine UTF8String]; - // [parameter getCString:str_machine_name maxLength:100 encoding:NSUTF8StringEncoding]; - //strcpy((char *)oldp, hwMachineCh); if (hwMachineCh) { - - // NSLog(@"machine,2,%@", machine); - size_t len = strlen(hwMachineCh); + size_t len = strlen(hwMachineCh); if (*oldlenp > len) { - strlcpy((char *)oldp, hwMachineCh, *oldlenp); // 使用安全的字符串复制 + strlcpy((char *)oldp, hwMachineCh, *oldlenp); *oldlenp = len; } - //strncpy((char *)oldp, hwMachineCh, strlen(hwMachineCh)); } } return ret; - } else if (strcmp(name, "hw.model") == 0) { - // NSLog(@"start hook sysctlbyname new model"); + } else if (strcmp(name, "hw.model") == 0) { int ret = %orig; - __block NSString *modelStr = nil; - tryGetVal(@"hwModel", ^(id parameter) { - if (parameter) { - NSString *temp = parameter; - modelStr = [temp copy]; - // NSLog(@"model,%@", temp); - } - }); + NSString *modelStr = [XHookSettings hwModel]; if (oldp && modelStr) { - // NSLog(@"model,1"); - // char str_machine_name[100]; const char *modelStrCh = [modelStr UTF8String]; - // [parameter getCString:str_machine_name maxLength:100 encoding:NSUTF8StringEncoding]; - //strcpy((char *)oldp, hwMachineCh); if (modelStrCh) { - // NSLog(@"model,2"); - strncpy((char *)oldp, modelStrCh, strlen(modelStrCh)); + size_t len = strlen(modelStrCh); + if (*oldlenp > len) { + strlcpy((char *)oldp, modelStrCh, *oldlenp); + *oldlenp = len; + } } } return ret; - } else { - // NSLog(@"start hook sysctlbyname old"); - return %orig; } + return %orig; } -// int clock_gettime(clockid_t __clock_id, struct timespec *__tp); %hookf(int, clock_gettime, clockid_t __clock_id, struct timespec *__tp) { - // NSLog(@"start hook clock_gettime"); int nret = %orig; if(__clock_id == CLOCK_MONOTONIC_RAW) { - // __tp->tv_sec = 20; - // __tp->tv_nsec = 20; + // Reserved } return nret; } -// 修改ip +#pragma mark - Network Interface Helpers -// 辅助函数:创建网络接口 static struct ifaddrs* createInterface(const char* name, const char* ipv4, const char* ipv6) { if (!name || !ipv4 || !ipv6) return NULL; @try { - // 创建新接口 struct ifaddrs *new_ifaddr = (struct ifaddrs *)calloc(1, sizeof(struct ifaddrs)); if (!new_ifaddr) return NULL; - // 设置接口名称 new_ifaddr->ifa_name = strdup(name); if (!new_ifaddr->ifa_name) { free(new_ifaddr); @@ -993,7 +701,6 @@ static struct ifaddrs* createInterface(const char* name, const char* ipv4, const } new_ifaddr->ifa_flags = IFF_UP | IFF_RUNNING; - // 创建IPv4地址结构 struct sockaddr_in *addr_in = (struct sockaddr_in *)calloc(1, sizeof(struct sockaddr_in)); if (!addr_in) { free(new_ifaddr->ifa_name); @@ -1001,7 +708,6 @@ static struct ifaddrs* createInterface(const char* name, const char* ipv4, const return NULL; } - // 设置IPv4地址 addr_in->sin_family = AF_INET; if (inet_pton(AF_INET, ipv4, &addr_in->sin_addr) != 1) { free(addr_in); @@ -1011,7 +717,6 @@ static struct ifaddrs* createInterface(const char* name, const char* ipv4, const } new_ifaddr->ifa_addr = (struct sockaddr *)addr_in; - // 创建IPv6结构 struct ifaddrs *next_ifaddr = (struct ifaddrs *)calloc(1, sizeof(struct ifaddrs)); if (!next_ifaddr) { free(addr_in); @@ -1020,7 +725,6 @@ static struct ifaddrs* createInterface(const char* name, const char* ipv4, const return NULL; } - // 设置IPv6名称 next_ifaddr->ifa_name = strdup(name); if (!next_ifaddr->ifa_name) { free(next_ifaddr); @@ -1031,7 +735,6 @@ static struct ifaddrs* createInterface(const char* name, const char* ipv4, const } next_ifaddr->ifa_flags = IFF_UP | IFF_RUNNING; - // 创建IPv6地址结构 struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)calloc(1, sizeof(struct sockaddr_in6)); if (!addr_in6) { free(next_ifaddr->ifa_name); @@ -1042,7 +745,6 @@ static struct ifaddrs* createInterface(const char* name, const char* ipv4, const return NULL; } - // 设置IPv6地址 addr_in6->sin6_family = AF_INET6; if (inet_pton(AF_INET6, ipv6, &addr_in6->sin6_addr) != 1) { free(addr_in6); @@ -1055,16 +757,13 @@ static struct ifaddrs* createInterface(const char* name, const char* ipv4, const } next_ifaddr->ifa_addr = (struct sockaddr *)addr_in6; - // 链接接口 new_ifaddr->ifa_next = next_ifaddr; return new_ifaddr; } @catch (NSException *exception) { - // NSLog(@"Error creating interface: %@", exception); return NULL; } } -// 修改现有接口的IP地址 static void modifyInterfaceIP(struct ifaddrs *interface, const char *ipv4, const char *ipv6) { if (!interface || !ipv4 || !ipv6) return; @@ -1079,7 +778,6 @@ static void modifyInterfaceIP(struct ifaddrs *interface, const char *ipv4, const } } } @catch (NSException *exception) { - // NSLog(@"Error modifying interface IP: %@", exception); } } @@ -1090,19 +788,11 @@ static void modifyInterfaceIP(struct ifaddrs *interface, const char *ipv4, const if (nRet != 0) return nRet; @try { - // 获取网络配置 - __block NSDictionary *networkConfig = nil; - tryGetVal(@"networkInterfacesInfo", ^(id parameter) { - if ([parameter isKindOfClass:[NSDictionary class]]) { - networkConfig = parameter; - } - }); - + NSDictionary *networkConfig = [XHookSettings networkInterfacesInfo]; if (!networkConfig) { return nRet; } - // 处理现有接口 struct ifaddrs *interface; bool isHookEn0IpV4 = false; @@ -1123,7 +813,6 @@ static void modifyInterfaceIP(struct ifaddrs *interface, const char *ipv4, const } } - // 添加新接口 if (!isHookEn0IpV4) { NSDictionary *en0Config = networkConfig[@"en0"]; if (en0Config) { @@ -1137,7 +826,6 @@ static void modifyInterfaceIP(struct ifaddrs *interface, const char *ipv4, const } } - // 添加pdp_ip0接口 NSDictionary *pdpConfig = networkConfig[@"pdp_ip0"]; if (pdpConfig) { const char *ipv4 = [pdpConfig[@"ipv4"] UTF8String]; @@ -1150,28 +838,15 @@ static void modifyInterfaceIP(struct ifaddrs *interface, const char *ipv4, const } } @catch (NSException *exception) { - // NSLog(@"Error in getifaddrs hook: %@", exception); } return nRet; } - - -/* Boolean -SCNetworkReachabilityGetFlags ( - SCNetworkReachabilityRef target, - SCNetworkReachabilityFlags *flags - ) -*/ - %hookf(Boolean, SCNetworkReachabilityGetFlags, SCNetworkReachabilityRef target, SCNetworkReachabilityFlags *flags) { - // NSLog(@"start hook SCNetworkReachabilityGetFlags"); Boolean result = %orig(target, flags); if (result && flags != NULL) { - // 可以在这里修改 flags 的值 - // 例如,总是添加 kSCNetworkReachabilityFlagsReachable 标志 *flags |= kSCNetworkReachabilityFlagsReachable; *flags |= kSCNetworkReachabilityFlagsIsWWAN; } @@ -1179,73 +854,50 @@ SCNetworkReachabilityGetFlags ( return result; } - -%hookf(BOOL,UIAccessibilityIsClosedCaptioningEnabled) { +%hookf(BOOL, UIAccessibilityIsClosedCaptioningEnabled) { return YES; } - - -%hookf(BOOL,UIAccessibilityIsMonoAudioEnabled) { +%hookf(BOOL, UIAccessibilityIsMonoAudioEnabled) { return YES; } +%end // end of %group XHooks -// group ohho end -%end +#pragma mark - ============== CONSTRUCTOR & DESTRUCTOR ============== - - - -%ctor{ - NSLog(@"start hook"); +%ctor { + NSLog(@"XHook: Starting..."); @try { - @autoreleasepool { - NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; - //// NSLog(@"print -- %@",bundleIdentifier); - NSString *jsonPath = @"/var/mobile/Documents/changeapp.json"; - // 创建文件管理器实例 - NSFileManager *fileManager = [NSFileManager defaultManager]; + @autoreleasepool { + NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; + NSString *jsonPath = @"/var/mobile/Documents/changeapp.json"; - // 检查文件是否存在 - BOOL fileExists = [fileManager fileExistsAtPath:jsonPath]; - if(fileExists) { - NSData *jsonData = [NSData dataWithContentsOfFile:jsonPath]; - if (jsonData) { - NSError *error = nil; - settings = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error]; - if (error) { - NSLog(@"Error parsing JSON: %@", error); - return; - } + BOOL loaded = [XHookSettings loadSettingsFromPath:jsonPath]; + if (!loaded) { + NSLog(@"XHook: Failed to load settings from %@", jsonPath); + return; } - NSArray *pkgs = settings[@"apps"]; - - if([pkgs indexOfObject:bundleIdentifier] != NSNotFound){ - NSString *idfa = settings[@"idfa"]; - // - SetupXSLog(); - NSLog(@"hooks start %@; idfa:%@", bundleIdentifier,idfa); - // 指定需要运行的执行的分组代码,%init有两种用法,这里只展示一种 - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"kLuxSSFaceKey"]; + + if ([XHookSettings shouldHookApp:bundleIdentifier]) { + NSUUID *idfa = [XHookSettings idfa]; - %init(ohno); + SetupXSLog(); + NSLog(@"XHook: Hooks started for %@; IDFA: %@", bundleIdentifier, idfa); + + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"kXHookSSFaceKey"]; + + %init(XHooks); + } else { + NSLog(@"XHook: App %@ not in hook list, skipping", bundleIdentifier); } } + } @catch (NSException *exception) { + NSLog(@"XHook: Error in ctor: %@", exception); } - } @catch (NSException *exception) { - // NSLog(@"Error in ctor: %@", exception); - } -} - -static void cleanup() { - // 清理分配的资源 - if (settings) { - settings = nil; - } - // 其他清理工作 } %dtor { - cleanup(); -} \ No newline at end of file + NSLog(@"XHook: Cleaning up..."); + [XHookSettings cleanup]; +} diff --git a/ios-change-info/XHookSettingsManager.h b/ios-change-info/XHookSettingsManager.h new file mode 100644 index 0000000..839eaf6 --- /dev/null +++ b/ios-change-info/XHookSettingsManager.h @@ -0,0 +1,70 @@ +// +// XHookSettingsManager.h +// 配置管理器 - 头文件 +// + +#import +#import + +@interface XHookSettingsManager : NSObject + +@property (nonatomic, assign, readonly) BOOL isLoaded; + +// 单例 ++ (instancetype)sharedManager; + +// 加载配置 +- (BOOL)loadSettingsFromPath:(NSString *)path; +- (void)cleanup; + +// 设备信息 +@property (nonatomic, strong, readonly) NSUUID *idfa; +@property (nonatomic, strong, readonly) NSUUID *idfv; +@property (nonatomic, copy, readonly) NSString *deviceName; +@property (nonatomic, copy, readonly) NSString *osVersion; +@property (nonatomic, copy, readonly) NSString *productStr; +@property (nonatomic, copy, readonly) NSString *hwModel; +@property (nonatomic, assign, readonly) NSUInteger trackingStatus; + +// 系统信息 +@property (nonatomic, assign, readonly) unsigned long long physicalMemory; +@property (nonatomic, assign, readonly) NSUInteger cpuCore; +@property (nonatomic, assign, readonly) double systemUptimeOffset; +@property (nonatomic, assign, readonly) NSInteger kernBootTimeOffset; + +// 电池信息 +@property (nonatomic, assign, readonly) UIDeviceBatteryState batteryState; +@property (nonatomic, assign, readonly) float batteryLevel; + +// 屏幕信息 +@property (nonatomic, assign, readonly) CGFloat screenBrightness; +@property (nonatomic, assign, readonly) CGFloat screenScale; +@property (nonatomic, strong, readonly) NSDictionary *screenBounds; +@property (nonatomic, strong, readonly) NSDictionary *safeAreaInsets; + +// 磁盘信息 +@property (nonatomic, assign, readonly) unsigned long long diskSize; +@property (nonatomic, assign, readonly) unsigned long long diskFreeSize; + +// 音频 +@property (nonatomic, assign, readonly) float outputVolume; + +// 网络信息 +@property (nonatomic, copy, readonly) NSString *userAgent; +@property (nonatomic, strong, readonly) NSDictionary *networkInterfacesInfo; +@property (nonatomic, strong, readonly) NSDictionary *simInfo; + +// 本地化 +@property (nonatomic, copy, readonly) NSString *locale; +@property (nonatomic, copy, readonly) NSString *timeZone; + +// 应用列表 +@property (nonatomic, strong, readonly) NSArray *apps; + +- (BOOL)shouldHookApp:(NSString *)bundleIdentifier; + +@end + +// 便捷宏 +#define XHookSettings [XHookSettingsManager sharedManager] + diff --git a/ios-change-info/XHookSettingsManager.m b/ios-change-info/XHookSettingsManager.m new file mode 100644 index 0000000..e7985f5 --- /dev/null +++ b/ios-change-info/XHookSettingsManager.m @@ -0,0 +1,232 @@ +// +// XHookSettingsManager.m +// 配置管理器 - 实现 +// + +#import "XHookSettingsManager.h" + +@implementation XHookSettingsManager + ++ (instancetype)sharedManager { + static XHookSettingsManager *manager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + manager = [[XHookSettingsManager alloc] init]; + }); + return manager; +} + +- (instancetype)init { + self = [super init]; + if (self) { + [self resetToDefaults]; + } + return self; +} + +- (void)resetToDefaults { + _isLoaded = NO; + + // 设备信息默认值 + _idfa = nil; + _idfv = nil; + _deviceName = nil; + _osVersion = nil; + _productStr = @"iPhone14,2"; + _hwModel = nil; + _trackingStatus = 3; // 默认已授权 + + // 系统信息默认值 + _physicalMemory = 0; + _cpuCore = 0; + _systemUptimeOffset = -1.0; + _kernBootTimeOffset = 0; + + // 电池信息默认值 + _batteryState = UIDeviceBatteryStateUnknown; + _batteryLevel = -1.0f; + + // 屏幕信息默认值 + _screenBrightness = -1.0; + _screenScale = -1.0; + _screenBounds = nil; + _safeAreaInsets = nil; + + // 磁盘信息默认值 + _diskSize = 0; + _diskFreeSize = 0; + + // 音频默认值 + _outputVolume = -1.0f; + + // 网络信息默认值 + _userAgent = nil; + _networkInterfacesInfo = nil; + _simInfo = nil; + + // 本地化默认值 + _locale = nil; + _timeZone = nil; + + // 应用列表默认值 + _apps = nil; +} + +#pragma mark - 解析辅助方法 + +- (NSString *)parseString:(id)value defaultValue:(NSString *)defaultValue { + if ([value isKindOfClass:[NSString class]]) { + return value; + } + return defaultValue; +} + +- (NSInteger)parseInteger:(id)value defaultValue:(NSInteger)defaultValue { + if ([value isKindOfClass:[NSString class]]) { + return [value integerValue]; + } else if ([value isKindOfClass:[NSNumber class]]) { + return [value integerValue]; + } + return defaultValue; +} + +- (float)parseFloat:(id)value defaultValue:(float)defaultValue { + if ([value isKindOfClass:[NSString class]]) { + return [value floatValue]; + } else if ([value isKindOfClass:[NSNumber class]]) { + return [value floatValue]; + } + return defaultValue; +} + +- (double)parseDouble:(id)value defaultValue:(double)defaultValue { + if ([value isKindOfClass:[NSString class]]) { + return [value doubleValue]; + } else if ([value isKindOfClass:[NSNumber class]]) { + return [value doubleValue]; + } + return defaultValue; +} + +- (unsigned long long)parseULL:(id)value defaultValue:(unsigned long long)defaultValue { + if ([value isKindOfClass:[NSString class]]) { + return [value longLongValue]; + } else if ([value isKindOfClass:[NSNumber class]]) { + return [value unsignedLongLongValue]; + } + return defaultValue; +} + +- (NSUUID *)parseUUID:(id)value { + if ([value isKindOfClass:[NSString class]]) { + return [[NSUUID alloc] initWithUUIDString:value]; + } + return nil; +} + +- (NSDictionary *)parseDictionary:(id)value { + if ([value isKindOfClass:[NSDictionary class]]) { + return value; + } + return nil; +} + +- (NSArray *)parseArray:(id)value { + if ([value isKindOfClass:[NSArray class]]) { + return value; + } + return nil; +} + +#pragma mark - 加载配置 + +- (BOOL)loadSettingsFromPath:(NSString *)path { + @try { + NSFileManager *fileManager = [NSFileManager defaultManager]; + if (![fileManager fileExistsAtPath:path]) { + return NO; + } + + NSData *jsonData = [NSData dataWithContentsOfFile:path]; + if (!jsonData) { + return NO; + } + + NSError *error = nil; + NSDictionary *settings = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error]; + if (error || !settings || ![settings isKindOfClass:[NSDictionary class]]) { + NSLog(@"XHookSettingsManager: Error parsing JSON: %@", error); + return NO; + } + + // 解析并初始化所有属性 + [self parseSettings:settings]; + + _isLoaded = YES; + return YES; + } @catch (NSException *exception) { + NSLog(@"XHookSettingsManager: Exception loading settings: %@", exception); + return NO; + } +} + +- (void)parseSettings:(NSDictionary *)settings { + // 设备信息 + _idfa = [self parseUUID:settings[@"idfa"]]; + _idfv = [self parseUUID:settings[@"idfv"]]; + _deviceName = [self parseString:settings[@"deviceName"] defaultValue:nil]; + _osVersion = [self parseString:settings[@"osVersion"] defaultValue:nil]; + _productStr = [self parseString:settings[@"productStr"] defaultValue:@"iPhone14,2"]; + _hwModel = [self parseString:settings[@"hwModel"] defaultValue:nil]; + _trackingStatus = [self parseInteger:settings[@"trackingStatus"] defaultValue:3]; + + // 系统信息 + _physicalMemory = [self parseULL:settings[@"physicalMemory"] defaultValue:0]; + _cpuCore = [self parseInteger:settings[@"cpuCore"] defaultValue:0]; + _systemUptimeOffset = [self parseDouble:settings[@"systemUptime"] defaultValue:-1.0]; + _kernBootTimeOffset = [self parseInteger:settings[@"kernBootTime"] defaultValue:0]; + + // 电池信息 + NSInteger batteryStateVal = [self parseInteger:settings[@"batteryState"] defaultValue:-1]; + _batteryState = (batteryStateVal >= 0) ? (UIDeviceBatteryState)batteryStateVal : UIDeviceBatteryStateUnknown; + _batteryLevel = [self parseFloat:settings[@"batteryLevel"] defaultValue:-1.0f]; + + // 屏幕信息 + _screenBrightness = [self parseDouble:settings[@"screenBrightness"] defaultValue:-1.0]; + _screenScale = [self parseDouble:settings[@"screenScale"] defaultValue:-1.0]; + _screenBounds = [self parseDictionary:settings[@"screenBounds"]]; + _safeAreaInsets = [self parseDictionary:settings[@"safeAreaInsets"]]; + + // 磁盘信息 + _diskSize = [self parseULL:settings[@"diskSize"] defaultValue:0]; + _diskFreeSize = [self parseULL:settings[@"diskFreeSize"] defaultValue:0]; + + // 音频 + _outputVolume = [self parseFloat:settings[@"outputVolume"] defaultValue:-1.0f]; + + // 网络信息 + _userAgent = [self parseString:settings[@"userAgent"] defaultValue:nil]; + _networkInterfacesInfo = [self parseDictionary:settings[@"networkInterfacesInfo"]]; + _simInfo = [self parseDictionary:settings[@"simInfo"]]; + + // 本地化 + _locale = [self parseString:settings[@"locale"] defaultValue:nil]; + _timeZone = [self parseString:settings[@"timeZone"] defaultValue:nil]; + + // 应用列表 + _apps = [self parseArray:settings[@"apps"]]; +} + +- (void)cleanup { + [self resetToDefaults]; +} + +#pragma mark - 应用检查 + +- (BOOL)shouldHookApp:(NSString *)bundleIdentifier { + if (!bundleIdentifier || !_apps) return NO; + return [_apps containsObject:bundleIdentifier]; +} + +@end +