This article is excerpted from Jiuyin Zhenjing: The Secret Book of iOS Hackers'Attack and Defense
In today's mobile Internet era, App in the platform will do some preferential activities in order to increase popularity, but in fact, the activation, registration and preferential activities of App may be "brushed". The brush team uses the method of modifying the mobile phone information so that the application can get false data and think that the old user is the new user. Apple's privacy protection prevents developers from acquiring UDID as the unique identity of devices, so the only way to identify devices is to use IDFA, OpenUDID and other third-party platforms to provide IDFA, which can be reset in system settings, and the IDs provided by OpenUDID and other third-party platforms are also available. It may be reset. As shown in Figure 9-1, it is a third-party application that installs a statistical platform. By resetting ID and modifying mobile phone information to cheat, it achieves the purpose of brush volume. In fact, it is a mobile phone, but 11 are added to the statistics.
Because the ID is stored in the sandbox and the application is unloaded, the data in the sandbox directory will be emptied, so some applications will store the ID information in Keychain, so that even if the application is unloaded, the data will not be cleared, but Keychain can also be cleared in the jailbreak environment.
9.1 Access to root privileges in a jailbreak environment
In fact, programs written in Xcode on iOS devices that have escaped from prison run as mobile users, without root privileges, so they cannot access some important files and directories.
There are two directories for iOS application installation, one is / private/var/mobile/Containers/Bundle/Application, and the other is / Applications. The former stores applications installed using Xcode or downloaded from App Store, while the latter is generally the application that comes with the system. If you want your application to run as root, follow these steps.
(1) Add the following code to the main function of the application:
setuid(0); setgid(0);
(2) Upload the generated application to / Applications/yourApp.app.
(3) At this time, there is no icon on the desktop, so you need to login SSH to run the uicache command, which is often used to fix the problem of no icon.
(4) Execute chown root yourApp and change the owner to root.
(5) Switch to the application directory and run chmod u+s yourApp.
Then click on your application on your mobile phone and see that the user running the process through the ps aux command is root, not mobile.
(6) The above method is not a problem in iOS 8 system. If it is iOS 9 and iOS 10, one more step is needed. Because of iOS 9 security restrictions, applications with root privileges are not allowed to start. After starting, you will find that they quit immediately. You can rename the executable file of the original application, such as yourApp_, and then create a new script with the name of yourApp and use the 755 command of chmod to set executable permissions. The script is as follows:
#!/bin/bash root=$(dirname "$0") exec "${root}"/yourApp_
After the yourApp script is executed, yourApp_ will be executed, so that it can start normally.
9.2 Modification of Mobile Phone Information
There are many kinds of information that can be modified by modifying the installation amount of mobile phone applications, including UDID, serial number, MAC address, Bluetooth address, system version, machine name, IDFA, IDFV, SSID, BSSID, DeviceToken, location information and so on.
9.2.1 Modification of basic information
The common method of modifying hardware information is hook MGCopy Answer. In chapter 6, when explaining MSHook Funcation, it is mentioned that the serial number of this machine can be modified by hook MGCopy Answer. This section has two goals, one is to modify hardware-related information, the other is to modify the system version, model, IDFA, IDFV.
First, we use Theos to build a new project. The commands and parameters are as follows:
exchen$ export THEOS=/opt/theos exchen$ /opt/theos/bin/nic.pl NIC 2.0 - New Instance Creator ------------------------------ ...... [11.] iphone/tweak Choose a Template (required): 11 Project Name (required): ChangeiPhoneInfo Package Name [com.yourcompany.changeiphoneinfo]: net.exchen.ChangeiPhoneInfo Author/Maintainer Name [boot]: exchen [iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: com.apple.Preferences [iphone/tweak] List of applications to terminate upon installation (space-separated, '-' for none) [SpringBoard]: Preferences Instantiating iphone/tweak in changeiphoneinfo/...
Then write the following code:
#include <substrate.h> #import <sys/utsname.h>
static CFTypeRef (*orig_MGCopyAnswer)(CFStringRef str);
static CFTypeRef (orig_MGCopyAnswer_internal)(CFStringRef str, uint32_t outTypeCode);
static int (*orig_uname)(struct utsname *);
CFTypeRef new_MGCopyAnswer(CFStringRef str);
CFTypeRef new_MGCopyAnswer_internal(CFStringRef str, uint32_t* outTypeCode);
int new_uname(struct utsname *systemInfo);
int new_uname(struct utsname * systemInfo){
NSLog(@"new_uname"); int nRet = orig_uname(systemInfo); char str_machine_name[100] = "iPhone8,1"; strcpy(systemInfo->machine,str_machine_name); return nRet;
}
CFTypeRef new_MGCopyAnswer(CFStringRef str){
//NSLog(@"new_MGCopyAnswer"); //NSLog(@"str: %@",str); NSString *keyStr = (__bridge NSString *)str; if ([keyStr isEqualToString:@"UniqueDeviceID"] ) { NSString *strUDID = @"57359dc2fa451304bd9f94f590d02068d563d283"; return (CFTypeRef)strUDID; } else if ([keyStr isEqualToString:@"SerialNumber"] ) { NSString *strSerialNumber = @"DNPJD17NDTTP"; return (CFTypeRef)strSerialNumber; } else if ([keyStr isEqualToString:@"WifiAddress"] ) { NSString *strWifiAddress = @"98:FE:94:1F:30:0A"; return (CFTypeRef)strWifiAddress; } else if ([keyStr isEqualToString:@"BluetoothAddress"] ) { NSString *strBlueAddress = @"98:FE:94:1F:30:0A"; return (CFTypeRef)strBlueAddress; } else if([keyStr isEqualToString:@"ProductVersion"]) { NSString *strProductVersion = @"10.3.3"; return (CFTypeRef)strProductVersion; } else if([keyStr isEqualToString:@"UserAssignedDeviceName"]) { NSString *strUserAssignedDeviceName = @"exchen's iPhone"; return (CFTypeRef)strUserAssignedDeviceName; } return orig_MGCopyAnswer(str);
}
CFTypeRef new_MGCopyAnswer_internal(CFStringRef str, uint32_t* outTypeCode) {
//NSLog(@"new_MGCopyAnswer_internal"); //NSLog(@"str: %@",str); NSString *keyStr = (__bridge NSString *)str; if ([keyStr isEqualToString:@"UniqueDeviceID"] ) { NSString *strUDID = @"57359dc2fa451304bd9f94f590d02068d563d283"; return (CFTypeRef)strUDID; } else if ([keyStr isEqualToString:@"SerialNumber"] ) { NSString *strSerialNumber = @"DNPJD17NDTTP"; return (CFTypeRef)strSerialNumber; } else if ([keyStr isEqualToString:@"WifiAddress"] ) { NSString *strWifiAddress = @"98:FE:94:1F:30:0A"; return (CFTypeRef)strWifiAddress; } else if ([keyStr isEqualToString:@"BluetoothAddress"] ) { NSString *strBlueAddress = @"98:FE:94:1F:30:0A"; return (CFTypeRef)strBlueAddress; } else if([keyStr isEqualToString:@"ProductVersion"]) { NSString *strProductVersion = @"10.3.3"; return (CFTypeRef)strProductVersion; } else if([keyStr isEqualToString:@"UserAssignedDeviceName"]) { NSString *strUserAssignedDeviceName = @"exchen's iPhone"; return (CFTypeRef)strUserAssignedDeviceName; } return orig_MGCopyAnswer_internal(str, outTypeCode);
}
void hook_uname(){
NSLog(@"hook_uname"); char str_libsystem_c[100] = {0}; strcpy(str_libsystem_c, "/usr/lib/libsystem_c.dylib"); void *h = dlopen(str_libsystem_c, RTLD_GLOBAL); if(h != 0){ MSImageRef ref = MSGetImageByName(str_libsystem_c); void * unameFn = MSFindSymbol(ref, "_uname"); NSLog(@"unameFn"); MSHookFunction(unameFn, (void *) new_uname, (void **)& orig_uname); } else { strcpy(str_libsystem_c, "/usr/lib/system/libsystem_c.dylib"); h = dlopen(str_libsystem_c, RTLD_GLOBAL); if(h != 0){ MSImageRef ref = MSGetImageByName(str_libsystem_c); void * unameFn = MSFindSymbol(ref, "_uname"); NSLog(@"unameFn"); MSHookFunction(unameFn, (void *) new_uname, (void **)& orig_uname); } else { NSLog(@"%s dlopen error", str_libsystem_c); } }
}
void hookMGCopyAnswer(){
char *dylib_path = (char*)"/usr/lib/libMobileGestalt.dylib"; void *h = dlopen(dylib_path, RTLD_GLOBAL); if (h != 0) { MSImageRef ref = MSGetImageByName([strDylibFile UTF8String]); void * MGCopyAnswerFn = MSFindSymbol(ref, "_MGCopyAnswer"); //64-bit signature uint8_t MGCopyAnswer_arm64_impl[8] = {0x01, 0x00, 0x80, 0xd2, 0x01, 0x00, 0x00, 0x14}; //10.3 signature uint8_t MGCopyAnswer_armv7_10_3_3_impl[5] = {0x21, 0x00, 0xf0, 0x00, 0xb8}; //Processing 64-bit system if (memcmp(MGCopyAnswerFn, MGCopyAnswer_arm64_impl, 8) == 0) { MSHookFunction((void*)((uint8_t*)MGCopyAnswerFn + 8), (void*)new_MGCopyAnswer_internal, (void**)&orig_MGCopyAnswer_internal); } //Processing 32-bit 10.3 to 10.3.3 systems else if(memcmp(MGCopyAnswerFn, MGCopyAnswer_armv7_10_3_3_impl, 5) == 0){ MSHookFunction((void*)((uint8_t*)MGCopyAnswerFn + 6), (void*)new_MGCopyAnswer_internal, (void**)&orig_MGCopyAnswer_internal); } else{ MSHookFunction(MGCopyAnswerFn, (void *) new_MGCopyAnswer, (void **)&orig_MGCopyAnswer); } }
}
%hook ASIdentifierManager
//IDFA
-(NSUUID*)advertisingIdentifier{
NSUUID *uuid = [[NSUUID alloc] init]; return uuid;
}
%end
%hook UIDevice
//IDFV
-(NSUUID*)identifierForVendor{
NSUUID *uuid = [[NSUUID alloc] init]; return uuid;
}
%end
%ctor{
hookMGCopyAnswer(); hook_uname();
}
Then download iDeviceLite and MyIDFA in the App Store. The application's Bundle ID is com.sanfriend. ios.iDeviceLite and com.iki.MyIDFA add the application's BundleID to the corresponding. plist file of Tweak, so that the two applications also load Tweak, as shown in Figure 9-2.
Getting UDID, serial number, MAC address, Bluetooth address uses MGCopyAnswer function, so we hook MGCopyAnswer. The new_MGCopyAnswer defined in the code is 32 bits and the new_MGCopyAnswer_internal is 64 bits. The corresponding parameters of MGCopyAnswer are judged in these two functions, and the corresponding false data is returned. For example, the system version returns 10.3.3 and the device name returns to the Dev iPhone, while the actual system version is 10.1.1 and the device name is the iPhone, as shown in Figures 9-3 and 9-4.
Next, hook the [ASIdentifier Manager Advertising Identifier] to return IDFA randomly, hook the [UIDevice Identifier ForVendor] to return IDFV randomly, and open the MyIDFA application, you will find that the IDFA data obtained each time is different, as shown in Figure 9-5.
The uname function will put the acquired information into the struct utsname structure. We hook the uname function and modify the machine member data of the structure to be the iPhone 8,1, which means returning to the iPhone 6s model.
At this point, we will modify the Makefile file file, the code is as follows:
THEOS_DEVICE_IP = 127.0.0.1 THEOS_DEVICE_PORT = 2222 ARCHS = armv7 arm64
include $(THEOS)/makefiles/common.mk
TWEAK_NAME = ChangeiPhoneInfo
ChangeiPhoneInfo_FILES = Tweak.xm
include $(THEOS_MAKE_PATH)/tweak.mk
after-install::
install.exec "killall -9 Preferences"
Use make package install to compile and install if the following error is prompted:
$ make package install ERROR: package name has characters that aren't lowercase alphanums or '-+.'.
Explain that the package name contains other special symbols, which can only be generated by modifying the package name. The package name can only contain lowercase letters and "-""+". ".
Since Xcode does not provide the header file of MobileGestalt.h, I found the header file of MobileGestalt.h from the Theos directory for readers to refer to the parameters of MGCopyAnswer:
#ifndef LIBMOBILEGESTALT_H_ #define LIBMOBILEGESTALT_H_
#include <CoreFoundation/CoreFoundation.h>
#if __cplusplus
extern "C" {
#endif
#pragma mark - API
CFPropertyListRef MGCopyAnswer(CFStringRef property); Boolean MGGetBoolAnswer(CFStringRef property); /* * Arguments are still a mistery. * CFPropertyListRef MGCopyAnswerWithError(CFStringRef question, int *error, ...); */ /* Use 0 for __unknown0. */ CFPropertyListRef MGCopyMultipleAnswers(CFArrayRef questions, int __unknown0); /* * Not all questions are assignable. * For example, kMGUserAssignedDeviceName is assignable but * kMGProductType is not. */ int MGSetAnswer(CFStringRef question, CFTypeRef answer);
#pragma mark - Identifying Information
static const CFStringRef kMGDiskUsage = CFSTR("DiskUsage"); static const CFStringRef kMGModelNumber = CFSTR("ModelNumber"); static const CFStringRef kMGSIMTrayStatus = CFSTR("SIMTrayStatus"); static const CFStringRef kMGSerialNumber = CFSTR("SerialNumber"); static const CFStringRef kMGMLBSerialNumber = CFSTR("MLBSerialNumber"); static const CFStringRef kMGUniqueDeviceID = CFSTR("UniqueDeviceID"); static const CFStringRef kMGUniqueDeviceIDData = CFSTR("UniqueDeviceIDData"); static const CFStringRef kMGUniqueChipID = CFSTR("UniqueChipID"); static const CFStringRef kMGInverseDeviceID = CFSTR("InverseDeviceID"); static const CFStringRef kMGDiagnosticsData = CFSTR("DiagData"); static const CFStringRef kMGDieID = CFSTR("DieId"); static const CFStringRef kMGCPUArchitecture = CFSTR("CPUArchitecture"); static const CFStringRef kMGPartitionType = CFSTR("PartitionType"); static const CFStringRef kMGUserAssignedDeviceName = CFSTR("UserAssignedDeviceName");
#pragma mark - Bluetooth Information
static const CFStringRef kMGBluetoothAddress = CFSTR("BluetoothAddress");
9.2.2 Modification of Wi-Fi Information
Modify Wi-Fi information mainly through hook CNCopy Current Network Info function, modify SSID and BSSID, so that the name of Wi-Fi hotspot will change. The specific code is as follows:
static CFDictionaryRef (*orig_CNCopyCurrentNetworkInfo) (CFStringRef interfaceName); static CFDictionaryRef new_CNCopyCurrentNetworkInfo (CFStringRef interfaceName){NSLog(@"new_CNCopyCurrentNetworkInfo"); NSString *keyStr = (__bridge NSString *)interfaceName; NSLog(@"interfaceName: %@", keyStr); if ([keyStr isEqualToString:@"en0"] ){ NSDictionary *oldDic = (__bridge NSDictionary*)orig_CNCopyCurrentNetworkInfo(interfaceName); NSMutableDictionary *dic = [[NSMutableDictionary alloc] initWithDictionary:oldDic]; [dic setValue:@"exchen" forKey:@"SSID"]; [dic setValue:@"0:6:f4:ac:2b:81" forKey:@"BSSID"]; [dic setValue:[@"exchen" dataUsingEncoding:NSUTF8StringEncoding] forKey:@"SSIDDATA"]; return (__bridge CFDictionaryRef)dic; } else{ return orig_CNCopyCurrentNetworkInfo(interfaceName); }
}
//hook
MSHookFunction((void*)CNCopyCurrentNetworkInfo, (void*)new_CNCopyCurrentNetworkInfo, (void **)
&orig_CNCopyCurrentNetworkInfo);
9.2.3 Modify DeviceToken
DeviceToken is mainly used for message pushing of applications. Some applications use DeviceToken as the basis for identifying devices. AppDelegate's didRegister ForRemoteNotifications WithDeviceToken function contains DeviceToken in its parameters, so to modify DeviceToken, hook the function. But the problem is that the name of AppDelegate is not fixed. It may also be called P1AppDelegate or a class name modified by the developer. In this case, we can call objc_getClassList to get all classes, and then use class_conformsToProtocol and class_getInstanceMethod to confirm whether it is AppDelegate for each class. The code is as follows:
//Find the name of AppDelegate int numClasses = objc_getClassList(NULL, 0); Class* list = (Class*)malloc(sizeof(Class) * numClasses); objc_getClassList(list, numClasses);
for (int i = 0; i < numClasses; i++)
{
if (class_conformsToProtocol(list[i], @protocol(UIApplicationDelegate)) &&
class_getInstanceMethod(list[i],
@selector(application:didRegisterForRemoteNotificationsWithDeviceToken:)))
{
NSLog(@"class %@", list[i]);
MSHookMessageEx(list[i],
@selector(application:didRegisterForRemoteNotificationsWithDeviceToken:),
(IMP)replaced_didRegisterForRemoteNotificationsWithDeviceToken,
(IMP*)&original_didRegisterForRemoteNotificationsWithDeviceToken);
}
}
This is the new didRegister ForRemoteNotifications WithDeviceToken function we built:
static IMP original_didRegisterForRemoteNotificationsWithDeviceToken; void replaced_didRegisterForRemoteNotificationsWithDeviceToken(id self, SEL _cmd, UIApplication* application, NSData* deviceToken) { NSString *strDeviceToken = [[[[deviceToken description] stringByReplacingOccurrencesOfString: @"<" withString: @""] stringByReplacingOccurrencesOfString: @">" withString: @""] stringByReplacingOccurrencesOfString: @" " withString: @""];NSLog(@"deviceToken: %@", strDeviceToken); deviceToken = genDeviceToken(); //Get a new Token original_didRegisterForRemoteNotificationsWithDeviceToken(self, _cmd, application, deviceToken);
}
Because the deviceToken parameter is similar to NSData, not NSString, we need to do a transformation here, the specific code is as follows:
NSData* genDeviceToken(){NSString *strDeviceToken =@"7f0601cd3eca155a836218320cb9ed013ab3ad79bd2e540d66376662c4c9750c"; strDeviceToken = [strDeviceToken stringByReplacingOccurrencesOfString:@" " withString:@""]; NSMutableData *data = [[NSMutableData alloc] init]; unsigned char whole_byte; char byte_chars[3] = {'\0','\0','\0'}; int i; for (i=0; i < [strDeviceToken length]/2; i++) { byte_chars[0] = [strDeviceToken characterAtIndex:i*2]; byte_chars[1] = [strDeviceToken characterAtIndex:i*2+1]; whole_byte = strtol(byte_chars, NULL, 16); [data appendBytes:&whole_byte length:1]; } NSData *newDeviceToken = [[NSData alloc] initWithData:data]; NSLog(@"newDeviceToken %@", newDeviceToken); return newDeviceToken;
}
9.2.4 Modification of location information
Location method of hook CLLocation Manager and coordinate method of CLLocation are used to modify location information. The code is as follows:
//Location MSHookMessageEx(objc_getClass("CLLocationManager"), @selector(location), (IMP)CLLocationManager_location, (IMP *)&_orig_CLLocationManager_location); MSHookMessageEx(objc_getClass("CLLocation"), @selector(coordinate), (IMP)CLLocation_coordinate, (IMP *)&_orig_CLLocation_coordinate);
The new location and coordinate processing code is as follows:
static CLLocation *(* _orig_CLLocationManager_location)(id _self, SEL _cmd1); static CLLocation *CLLocationManager_location(id _self, SEL _cmd1) {NSLog(@"CLLocationManager_location"); CLLocation *location = _orig_CLLocationManager_location(_self, _cmd1); return location;
}
static CLLocationCoordinate2D (* _orig_CLLocation_coordinate)(id _self, SEL _cmd1);
CLLocationCoordinate2D CLLocation_coordinate(id _self, SEL _cmd1) {
NSLog(@"CLLocation_coordinate"); NSString *strLatitude = @"39.98788022" NSString *strLongitude = @"116.34287412" if([g_strRandomLocaltion isEqualToString:@"1"]){ CLLocationCoordinate2D coordinate; coordinate.latitude = [strLatitude doubleValue]; coordinate.longitude = [strLongitude doubleValue]; return coordinate; } else if((strLatitude != nil) && (strLongitude != nil)){ CLLocationCoordinate2D coordinate; coordinate.latitude = [strLatitude doubleValue]; coordinate.longitude = [strLongitude doubleValue]; return coordinate; } else{ return _orig_CLLocation_coordinate(_self, _cmd1); }
}
9.3 Clearance of application data
Because iOS applications generally store data in sandbox directories, as long as the application sandbox directory is emptied, the ordinary data stored by applications will be deleted. In this way, the application will be considered as a new running environment when it is re-installed.
For example, to clear the sandbox data of Wechat, we first need to get the path of the sandbox directory of Wechat and call the custom function getWeChat.- Sandbox Path, which uses private API s such as LS Application Workspace all Applications, obtains installation information for all applications, judges from the installation information that if Bundle ID is com.tencent.xin, it returns to the corresponding sandbox directory.
Clean Bundle Container clears sandbox data, and when it's done, remember to create a directory as a mobile user. Otherwise, the sandbox directory may not be writable after installing Wechat again due to permission issues. The code is as follows:
-(void)cleanBundleContainer:(NSString*)strBundleDataPath{//Judging directories, only these two directories can be cleared, if other directories, such as / var/mobile/Documents / must not be cleared, //Otherwise, it may need to reactivate or cause other problems. if ([strBundleDataPath hasPrefix:@"/private/var/mobile/Containers/Data/Application/"] || [strBundleDataPath hasPrefix:@"/var/mobile/Containers/Data/Application"]) { NSFileManager *fm = [NSFileManager defaultManager]; NSString *strDocumentsPath = [strBundleDataPath stringByAppendingPathComponent:@"Documents"]; [fm removeItemAtPath:strDocumentsPath error:nil]; NSString *strLibraryPath = [strBundleDataPath stringByAppendingPathComponent:@"Library"]; [fm removeItemAtPath:strLibraryPath error:nil]; NSString *strCachesPath = [strLibraryPath stringByAppendingPathComponent:@"Caches"]; NSString *strPreferencesPath = [strLibraryPath stringByAppendingPathComponent:@"Preferences"]; NSString *strTmpPath = [strBundleDataPath stringByAppendingPathComponent:@"tmp"]; [fm removeItemAtPath:strTmpPath error:nil]; NSString *strStoreKitPath = [strBundleDataPath stringByAppendingPathComponent:@"StoreKit"]; [fm removeItemAtPath:strStoreKitPath error:nil]; //After deleting the sandbox directory, create the corresponding directory as mobile, otherwise the application may be re-installed due to permission issues. //Cannot write to application sandbox directory NSDictionary *strAttrib = [NSDictionary dictionaryWithObjectsAndKeys: @"mobile",NSFileGroupOwnerAccountName, @"mobile",NSFileOwnerAccountName, nil]; [fm createDirectoryAtPath:strBundleDataPath withIntermediateDirectories:NO attributes:strAttrib error:nil]; [fm createDirectoryAtPath:strDocumentsPath withIntermediateDirectories:NO attributes:strAttrib error:nil]; [fm createDirectoryAtPath:strLibraryPath withIntermediateDirectories:NO attributes:strAttrib error:nil]; [fm createDirectoryAtPath:strCachesPath withIntermediateDirectories:NO attributes:strAttrib error:nil]; [fm createDirectoryAtPath:strPreferencesPath withIntermediateDirectories:NO attributes:strAttrib error:nil]; [fm createDirectoryAtPath:strTmpPath withIntermediateDirectories:NO attributes:strAttrib error:nil]; }
}
-(NSString*) getWeChatSandboxPath{
NSMutableArray *arrayAppInfo = [[NSMutableArray alloc] init]; //Get the list of applications Class cls = NSClassFromString(@"LSApplicationWorkspace"); id s = [(id)cls performSelector:NSSelectorFromString(@"defaultWorkspace")]; NSArray *array = [s performSelector:NSSelectorFromString(@"allApplications")]; Class LSApplicationProxy_class = NSClassFromString(@"LSApplicationProxy"); for (LSApplicationProxy_class in array){ NSString *strBundleID = [LSApplicationProxy_class performSelector: @selector(bundleIdentifier)]; //Getting information about the application NSString *strVersion = [LSApplicationProxy_class performSelector:@selector(bundleVersion)]; NSString *strShortVersion = [LSApplicationProxy_class performSelector: @selector(shortVersionString)]; NSURL *strContainerURL = [LSApplicationProxy_class performSelector:@selector(containerURL)]; NSString *strContainerDataPath = [strContainerURL path]; NSURL *strResourcesDirectoryURL = [LSApplicationProxy_class performSelector: @selector(resourcesDirectoryURL)]; NSString *strContainerBundlePath = [strResourcesDirectoryURL path]; NSString *strLocalizedName = [LSApplicationProxy_class performSelector: @selector(localizedName)]; NSString *strBundleExecutable = [LSApplicationProxy_class performSelector: @selector(bundleExecutable)]; //NSLog(@"bundleID: %@ localizedName: %@", strBundleID, strLocalizedName); if ([strBundleID isEqualToString:@"com.tencent.xin"]) { return strContainerDataPath; } } return nil;
}
-
(void)viewDidLoad {
[super viewDidLoad];
//Do any additional setup after loading the view, typically from a nib// Get Wechat Sandbox Catalogue
NSString *strContainerDataPath = [self getWeChatSandboxPath];
if (strContainerDataPath) {//Clear the Sandbox Catalogue of Wechat [self cleanBundleContainer:strContainerDataPath];
}
else{
NSLog(@"can't find WeChat sandbox path");
}
}
In addition to the sandbox directory, the application data may also be stored in a. plist file in the / var/mobile/Library/Preferences directory. For example, QQ will save / var/mobile/Library/Preferences / com. tencent. mqqq. plist, and Wechat will save / var/mobile/Library/Preferences/com.tencent.xin.plist. Clear their code as follows:
-(void)cleanPreferencesFile:(NSString*)strBundleId{NSString *strPreferencesFile = [NSString stringWithFormat:@"/var/mobile/Library/ Preferences/%@.plist",strBundleId]; NSFileManager *fm = [NSFileManager defaultManager]; [fm removeItemAtPath:strPreferencesFile error:nil];
}
9.4 Clear Keychain
On iOS system, Keychain's data is stored in / var/Keychains/keychain-2.db. This file is a SQLite database, and we can execute SQL statements to delete the corresponding data. Because Keychain is very important data, deleting system-related data may lead to serious consequences such as inability to access the system. If you want to delete application-related storage, you can clean it up by executing the following statements:
DELETE FROM genp WHERE agrp<>'apple' DELETE FROM cert WHERE agrp<>'lockdown-identities' DELETE FROM keys WHERE agrp<>'lockdown-identities' DELETE FROM inet DELETE FROM sqlite_sequence
Keychain can also be cleaned up by code, noting that the application must run with root privileges. The code is as follows:
-(void)cleanKeychain{sqlite3 *db; //Pointer to database NSString *strFile = @"/var/Keychains/keychain-2.db"; int result = sqlite3_open([strFile UTF8String], &db); //Determine whether the database was opened successfully if (result != SQLITE_OK) { NSString *strText = [NSString stringWithFormat:@"open sqlite error %d",result]; UIAlertView *alert =[[UIAlertView alloc] initWithTitle:@"info" message:strText delegate:self cancelButtonTitle:@"ok" otherButtonTitles:nil]; [alert show]; return; } char *perror = NULL; //When the execution of the SQLite statement fails, the reason for the failure is stored in it. NSString *strSQL = @"DELETE FROM genp WHERE agrp<>'apple'"; result = sqlite3_exec(db, [strSQL UTF8String], nil, nil, &perror); strSQL = @"DELETE FROM cert WHERE agrp<>'lockdown-identities'"; result = sqlite3_exec(db, [strSQL UTF8String], nil, nil, &perror); strSQL = @"DELETE FROM keys WHERE agrp<>'lockdown-identities'"; result = sqlite3_exec(db, [strSQL UTF8String], nil, nil, &perror); strSQL = @"DELETE FROM inet"; result = sqlite3_exec(db, [strSQL UTF8String], nil, nil, &perror); strSQL = @"DELETE FROM sqlite_sequence"; result = sqlite3_exec(db, [strSQL UTF8String], nil, nil, &perror); sqlite3_close(db);
}
9.5 Clear Clipboard
The way to clear the clipboard is to call the launchctl unload command to stop the clipboard service, then delete the pasteboardDB file, and then call the launchctl load to load the clipboard service. So the content of the clipboard is empty. The relevant code is as follows:
-(void)cleanPasteboard{NSString *strCmd = @"launchctl unload -w /System/Library/LaunchDaemons/ com.apple.UIKit.pasteboardd.plist"; system([strCmd UTF8String]); strCmd = @"rm /var/mobile/Library/Caches/com.apple.UIKit.pboard/pasteboardDB"; system([strCmd UTF8String]); strCmd = @"launchctl load -w /System/Library/LaunchDaemons/com.apple.UIKit.pasteboardd.plist"; system([strCmd UTF8String]);
}
The above method can clear the clipboard in iOS 8 and iOS 9, but by iOS 10, the storage location and structure of the clipboard have changed, so it can no longer be used. The specific code for iOS 10 to clear the clipboard is as follows:
//iOS 10 Clears Clipboard Cache Directory NSString *strPasteboardPath = @"/var/mobile/Library/Caches/com.apple.Pasteboard"; NSFileManager *fm = [NSFileManager defaultManager]; NSArray *dirs = [fm contentsOfDirectoryAtPath:strPasteboardPath error:nil]; NSString *dir; for (dir in dirs) { CLog(@"%@",dir);if (![dir isEqualToString:@"Schema.plist"]) { NSString *strPasteboardDir = [NSString stringWithFormat:@"%@/%@",strPasteboardPath,dir]; [fm removeItemAtPath:strPasteboardDir error:nil]; }
}
9.6 Publishing Applications
When we write a jailbreak application, we usually pack the application into deb format, then make our own Cydia source and publish the Cydia source address. In this way, the user can search for our application and download it by adding the source address.
9.6.1 Package App into deb
Create a new debtest directory and two new directories, DEBIAN and Applications, under the debtest directory. Then a new text file control is created under DEBIAN, which is the configuration file for packaging. The editing file is as follows:
Package: net.exchen.test Name: Application testing Version: 0.1 Description: This is a test program. Section: Game Depends: firmware (>= 8.0) Priority: optional Architecture: iphoneos-arm Author: exchen Homepage: http://www.exchen.net Icon: file:///Applications/test.app/Icon.png Maintainer: exchen
Find the application you compiled with Xcode, copy it to the Applications directory, and remember to delete the. DS_Store file, otherwise the installation may fail, use ls-al to view the file for confirmation. Switch to the debtest superior directory and run the following command. If dpkg-deb is prompted not to find this command, go to the Theos directory to find:
/opt/theos/bin/dpkg-deb -b debtest test.deb
Package test.deb and install it. There are two ways to install, the first is to use iFile installation, upload files to any location on the mobile phone, open with iFile can be installed, if there are installation errors, the return code is 256, then it may be packaging. DS_Store into the package, the debtest directory. DS_Store files are deleted, re-typed. Package upload installation once, you can install successfully.
The second is to use Cydia installation to upload test.deb to the / var/root/Media/Cydia/AutoInstall directory, which will be installed automatically after reboot. Sometimes we need to decompress other people's packages for analysis. The DEB unpacking command is as follows:
dpkg -x test.deb testdir
9.6.2 Making Cydia Source Publishing Application
First generate Packages.bz2:
dpkg-scanpackages xxxx.deb > Packages
Packages file is actually a collection of control files. Open Packages and check it out. It's similar to the following format:
Package: net.exchen.xxx Version: 1.0.0 Architecture: iphoneos-arm Maintainer: exchen <http://www.exchen.net> Depends: firmware (>= 8.0) Filename: xxx.deb Size: 120682 MD5sum: a55677d77e229dace421d65db2a80603 SHA1: 43bcff95156c043c461650938c89fce8dc8da037 SHA256: d088b1d050a7191078550a24340ed8228cfca019b665a60706d0996dd2e197e3 Section: System tools Priority: optional Homepage: http://www.exchen.net Description: Powerful xxx Software Author: exchen <http://www.exchen.net> Icon: file:///Applications/xxx.app/AppIcon60x60@2x.png Name: xxx
Also note that if your application contains dylib, to add Depends to the dependency of mobile strategy, Cydia will prompt you to restart after installing the application:
Depends: firmware (>= 8.0) mobilesubstrate
Then compress to generate Packages.bz2:
bzip2 Packages
Write Release files:
Origin: exchen Software Source™ Label: exchen Suite: stable Version: 1.7 Codename: exchen Architectures: iphoneos-arm Components: main Description: exchen Software Source
Upload deb, Packages.bz2, Release to the Web server, add the source server address in Cydia, and install the application after successful addition, as shown in Figure 9-6.
9.7 Switching of Privileges
The application uses setuid(0); there is a problem when it is set to root. Because SpringBoard users are mobile, it can't "kill" the application. That is to say, double-clicking the Home button to push upward can't exit the application, but can cause the system to get stuck. At this time, only through SSH login to the system, executing killall-9 XXXX "kill" application, can the system return to normal use.
There are several ways to solve this problem, such as writing a Tweak to detect the event that SpringBoard double-clicks the Home key to exit the application, and then "kill" the application. But this method is somewhat circumvented, the simplest is to switch permissions, the application does not need to set uid at the beginning of startup, when it needs only root permission to do things, only set uid to 0, such as clearing Keychain must switch uid to 0, clearing after the uid set to 501, that is, mobile users.
setreuid(0,0); clearKeychain(); setreuid(501,0);
9.8 Change IP Address
Opening an application or registering an account may be recorded as an IP address. If IP is unchanged, the application provider can filter the brush volume data through the IP address field. If IP is changed every time an application opens or registers, then big data statistics can not filter the brush volume behavior through the IP field.
In terms of network communication technology, the IP address of external network communication can not be disguised and modified by hook like system information. The most common way to change IP is to use VPN and HTTP proxy. These two methods need a large number of VPN servers and HTTP proxy servers. The cost is very high. At present, the most convenient way is to make the IP address of the operator with SIM card. Every time the flight mode is switched, the IP address will be retrieved.
The method of switching on and off flight mode is to use the private API [Radios Preferences setAirplane Mode], which is coded as follows:
Class RadiosPreferences = NSClassFromString(@"RadiosPreferences"); id radioPreferences = [[RadiosPreferences alloc] init]; [radioPreferences setAirplaneMode:YES]; sleep(1); [radioPreferences setAirplaneMode:NO];
The header information of Radios Preferences is as follows:
@protocol RadiosPreferencesDelegate, OS_dispatch_queue, OS_os_log; //#import "AppSupport-Structs.h" @class NSObject;
typedef struct __SCPreferences* SCPreferencesRef;
@interface RadiosPreferences : NSObject {
SCPreferencesRef _prefs; int _applySkipCount; id<RadiosPreferencesDelegate> _delegate; BOOL _isCachedAirplaneModeValid; BOOL _cachedAirplaneMode; //NSObject*<OS_dispatch_queue> _dispatchQueue; //NSObject*<OS_os_log> radios_prefs_log; BOOL notifyForExternalChangeOnly;
}
@property (assign,nonatomic) BOOL airplaneMode;
@property (assign,nonatomic) id<RadiosPreferencesDelegate> delegate; //@synthesize delegate=_delegate - In the implementation block
@property (assign,nonatomic) BOOL notifyForExternalChangeOnly;
+(BOOL)shouldMirrorAirplaneMode;
-(void*)getValueForKey:(id)arg1 ;
-(void)notifyTarget:(unsigned)arg1 ;
-(void)initializeSCPrefs:(id)arg1 ;
-(void)setAirplaneModeWithoutMirroring:(BOOL)arg1 ;
-(void*)getValueWithLockForKey:(id)arg1 ;
//-(void)setCallback:(/function pointer/void*)arg1 withContext:(SCD_Struct_Ra9*)arg2 ;
-(BOOL)notifyForExternalChangeOnly;
-(id)init;
-(oneway void)release;
-(void)setValue:(void*)arg1 forKey:(id)arg2 ;
-(id<RadiosPreferencesDelegate>)delegate;
-(void)synchronize;
-(void)setDelegate:(id<RadiosPreferencesDelegate>)arg1 ;
-(void)dealloc;
-(id)initWithQueue:(id)arg1 ;
-(void)refresh;
-(BOOL)airplaneMode;
-(void)setNotifyForExternalChangeOnly:(BOOL)arg1 ;
-(BOOL)telephonyStateWithBundleIdentifierOut:(id*)arg1 ;
-(void)setTelephonyState:(BOOL)arg1 fromBundleID:(id)arg2 ;
-(void)setAirplaneMode:(BOOL)arg1 ;
@end
Note that you also need to sign the executable file of the application, add access rights, and create a new ent2.plist:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.wifi.manager-access</key> <true/> <key>com.apple.SystemConfiguration.SCPreferences-write-access</key> <array> <string>com.apple.radios.plist</string> </array> <key>com.apple.SystemConfiguration.SCDynamicStore-write-access</key> <true/> <key>com.apple.springboard.debugapplications</key> <true/> <key>run-unsigned-code</key> <true/> <key>get-task-allow</key> <true/> <key>task_for_pid-allow</key> <true/> </dict> </plist>
Then sign:
BUILD_APP_PATH_FILE="$BUILT_PRODUCTS_DIR/$TARGET_NAME.app/$TARGET_NAME" codesign -s - --entitlements ~/dev/tools/ent2.plist -f "$BUILD_APP_PATH_FILE"
In this way, the setAirplaneMode function can be executed effectively.
9.9 Prison Break Detection
If a device is jailbreaking, it may be considered a risky device by the supplier of the application. To avoid being detected in the jailbreaking state, brush teams will rack their brains to use various methods to combat it. Apple does not provide an API to detect jailbreak status directly. The common method of jailbreak detection is to judge whether Cydia and other related documents exist or if they exist, they indicate jailbreak, otherwise there will be no jailbreak. The following files are generally detected:
/Applications/Cydia.app
/private/var/lib/cydia
/Applications/iFile.app
/Library/MobileSubstrate/MobileSubstrate.dylib
/usr/bin/sshd
/var/lib/apt
/private/var/lib/apt
/.cydia_no_stash
To bypass jailbreak detection, hook-related file judgement functions such as [NSFileManager file ExistsAtPath] and stat are required. Then, determine the path of the file. If the file path is a file related to jailbreak, it returns to the state that cannot be found. The hook code is as follows:
MSHookFunction((void*)stat, (void*)replaced_stat, (void **) &original_stat); MSHookMessageEx(objc_getClass("NSFileManager"), @selector(fileExistsAtPath:), (IMP)NSFileManager_fileExistsAtPath, (IMP *)&_orig_NSFileManager_fileExistsAtPath); MSHookMessageEx(objc_getClass("NSFileManager"), @selector(fileExistsAtPath:isDirectory:), (IMP)NSFileManager_fileExistsAtPath_isDirectory, (IMP *)&_orig_NSFileManager_ fileExistsAtPath_isDirectory);
After hook, the new function is processed as follows:
NSArray *bypassList = [[NSArray alloc] initWithObjects: @"/Applications", @"/usr/sbin", @"/usr/libexec", @"/usr/bin/sshd", @"/var/lib", @"/private/var/lib", @"/var/root", @"/bin/bunzip2", @"/bin/bash", @"/bin/sh", @"/User/Applications", @"/User/Applications/", @"/etc", @"/panguaxe", @"/panguaxe.installed", @"/xuanyuansword", @"/xuanyuansword.installed", @"/taig", @"/report_3K.plist", @"/.pg_inst", @"/pguntether", @"/.cydia_no_stash", @"/Library/MobileSubstrate", @"/System/Library/LaunchDaemons", @"/var/mobile/Library/Preferences", nil];
int (*original_stat)(const char *path, struct stat *info);
static int replaced_stat(const char *path, struct stat *info) {
for (NSString *bypassPath in bypassList) { if (strncmp([bypassPath UTF8String], path, [bypassPath length]) == 0) { errno = ENOENT; return -1; } } return original_stat(path, info);
}
static BOOL (* _orig_NSFileManager_fileExistsAtPath)(id _self, SEL _cmd1, NSString *path);
BOOL NSFileManager_fileExistsAtPath(id _self, SEL _cmd1, NSString *path) {
for (NSString *bypassPath in bypassList) { if ([path hasPrefix:bypassPath]) { return NO; } } return _orig_NSFileManager_fileExistsAtPath(_self, _cmd1, path);
}
static BOOL (* _orig_NSFileManager_fileExistsAtPath_isDirectory)(id _self, SEL _cmd1, NSString *path,
BOOL *isDirectory);
BOOL NSFileManager_fileExistsAtPath_isDirectory(id _self, SEL _cmd1, NSString *path, BOOL *isDirectory) {
for (NSString *bypassPath in bypassList) { if ([path hasPrefix:bypassPath]) { return NO; } } return _orig_NSFileManager_fileExistsAtPath_isDirectory(_self, _cmd1, path, isDirectory);
}
In addition to file judgment, there are also some "advanced" methods of jailbreak detection, such as detecting the environment variables of DYLD_INSERT_LIBRARIES and whether the detection function is hook ed. These methods will be introduced in Section 12.4.
9.10 Modify any location information without jailbreak
The method of modifying location information is explained in Section 9.3.4. The principle is to hook the corresponding method after escape. How to modify the location information if you don't escape from prison? Of course, there are ways. Remember that in Xcode, debugging programs on simulators can set virtual locations? This method can also be applied to the real machine, so as to achieve the effect of modifying the location information without jailbreak.
iOS original coordinate system is WGS-84, Gaud coordinate system is GCS-02, Baidu coordinate system is BD-09. We need to do a coordinate conversion before changing the location. The Golden Map provides a coordinate pickup system, which can easily find the coordinate location. The address is http://lbs.amap.com/console/show/picker. First, find a coordinate position as the location we intend to specify, such as searching Beijing Jiaotong University, find the coordinates 116.342802, 39.952291, as shown in Figure 9-7.
When the coordinates of Golden Map are displayed on the mobile phone, there will be offset errors, so it needs to be converted to the coordinate system used by Apple. Through [JZLocation Converterger cj02ToWgs84:] method, coordinate transformation can be carried out. The specific code is as follows:
CLLocation *location = [[CLLocation alloc] initWithLatitude:39.952291 longitude:116.342802]; CLLocationCoordinate2D c2d = [JZLocationConverter gcj02ToWgs84:location.coordinate]; NSLog(@"The converted coordinates are:%f,%f",c2d.latitude,c2d.longitude);
The coordinates after transformation are 39.950950, 116.336629. Create a new APP project, create a new gpx file in the project, write the converted coordinates, the specific code is as follows:
<?xml version="1.0" encoding="UTF-8" ?> <gpx version="1.1" creator="http://www.exchen.net" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"> <wpt lat="39.950950" lon="116.336629"> <name>beijing</name> <cmt>Beijing Jiaotong University</cmt> <desc>Beijing Jiaotong University</desc> </wpt> </gpx>
Then click Produce Scheme Eidt Scheme Options in Xcode, check Allow Location Simulation, and select the new gpx file we just created, as shown in Figure 9-8.
After running, open Baidu Map or Gaode Map on the mobile phone, and it will show that the current location is in Beijing Jiaotong University, as shown in Figure 9-9.
9.11 Log on to the same Wechat on both mobile phones at the same time
We know that a Wechat account can only be logged on to one mobile phone. If a Wechat account is logged on to two mobile phones, the latter will cancel the former. So how to make a Wechat account login on two mobile phones at the same time? We found that Wechat supports simultaneous login of computers and mobile phones, but some people may overlook one thing: Wechat itself supports simultaneous login of the iPhone and the iPad. So there's a trick to change the iPhone to the iPad for Wechat on the system, so that you can log in at the same time, as shown in Figure 9-10.
The modified methods are hook uname and [UIDevice_model] methods, which are coded as follows:
MSHookFunction((void*)uname, (void*)new_uname, (void **)&orig_uname); MSHookMessageEx(objc_getClass("UIDevice"), @selector(model), (IMP)UIDevice_model, &_orig_UIDevice_model);
Then write a new function with the following code:
static int (*orig_uname)(struct utsname *); int new_uname(struct utsname *systemInfo);
int new_uname(struct utsname * systemInfo){
NSLog(@"new_uname"); int nRet = orig_uname(systemInfo); char str_machine_name[100] = {"iPad3,6"}; //iPad4 char str_device_name[100] = {"iPad"}; strcpy(systemInfo->machine,str_machine_name); strcpy(systemInfo->nodename, str_device_name); return nRet;
}
static IMP _orig_UIDevice_model;
NSString *UIDevice_model(id _self, SEL _cmd1) {
NSString *fakeModel = @"iPad";
return fakeModel;
}
9.12 62 Data of Wechat
When a Wechat account is logged on to a new device, it is prompted to authenticate, as shown in Figure 9-11.
Click on "Start Verification" to indicate that there are three ways to authenticate identity. The first way is "Short Message Verification". The second way is "Sweep Two-Dimensional Code Verification". The third way is "Invite Friends to Authenticate". Only after the validation has passed, can the login succeed.
The purpose of 62 data is to bypass the authentication of new device login. 62 Data Stored in Sandbox Directory Library/ In the WechatPrivate/wx.dat file. Close the Wechat process, first copy the file to the new device, and then enter the following command to set the permission of the file:
chown -R mobile:mobile /private/var/mobile/Containers/Data/Application/236C09C7-E9BC-41E5-A956- 53FE5743EDC8/Library/WechatPrivate chmod -R 755 /private/var/mobile/Containers/Data/Application/236C09C7-E9BC-41E5-A956-53FE5743EDC8/ Library/WechatPrivate
Finally, when you open the Wechat and log in, you won't be prompted to authenticate. This is called 62 data because the wx.dat file begins with hexadecimal 62, as shown in Figure 9-12.