904 lines
26 KiB
Plaintext
904 lines
26 KiB
Plaintext
//
|
|
// Tweak.x
|
|
// 主入口文件 - 包含所有 Hooks
|
|
//
|
|
|
|
#import <Foundation/Foundation.h>
|
|
#import <UIKit/UIKit.h>
|
|
#import <WebKit/WebKit.h>
|
|
#import <CoreMotion/CoreMotion.h>
|
|
#import <CoreTelephony/CTCarrier.h>
|
|
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
|
|
#import <SystemConfiguration/SystemConfiguration.h>
|
|
#import <AppTrackingTransparency/AppTrackingTransparency.h>
|
|
#import <AVFoundation/AVFoundation.h>
|
|
#import <sys/utsname.h>
|
|
#include <sys/sysctl.h>
|
|
#import <ifaddrs.h>
|
|
#import <arpa/inet.h>
|
|
#import <net/if.h>
|
|
#import <objc/runtime.h>
|
|
#import "XSLog.h"
|
|
#import "XHookSettingsManager.h"
|
|
|
|
// 设置日志
|
|
void SetupXSLog() {
|
|
XSLogSetupLogWithFilePath(@"/var/mobile/Documents/App.log", 50 * 1024 * 1024);
|
|
XSLogRedirectNSLog();
|
|
}
|
|
|
|
#pragma mark - ============== HOOKS GROUP ==============
|
|
|
|
%group XHooks
|
|
|
|
#pragma mark - ATTrackingManager Hook
|
|
|
|
%hook ATTrackingManager
|
|
|
|
+ (ATTrackingManagerAuthorizationStatus)trackingAuthorizationStatus {
|
|
ATTrackingManagerAuthorizationStatus originalStatus = %orig;
|
|
NSUInteger status = [XHookSettings trackingStatus];
|
|
return (ATTrackingManagerAuthorizationStatus)status;
|
|
}
|
|
|
|
+ (void)requestTrackingAuthorizationWithCompletionHandler:(void (^)(ATTrackingManagerAuthorizationStatus status))completion {
|
|
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);
|
|
}
|
|
});
|
|
}
|
|
|
|
%end
|
|
|
|
#pragma mark - ASIdentifierManager Hook
|
|
|
|
%hook ASIdentifierManager
|
|
|
|
- (NSUUID *)advertisingIdentifier {
|
|
NSUUID *idfa = [XHookSettings idfa];
|
|
return idfa ?: %orig;
|
|
}
|
|
|
|
- (BOOL)isAdvertisingTrackingEnabled {
|
|
NSUInteger status = [XHookSettings trackingStatus];
|
|
return (status >= 3);
|
|
}
|
|
|
|
%end
|
|
|
|
#pragma mark - UIDevice Hook
|
|
|
|
%hook UIDevice
|
|
|
|
- (NSUUID *)identifierForVendor {
|
|
NSUUID *idfv = [XHookSettings idfv];
|
|
return idfv ?: %orig;
|
|
}
|
|
|
|
- (NSString *)name {
|
|
NSString *name = [XHookSettings deviceName];
|
|
return name ?: %orig;
|
|
}
|
|
|
|
- (NSString *)model {
|
|
return %orig;
|
|
}
|
|
|
|
- (NSString *)localizedModel {
|
|
return %orig;
|
|
}
|
|
|
|
- (NSString *)systemName {
|
|
return %orig;
|
|
}
|
|
|
|
- (NSString *)systemVersion {
|
|
NSString *version = [XHookSettings osVersion];
|
|
return version ?: %orig;
|
|
}
|
|
|
|
- (UIDeviceBatteryState)batteryState {
|
|
UIDeviceBatteryState state = [XHookSettings batteryState];
|
|
return (state != UIDeviceBatteryStateUnknown) ? state : %orig;
|
|
}
|
|
|
|
- (float)batteryLevel {
|
|
float level = [XHookSettings batteryLevel];
|
|
return (level > -1.0f) ? level : %orig;
|
|
}
|
|
|
|
- (UIDeviceOrientation)orientation {
|
|
return UIDeviceOrientationPortrait;
|
|
}
|
|
|
|
%end
|
|
|
|
#pragma mark - NSProcessInfo Hook
|
|
|
|
%hook NSProcessInfo
|
|
|
|
- (NSTimeInterval)systemUptime {
|
|
NSTimeInterval res = %orig;
|
|
double offset = [XHookSettings systemUptimeOffset];
|
|
if (offset > -1.0) {
|
|
res += offset;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
- (unsigned long long)physicalMemory {
|
|
unsigned long long memory = [XHookSettings physicalMemory];
|
|
return (memory > 1000) ? memory : %orig;
|
|
}
|
|
|
|
- (NSUInteger)processorCount {
|
|
NSUInteger cores = [XHookSettings cpuCore];
|
|
return (cores > 0) ? cores : %orig;
|
|
}
|
|
|
|
- (NSOperatingSystemVersion)operatingSystemVersion {
|
|
NSOperatingSystemVersion version = %orig;
|
|
NSString *versionString = [XHookSettings osVersion];
|
|
|
|
if (!versionString || ![versionString length]) {
|
|
return version;
|
|
}
|
|
|
|
NSArray *components = [versionString componentsSeparatedByString:@"."];
|
|
if (components.count == 0 || components.count > 3) {
|
|
return version;
|
|
}
|
|
|
|
for (NSString *component in components) {
|
|
if (![[NSScanner scannerWithString:component] scanInteger:NULL]) {
|
|
return version;
|
|
}
|
|
}
|
|
|
|
version.majorVersion = [components[0] integerValue];
|
|
version.minorVersion = components.count > 1 ? [components[1] integerValue] : 0;
|
|
version.patchVersion = components.count > 2 ? [components[2] integerValue] : 0;
|
|
|
|
return version;
|
|
}
|
|
|
|
%end
|
|
|
|
#pragma mark - UIWindow Hook
|
|
|
|
%hook UIWindow
|
|
|
|
- (UIEdgeInsets)safeAreaInsets {
|
|
UIEdgeInsets originalInsets = %orig;
|
|
NSDictionary *dic = [XHookSettings safeAreaInsets];
|
|
|
|
if (dic) {
|
|
CGFloat top = [dic[@"top"] doubleValue];
|
|
CGFloat bottom = [dic[@"bottom"] doubleValue];
|
|
originalInsets = UIEdgeInsetsMake(top, 0, bottom, 0);
|
|
}
|
|
|
|
return originalInsets;
|
|
}
|
|
|
|
%end
|
|
|
|
#pragma mark - UIScreen Hook
|
|
|
|
%hook UIScreen
|
|
|
|
- (CGFloat)brightness {
|
|
CGFloat brightness = [XHookSettings screenBrightness];
|
|
return (brightness > -1.0) ? brightness : %orig;
|
|
}
|
|
|
|
- (CGRect)bounds {
|
|
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 {
|
|
CGFloat scale = [XHookSettings screenScale];
|
|
return (scale > -1.0) ? scale : %orig;
|
|
}
|
|
|
|
- (CGRect)nativeBounds {
|
|
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 {
|
|
return NO;
|
|
}
|
|
|
|
- (UIScreen *)mirroredScreen {
|
|
return nil;
|
|
}
|
|
|
|
%end
|
|
|
|
#pragma mark - UIScreenMode Hook
|
|
|
|
%hook UIScreenMode
|
|
|
|
- (CGSize)size {
|
|
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<NSFileAttributeKey, id> *)attributesOfFileSystemForPath:(NSString *)path error:(NSError **)error {
|
|
NSDictionary<NSFileAttributeKey, id> *res = %orig;
|
|
|
|
if ([path isEqualToString:NSHomeDirectory()]) {
|
|
NSMutableDictionary *modifiedAttributes = [res mutableCopy];
|
|
|
|
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 {
|
|
NSArray<NSString *> *array = @[@"/Application/Cydia.app",
|
|
@"/Application/Sileo.app",
|
|
@"/usr/lib/TweakInject",
|
|
@"/Library/TweakInject",
|
|
@"/Library/MobileSubstrate/MobileSubstrate.dylib",
|
|
@"/bin/bash",
|
|
@"/usr/sbin/sshd",
|
|
@"/etc/apt",
|
|
@"/usr/bin/ssh",
|
|
@"/private/var/lib/apt",
|
|
@"/private/var/lib/cydia",
|
|
@"/private/var/tmp/cydia.log",
|
|
@"/Applications/WinterBoard.app",
|
|
@"/var/lib/cydia",
|
|
@"/private/etc/dpkg/origins/debian",
|
|
@"/bin.sh",
|
|
@"/private/etc/apt",
|
|
@"/etc/ssh/sshd_config",
|
|
@"/private/etc/ssh/sshd_config",
|
|
@"/Applications/SBSetttings.app",
|
|
@"/private/var/mobileLibrary/SBSettingsThemes/",
|
|
@"/private/var/stash",
|
|
@"/usr/libexec/sftp-server",
|
|
@"/usr/libexec/cydia/",
|
|
@"/usr/sbin/frida-server",
|
|
@"/usr/bin/cycript",
|
|
@"/usr/local/bin/cycript",
|
|
@"/usr/lib/libcycript.dylib",
|
|
@"/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
|
|
@"/System/Library/LaunchDaemons/com.ikey.bbot.plist",
|
|
@"/Applications/FakeCarrier.app",
|
|
@"/Library/MobileSubstrate/DynamicLibraries/Veency.plist",
|
|
@"/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist",
|
|
@"/usr/libexec/ssh-keysign",
|
|
@"/usr/libexec/sftp-server",
|
|
@"/Applications/blackra1n.app",
|
|
@"/Applications/IntelliScreen.app",
|
|
@"/Applications/Snoop-itConfig.app",
|
|
@"/var/lib/dpkg/info",
|
|
@"/User/Applications"];
|
|
|
|
NSInteger index = [array indexOfObjectPassingTest:^BOOL(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
|
BOOL ret = [event hasPrefix:obj];
|
|
if (ret) {
|
|
*stop = YES;
|
|
}
|
|
return ret;
|
|
}];
|
|
if (index != NSNotFound) {
|
|
return NO;
|
|
}
|
|
return %orig;
|
|
}
|
|
|
|
%end
|
|
|
|
#pragma mark - NSLocale Hook
|
|
|
|
%hook NSLocale
|
|
|
|
+ (NSArray *)preferredLanguages {
|
|
return @[@"en-US"];
|
|
}
|
|
|
|
+ (NSLocale *)currentLocale {
|
|
NSString *loc = [XHookSettings locale];
|
|
if (loc) {
|
|
return [[NSLocale alloc] initWithLocaleIdentifier:loc];
|
|
}
|
|
return %orig;
|
|
}
|
|
|
|
+ (NSLocale *)systemLocale {
|
|
NSString *loc = [XHookSettings locale];
|
|
if (loc) {
|
|
return [[NSLocale alloc] initWithLocaleIdentifier:loc];
|
|
}
|
|
return %orig;
|
|
}
|
|
|
|
%end
|
|
|
|
#pragma mark - NSTimeZone Hook
|
|
|
|
%hook NSTimeZone
|
|
|
|
+ (NSTimeZone *)localTimeZone {
|
|
NSString *tz = [XHookSettings timeZone];
|
|
if (tz) {
|
|
return [NSTimeZone timeZoneWithName:tz];
|
|
}
|
|
return %orig;
|
|
}
|
|
|
|
+ (NSTimeZone *)systemTimeZone {
|
|
NSString *tz = [XHookSettings timeZone];
|
|
if (tz) {
|
|
return [NSTimeZone timeZoneWithName:tz];
|
|
}
|
|
return %orig;
|
|
}
|
|
|
|
+ (NSTimeZone *)defaultTimeZone {
|
|
NSString *tz = [XHookSettings timeZone];
|
|
if (tz) {
|
|
return [NSTimeZone timeZoneWithName:tz];
|
|
}
|
|
return %orig;
|
|
}
|
|
|
|
%end
|
|
|
|
#pragma mark - NSMutableURLRequest Hook
|
|
|
|
%hook NSMutableURLRequest
|
|
|
|
- (instancetype)initWithURL:(NSURL *)URL {
|
|
self = %orig(URL);
|
|
if (self) {
|
|
NSString *userAgent = [XHookSettings userAgent];
|
|
if (userAgent) {
|
|
[self setValue:userAgent forHTTPHeaderField:@"User-Agent"];
|
|
}
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field {
|
|
NSString *finalValue = value;
|
|
if ([field isEqualToString:@"User-Agent"]) {
|
|
NSString *userAgent = [XHookSettings userAgent];
|
|
if (userAgent) {
|
|
finalValue = userAgent;
|
|
}
|
|
}
|
|
%orig(finalValue, field);
|
|
}
|
|
|
|
%end
|
|
|
|
#pragma mark - NSURLSession Hook
|
|
|
|
%hook NSURLSession
|
|
|
|
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration {
|
|
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];
|
|
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 {
|
|
NSString *userAgent = [XHookSettings userAgent];
|
|
if (userAgent) {
|
|
[self setCustomUserAgent:userAgent];
|
|
}
|
|
%orig(javaScriptString, completionHandler);
|
|
}
|
|
|
|
- (void)setCustomUserAgent:(NSString *)userAgent {
|
|
NSString *customUA = [XHookSettings userAgent];
|
|
%orig(customUA ?: userAgent);
|
|
}
|
|
|
|
- (NSString *)customUserAgent {
|
|
NSString *userAgent = [XHookSettings userAgent];
|
|
return userAgent ?: %orig;
|
|
}
|
|
|
|
%end
|
|
|
|
#pragma mark - CTTelephonyNetworkInfo Hook
|
|
|
|
%hook CTTelephonyNetworkInfo
|
|
|
|
- (NSDictionary<NSString *, NSString *> *)serviceCurrentRadioAccessTechnology {
|
|
NSDictionary<NSString *, NSString *> *originalTechnologies = %orig;
|
|
NSDictionary *simInfoDic = [XHookSettings simInfo];
|
|
|
|
if (!simInfoDic) {
|
|
return originalTechnologies;
|
|
}
|
|
|
|
NSMutableDictionary<NSString *, NSString *> *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<NSString *, CTCarrier *> *)serviceSubscriberCellularProviders {
|
|
NSDictionary<NSString *, CTCarrier *> *originalProviders = %orig;
|
|
NSDictionary *simInfoDic = [XHookSettings simInfo];
|
|
|
|
if (!simInfoDic) {
|
|
return originalProviders;
|
|
}
|
|
|
|
NSMutableDictionary<NSString *, CTCarrier *> *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;
|
|
}
|
|
|
|
@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();
|
|
|
|
CustomCMGyroData *customGyroData = (CustomCMGyroData *)[[objc_getClass("CMGyroData") alloc] init];
|
|
customGyroData.customRotationRate = randomRate;
|
|
|
|
if (handler) {
|
|
handler((CMGyroData *)customGyroData, error);
|
|
}
|
|
};
|
|
|
|
%orig(queue, newHandler);
|
|
}
|
|
|
|
%end
|
|
|
|
#pragma mark - C Function Hooks
|
|
|
|
%hookf(int, uname, struct utsname *systemInfo) {
|
|
int nRet = %orig;
|
|
NSString *productStr = [XHookSettings productStr];
|
|
if (productStr) {
|
|
char str_machine_name[100];
|
|
[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) {
|
|
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;
|
|
NSInteger kernBootTimeOffset = [XHookSettings kernBootTimeOffset];
|
|
if (kernBootTimeOffset != 0) {
|
|
a->tv_sec += kernBootTimeOffset;
|
|
}
|
|
}
|
|
return nRet;
|
|
}
|
|
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;
|
|
|
|
if (!oldp && oldlenp) {
|
|
*oldlenp = modelLen;
|
|
return 0;
|
|
}
|
|
|
|
if (oldp && oldlenp) {
|
|
if (*oldlenp < modelLen) {
|
|
return ENOMEM;
|
|
}
|
|
strlcpy((char *)oldp, spoofedModel, *oldlenp);
|
|
*oldlenp = modelLen;
|
|
return 0;
|
|
}
|
|
|
|
return EINVAL;
|
|
}
|
|
|
|
return %orig;
|
|
}
|
|
|
|
%hookf(int, sysctlbyname, const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
|
|
if (strcmp(name, "hw.machine") == 0) {
|
|
int ret = %orig;
|
|
NSString *machine = [XHookSettings productStr];
|
|
if (oldp && machine) {
|
|
const char *hwMachineCh = [machine UTF8String];
|
|
if (hwMachineCh) {
|
|
size_t len = strlen(hwMachineCh);
|
|
if (*oldlenp > len) {
|
|
strlcpy((char *)oldp, hwMachineCh, *oldlenp);
|
|
*oldlenp = len;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
} else if (strcmp(name, "hw.model") == 0) {
|
|
int ret = %orig;
|
|
NSString *modelStr = [XHookSettings hwModel];
|
|
if (oldp && modelStr) {
|
|
const char *modelStrCh = [modelStr UTF8String];
|
|
if (modelStrCh) {
|
|
size_t len = strlen(modelStrCh);
|
|
if (*oldlenp > len) {
|
|
strlcpy((char *)oldp, modelStrCh, *oldlenp);
|
|
*oldlenp = len;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
return %orig;
|
|
}
|
|
|
|
%hookf(int, clock_gettime, clockid_t __clock_id, struct timespec *__tp) {
|
|
int nret = %orig;
|
|
if(__clock_id == CLOCK_MONOTONIC_RAW) {
|
|
// Reserved
|
|
}
|
|
return nret;
|
|
}
|
|
|
|
#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);
|
|
return NULL;
|
|
}
|
|
new_ifaddr->ifa_flags = IFF_UP | IFF_RUNNING;
|
|
|
|
struct sockaddr_in *addr_in = (struct sockaddr_in *)calloc(1, sizeof(struct sockaddr_in));
|
|
if (!addr_in) {
|
|
free(new_ifaddr->ifa_name);
|
|
free(new_ifaddr);
|
|
return NULL;
|
|
}
|
|
|
|
addr_in->sin_family = AF_INET;
|
|
if (inet_pton(AF_INET, ipv4, &addr_in->sin_addr) != 1) {
|
|
free(addr_in);
|
|
free(new_ifaddr->ifa_name);
|
|
free(new_ifaddr);
|
|
return NULL;
|
|
}
|
|
new_ifaddr->ifa_addr = (struct sockaddr *)addr_in;
|
|
|
|
struct ifaddrs *next_ifaddr = (struct ifaddrs *)calloc(1, sizeof(struct ifaddrs));
|
|
if (!next_ifaddr) {
|
|
free(addr_in);
|
|
free(new_ifaddr->ifa_name);
|
|
free(new_ifaddr);
|
|
return NULL;
|
|
}
|
|
|
|
next_ifaddr->ifa_name = strdup(name);
|
|
if (!next_ifaddr->ifa_name) {
|
|
free(next_ifaddr);
|
|
free(addr_in);
|
|
free(new_ifaddr->ifa_name);
|
|
free(new_ifaddr);
|
|
return NULL;
|
|
}
|
|
next_ifaddr->ifa_flags = IFF_UP | IFF_RUNNING;
|
|
|
|
struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)calloc(1, sizeof(struct sockaddr_in6));
|
|
if (!addr_in6) {
|
|
free(next_ifaddr->ifa_name);
|
|
free(next_ifaddr);
|
|
free(addr_in);
|
|
free(new_ifaddr->ifa_name);
|
|
free(new_ifaddr);
|
|
return NULL;
|
|
}
|
|
|
|
addr_in6->sin6_family = AF_INET6;
|
|
if (inet_pton(AF_INET6, ipv6, &addr_in6->sin6_addr) != 1) {
|
|
free(addr_in6);
|
|
free(next_ifaddr->ifa_name);
|
|
free(next_ifaddr);
|
|
free(addr_in);
|
|
free(new_ifaddr->ifa_name);
|
|
free(new_ifaddr);
|
|
return NULL;
|
|
}
|
|
next_ifaddr->ifa_addr = (struct sockaddr *)addr_in6;
|
|
|
|
new_ifaddr->ifa_next = next_ifaddr;
|
|
return new_ifaddr;
|
|
} @catch (NSException *exception) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static void modifyInterfaceIP(struct ifaddrs *interface, const char *ipv4, const char *ipv6) {
|
|
if (!interface || !ipv4 || !ipv6) return;
|
|
|
|
@try {
|
|
if (interface->ifa_addr) {
|
|
if (((struct sockaddr_in*)interface->ifa_addr)->sin_family == AF_INET) {
|
|
struct sockaddr_in *addr = (struct sockaddr_in*)interface->ifa_addr;
|
|
inet_pton(AF_INET, ipv4, &addr->sin_addr);
|
|
} else if (((struct sockaddr_in*)interface->ifa_addr)->sin_family == AF_INET6) {
|
|
struct sockaddr_in6 *addr6 = (struct sockaddr_in6*)interface->ifa_addr;
|
|
inet_pton(AF_INET6, ipv6, &addr6->sin6_addr);
|
|
}
|
|
}
|
|
} @catch (NSException *exception) {
|
|
}
|
|
}
|
|
|
|
%hookf(int, getifaddrs, struct ifaddrs **interfaces) {
|
|
if (!interfaces) return EINVAL;
|
|
|
|
int nRet = %orig;
|
|
if (nRet != 0) return nRet;
|
|
|
|
@try {
|
|
NSDictionary *networkConfig = [XHookSettings networkInterfacesInfo];
|
|
if (!networkConfig) {
|
|
return nRet;
|
|
}
|
|
|
|
struct ifaddrs *interface;
|
|
bool isHookEn0IpV4 = false;
|
|
|
|
for (interface = *interfaces; interface; interface = interface->ifa_next) {
|
|
if (!interface->ifa_name) continue;
|
|
|
|
NSString *name = @(interface->ifa_name);
|
|
if ([name isEqualToString:@"en0"]) {
|
|
NSDictionary *en0Config = networkConfig[@"en0"];
|
|
if (en0Config) {
|
|
const char *ipv4 = [en0Config[@"ipv4"] UTF8String];
|
|
const char *ipv6 = [en0Config[@"ipv6"] UTF8String];
|
|
if (ipv4 && ipv6) {
|
|
modifyInterfaceIP(interface, ipv4, ipv6);
|
|
isHookEn0IpV4 = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!isHookEn0IpV4) {
|
|
NSDictionary *en0Config = networkConfig[@"en0"];
|
|
if (en0Config) {
|
|
const char *ipv4 = [en0Config[@"ipv4"] UTF8String];
|
|
const char *ipv6 = [en0Config[@"ipv6"] UTF8String];
|
|
struct ifaddrs *new_en0 = createInterface("en0", ipv4, ipv6);
|
|
if (new_en0) {
|
|
new_en0->ifa_next->ifa_next = *interfaces;
|
|
*interfaces = new_en0;
|
|
}
|
|
}
|
|
}
|
|
|
|
NSDictionary *pdpConfig = networkConfig[@"pdp_ip0"];
|
|
if (pdpConfig) {
|
|
const char *ipv4 = [pdpConfig[@"ipv4"] UTF8String];
|
|
const char *ipv6 = [pdpConfig[@"ipv6"] UTF8String];
|
|
struct ifaddrs *new_pdp = createInterface("pdp_ip0", ipv4, ipv6);
|
|
if (new_pdp) {
|
|
new_pdp->ifa_next->ifa_next = *interfaces;
|
|
*interfaces = new_pdp;
|
|
}
|
|
}
|
|
|
|
} @catch (NSException *exception) {
|
|
}
|
|
|
|
return nRet;
|
|
}
|
|
|
|
%hookf(Boolean, SCNetworkReachabilityGetFlags, SCNetworkReachabilityRef target, SCNetworkReachabilityFlags *flags) {
|
|
Boolean result = %orig(target, flags);
|
|
|
|
if (result && flags != NULL) {
|
|
*flags |= kSCNetworkReachabilityFlagsReachable;
|
|
*flags |= kSCNetworkReachabilityFlagsIsWWAN;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
%hookf(BOOL, UIAccessibilityIsClosedCaptioningEnabled) {
|
|
return YES;
|
|
}
|
|
|
|
%hookf(BOOL, UIAccessibilityIsMonoAudioEnabled) {
|
|
return YES;
|
|
}
|
|
|
|
%end // end of %group XHooks
|
|
|
|
#pragma mark - ============== CONSTRUCTOR & DESTRUCTOR ==============
|
|
|
|
%ctor {
|
|
NSLog(@"XHook: Starting...");
|
|
@try {
|
|
@autoreleasepool {
|
|
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
|
|
NSString *jsonPath = @"/var/mobile/Documents/changeapp.json";
|
|
|
|
BOOL loaded = [XHookSettings loadSettingsFromPath:jsonPath];
|
|
if (!loaded) {
|
|
NSLog(@"XHook: Failed to load settings from %@", jsonPath);
|
|
return;
|
|
}
|
|
|
|
if ([XHookSettings shouldHookApp:bundleIdentifier]) {
|
|
NSUUID *idfa = [XHookSettings idfa];
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
%dtor {
|
|
NSLog(@"XHook: Cleaning up...");
|
|
[XHookSettings cleanup];
|
|
}
|