├── iReSign ├── .gitignore ├── iReSign │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── iReSign │ │ ├── en.lproj │ │ │ ├── InfoPlist.strings │ │ │ └── MainMenu.xib │ │ ├── zh-Hans.lproj │ │ │ ├── InfoPlist.strings │ │ │ └── MainMenu.xib │ │ ├── iReSign-Prefix.pch │ │ ├── IRTextFieldDrag.h │ │ ├── IRHelpWindow.h │ │ ├── main.m │ │ ├── IRHelpWindow.m │ │ ├── ResourceRules.plist │ │ ├── iReSign-Info.plist │ │ ├── IRTextFieldDrag.m │ │ ├── iReSignAppDelegate.h │ │ └── iReSignAppDelegate.m │ ├── Icon.icns │ └── iReSign.xcodeproj │ │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── iReSign.xccheckout │ │ └── project.pbxproj └── README.md ├── screenshot.png ├── DyldPatcher ├── optool ├── README.md └── patchapp.sh ├── DyldXcodeProject ├── HackingFacebook.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── wutian.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ ├── xcuserdata │ │ └── wutian.xcuserdatad │ │ │ └── xcschemes │ │ │ ├── xcschememanagement.plist │ │ │ └── HackingFacebook.xcscheme │ └── project.pbxproj ├── HackingFacebook │ ├── HackingEntry.h │ ├── HackingFacebook.h │ ├── ClassDumpHeaders │ │ ├── FBSSLPinningVerifier.h │ │ └── FBLigerConfig.h │ ├── Info.plist │ ├── Aspect │ │ ├── LICENSE │ │ ├── Aspects.h │ │ ├── README.md │ │ └── Aspects.m │ └── HackingEntry.m └── HackingFacebookTests │ ├── Info.plist │ └── HackingFacebookTests.m └── README.md /iReSign/.gitignore: -------------------------------------------------------------------------------- 1 | DerivedData 2 | xcuserdata -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Naituw/HackingFacebook/HEAD/screenshot.png -------------------------------------------------------------------------------- /DyldPatcher/optool: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Naituw/HackingFacebook/HEAD/DyldPatcher/optool -------------------------------------------------------------------------------- /iReSign/iReSign/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /iReSign/iReSign/Icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Naituw/HackingFacebook/HEAD/iReSign/iReSign/Icon.icns -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign/zh-Hans.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign/iReSign-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'iReSign' target in the 'iReSign' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign/IRTextFieldDrag.h: -------------------------------------------------------------------------------- 1 | // 2 | // IRTextFieldDrag.h 3 | // iReSign 4 | // 5 | // Created by Esteban Bouza on 01/12/12. 6 | // 7 | 8 | #import 9 | 10 | @interface IRTextFieldDrag : NSTextField 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebook.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebook.xcodeproj/project.xcworkspace/xcuserdata/wutian.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Naituw/HackingFacebook/HEAD/DyldXcodeProject/HackingFacebook.xcodeproj/project.xcworkspace/xcuserdata/wutian.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebook/HackingEntry.h: -------------------------------------------------------------------------------- 1 | // 2 | // HackingEntry.h 3 | // HackingFacebook 4 | // 5 | // Created by wutian on 2017/2/14. 6 | // Copyright © 2017年 Weibo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HackingEntry : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign/IRHelpWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // IRHelpWindow.h 3 | // iReSign 4 | // 5 | // Created by Kramer on 12/13/13. 6 | // Copyright (c) 2013 nil. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface IRHelpWindow : NSWindow 12 | 13 | // Close the window 14 | - (IBAction)closeOK:(id)sender; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // iReSign 4 | // 5 | // Created by Maciej Swic on 2011-05-16. 6 | // Copyright (c) 2011 Maciej Swic, Licensed under the MIT License. 7 | // See README.md for details 8 | // 9 | 10 | #import 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | return NSApplicationMain(argc, (const char **)argv); 15 | } 16 | -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign/IRHelpWindow.m: -------------------------------------------------------------------------------- 1 | // 2 | // IRHelpWindow.m 3 | // iReSign 4 | // 5 | // Created by Shmoopi on 12/13/13. 6 | // Copyright (c) 2013 nil. All rights reserved. 7 | // 8 | 9 | #import "IRHelpWindow.h" 10 | 11 | @implementation IRHelpWindow 12 | 13 | // Move the window by dragging 14 | - (BOOL)isMovableByWindowBackground { 15 | return YES; 16 | } 17 | 18 | // Close the window 19 | - (IBAction)closeOK:(id)sender { 20 | [self close]; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign/ResourceRules.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | rules 6 | 7 | .* 8 | 9 | Info.plist 10 | 11 | omit 12 | 13 | weight 14 | 10 15 | 16 | ResourceRules.plist 17 | 18 | omit 19 | 20 | weight 21 | 100 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebook/HackingFacebook.h: -------------------------------------------------------------------------------- 1 | // 2 | // HackingFacebook.h 3 | // HackingFacebook 4 | // 5 | // Created by wutian on 2017/2/14. 6 | // Copyright © 2017年 Weibo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for HackingFacebook. 12 | FOUNDATION_EXPORT double HackingFacebookVersionNumber; 13 | 14 | //! Project version string for HackingFacebook. 15 | FOUNDATION_EXPORT const unsigned char HackingFacebookVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebook/ClassDumpHeaders/FBSSLPinningVerifier.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by class-dump 3.5 (64 bit). 3 | // 4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard. 5 | // 6 | 7 | #import 8 | 9 | @interface FBSSLPinningVerifier : NSObject 10 | //{ 11 | // struct unique_ptr> _pinner; 12 | // struct unique_ptr> _sandboxPinner; 13 | //} 14 | 15 | + (id)sharedVerifier; 16 | - (_Bool)checkPinning:(id)arg1; 17 | - (id)init; 18 | 19 | @end 20 | 21 | -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebook.xcodeproj/xcuserdata/wutian.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | HackingFacebook.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | C6091D9F1E52BC0E003F0FF2 16 | 17 | primary 18 | 19 | 20 | C6091DA81E52BC0E003F0FF2 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebookTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebook/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebookTests/HackingFacebookTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // HackingFacebookTests.m 3 | // HackingFacebookTests 4 | // 5 | // Created by wutian on 2017/2/14. 6 | // Copyright © 2017年 Weibo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HackingFacebookTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation HackingFacebookTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebook/Aspect/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2015 Peter Steinberger, steipete@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign/iReSign-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | Icon.icns 11 | CFBundleIdentifier 12 | com.appulize.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.4 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSApplicationCategoryType 26 | public.app-category.developer-tools 27 | LSMinimumSystemVersion 28 | ${MACOSX_DEPLOYMENT_TARGET} 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /DyldPatcher/README.md: -------------------------------------------------------------------------------- 1 | # iOS Dylib Patching Demo 2 | 3 | This repository serve as a tool to teach iOS Developers how to inject using dynamic libraries into Ipa Files 4 | 5 | ## Main Components (included) 6 | - [optool](https://github.com/alexzielenski/optool) 7 | - patchapp.sh (code was modified from https://github.com/bamtan/PokemonGoAnywhere/blob/master/patchapp.sh) 8 | 9 | ## How to patch a ipa file 10 | 1. Download a cracked ipa. (eg. https://www.iphonecake.com) 11 | 2. Use Xcode to create an iOS Dynamic Framework (see below as an example). Build the framework target for iphoneos (non-iphonesimulator) 12 | 3. Reveal the framework in finder and locate the dynamic library binary. 13 | 4. Copy the dynamic library binary file and any depedent dylibs into a folder (lets call it DYLIBS). You may need additional XCode stock swift dylib which you can find them at ```/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift```. Copy any required swift dylib into the DYLIBS folder 14 | 5. Go to the root level of this repository and run ```sh ./patchapp.sh cracked.ipa ./DYLIBS``` 15 | 6. You will see an output file called "cracked-patch.ipa". Use Cydia Impactor (http://www.cydiaimpactor.com) to sign the new and install it into your device. 16 | 17 | #### This repository was inspired by the work of https://github.com/bamtan/PokemonGoAnywhere 18 | 19 | -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign/IRTextFieldDrag.m: -------------------------------------------------------------------------------- 1 | // 2 | // IRTextFieldDrag.m 3 | // iReSign 4 | // 5 | // Created by Esteban Bouza on 01/12/12. 6 | // 7 | 8 | #import "IRTextFieldDrag.h" 9 | 10 | @implementation IRTextFieldDrag 11 | 12 | - (void)awakeFromNib { 13 | [self registerForDraggedTypes:@[NSFilenamesPboardType]]; 14 | } 15 | 16 | - (BOOL)performDragOperation:(id )sender 17 | { 18 | NSPasteboard *pboard = [sender draggingPasteboard]; 19 | 20 | if ( [[pboard types] containsObject:NSURLPboardType] ) { 21 | NSArray *files = [pboard propertyListForType:NSFilenamesPboardType]; 22 | if (files.count <= 0) { 23 | return NO; 24 | } 25 | self.stringValue = [files objectAtIndex:0]; 26 | 27 | } 28 | return YES; 29 | } 30 | 31 | 32 | // Source: http://www.cocoabuilder.com/archive/cocoa/11014-dnd-for-nstextfields-drag-drop.html 33 | - (NSDragOperation)draggingEntered:(id )sender { 34 | 35 | if (!self.isEnabled) return NSDragOperationNone; 36 | 37 | NSPasteboard *pboard; 38 | NSDragOperation sourceDragMask; 39 | 40 | sourceDragMask = [sender draggingSourceOperationMask]; 41 | pboard = [sender draggingPasteboard]; 42 | 43 | if ( [[pboard types] containsObject:NSColorPboardType] ) { 44 | if (sourceDragMask & NSDragOperationCopy) { 45 | return NSDragOperationCopy; 46 | } 47 | } 48 | if ( [[pboard types] containsObject:NSFilenamesPboardType] ) { 49 | if (sourceDragMask & NSDragOperationCopy) { 50 | return NSDragOperationCopy; 51 | } 52 | } 53 | 54 | return NSDragOperationNone; 55 | } 56 | 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign.xcodeproj/project.xcworkspace/xcshareddata/iReSign.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 55C76783-2CEC-4A8B-BA32-DF9DAB6B90C5 9 | IDESourceControlProjectName 10 | iReSign 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 43280492CE74ADAC7B7AD1393F0FB52CA2FE257C 14 | github.com:maciekish/iReSign.git 15 | 16 | IDESourceControlProjectPath 17 | iReSign/iReSign.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 43280492CE74ADAC7B7AD1393F0FB52CA2FE257C 21 | ../../.. 22 | 23 | IDESourceControlProjectURL 24 | github.com:maciekish/iReSign.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 43280492CE74ADAC7B7AD1393F0FB52CA2FE257C 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 43280492CE74ADAC7B7AD1393F0FB52CA2FE257C 36 | IDESourceControlWCCName 37 | iReSign 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /iReSign/README.md: -------------------------------------------------------------------------------- 1 | iReSign 2 | ======= 3 | 4 | iReSign allows iDevice app bundles (.ipa) files to be signed or resigned with a digital certificate from Apple for distribution. It can also create signed iDevice app bundles (.ipa) files from .xcarchive files. This tool is aimed at enterprises users, for enterprise deployment, when the person signing the app is different than the person(s) developing it. 5 | 6 | How to use 7 | ======= 8 | 9 | iReSign allows you to re-sign any unencrypted ipa-file with any certificate for which you hold the corresponding private key. iResign can also created a signed ipa-file from an xcarchive file. 10 | 11 | 1. Drag your unsigned .ipa or .xcarchive file to the top box, or use the browse button. 12 | 13 | 2. Enter your full certificate name from Keychain Access, for example "iPhone Developer: Firstname Lastname (XXXXXXXXXX)" in the bottom box. 14 | 15 | 3. Click ReSign! and wait. The resigned file will be saved in the same folder as the original file. 16 | 17 | License 18 | ======= 19 | 20 | Copyright (c) 2014 Maciej Swic 21 | 22 | Permission is hereby granted, free of charge, to any person obtaining a copy 23 | of this software and associated documentation files (the "Software"), to deal 24 | in the Software without restriction, including without limitation the rights 25 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 26 | copies of the Software, and to permit persons to whom the Software is 27 | furnished to do so, subject to the following conditions: 28 | 29 | The above copyright notice and this permission notice shall be included in 30 | all copies or substantial portions of the Software. 31 | 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 33 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 34 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 35 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 36 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 37 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 38 | THE SOFTWARE. -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign/iReSignAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // iReSignAppDelegate.h 3 | // iReSign 4 | // 5 | // Created by Maciej Swic on 2011-05-16. 6 | // Copyright (c) 2011 Maciej Swic, Licensed under the MIT License. 7 | // See README.md for details 8 | // 9 | 10 | #import 11 | #import "IRTextFieldDrag.h" 12 | 13 | @interface iReSignAppDelegate : NSObject { 14 | @private 15 | NSWindow *__unsafe_unretained window; 16 | 17 | NSUserDefaults *defaults; 18 | 19 | NSTask *unzipTask; 20 | NSTask *copyTask; 21 | NSTask *provisioningTask; 22 | NSTask *codesignTask; 23 | NSTask *generateEntitlementsTask; 24 | NSTask *verifyTask; 25 | NSTask *zipTask; 26 | NSString *sourcePath; 27 | NSString *appPath; 28 | NSString *frameworksDirPath; 29 | NSString *frameworkPath; 30 | NSString *workingPath; 31 | NSString *appName; 32 | NSString *fileName; 33 | 34 | NSString *entitlementsResult; 35 | NSString *codesigningResult; 36 | NSString *verificationResult; 37 | 38 | NSMutableArray *frameworks; 39 | Boolean hasFrameworks; 40 | 41 | IBOutlet IRTextFieldDrag *pathField; 42 | IBOutlet IRTextFieldDrag *provisioningPathField; 43 | IBOutlet IRTextFieldDrag *entitlementField; 44 | IBOutlet IRTextFieldDrag *bundleIDField; 45 | IBOutlet NSButton *browseButton; 46 | IBOutlet NSButton *provisioningBrowseButton; 47 | IBOutlet NSButton *entitlementBrowseButton; 48 | IBOutlet NSButton *resignButton; 49 | IBOutlet NSTextField *statusLabel; 50 | IBOutlet NSProgressIndicator *flurry; 51 | IBOutlet NSButton *changeBundleIDCheckbox; 52 | 53 | IBOutlet NSComboBox *certComboBox; 54 | NSMutableArray *certComboBoxItems; 55 | NSTask *certTask; 56 | NSArray *getCertsResult; 57 | 58 | } 59 | 60 | @property (unsafe_unretained) IBOutlet NSWindow *window; 61 | 62 | @property (nonatomic, strong) NSString *workingPath; 63 | 64 | - (IBAction)resign:(id)sender; 65 | - (IBAction)browse:(id)sender; 66 | - (IBAction)provisioningBrowse:(id)sender; 67 | - (IBAction)entitlementBrowse:(id)sender; 68 | - (IBAction)changeBundleIDPressed:(id)sender; 69 | 70 | - (void)checkUnzip:(NSTimer *)timer; 71 | - (void)checkCopy:(NSTimer *)timer; 72 | - (void)doProvisioning; 73 | - (void)checkProvisioning:(NSTimer *)timer; 74 | - (void)doCodeSigning; 75 | - (void)checkCodesigning:(NSTimer *)timer; 76 | - (void)doVerifySignature; 77 | - (void)checkVerificationProcess:(NSTimer *)timer; 78 | - (void)doZip; 79 | - (void)checkZip:(NSTimer *)timer; 80 | - (void)disableControls; 81 | - (void)enableControls; 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # HackingFacebook 3 | 4 | Bypassing Facebook for iOS's SSL Pinning, allow us to capture decrypted HTTPS request send from Facebook, with tools like [Charles](https://www.charlesproxy.com/). 5 | 6 | ![Screen Shot](https://raw.githubusercontent.com/Naituw/HackingFacebook/master/screenshot.png) 7 | 8 | ## Description 9 | 10 | This repository shows how to kill the certificate pinning in [Facebook for iOS](https://itunes.apple.com/cn/app/facebook/id284882215?mt=8) **without** Jailbreak your device. 11 | 12 | I've successfully captured decrypted https requests from Facebook with Charles by apply this patch. I tested the currently newest Facebook for iOS version 79.0, this patch may become invalid with newer version. 13 | 14 | ## About 15 | - Inspired by https://github.com/nabla-c0d3/ssl-kill-switch2, https://github.com/nabla-c0d3/ssl-kill-switch2/issues/13 16 | - Contents of `DyldPatcher` is created by [depoon](https://github.com/depoon), the original repo is https://github.com/depoon/iOSDylibInjectionDemo, and an article about this https://medium.com/@kennethpoon/how-to-perform-ios-code-injection-on-ipa-files-1ba91d9438db#.mwx82zyds 17 | - The `iResign` is modified version of https://github.com/maciekish/iReSign, created by Maciej Swic. I added support for injected libraries. 18 | - The Aspects library is created by Peter Steinberger, licensed under MIT, the original repo is https://github.com/steipete/Aspects 19 | 20 | ## Instructions 21 | 22 | **Update 20170319** 23 | 24 | I've developed a new tool to finish this steps in more simple way, see: 25 | 26 | https://github.com/Naituw/IPAPatch 27 | 28 | **Original Instructions** 29 | 30 | 1. Prepare `Facebook_extenstion_removed.ipa` 31 | - Get decrypted Facebook ipa, wether from a jailbroken device or ipa download site (I'm using ipa downloaded from http://www.iphonecake.com) 32 | - Unzip ipa, Remove `Payload/Facebook.app/Plugins` folder, which contains App Extensions. 33 | - Zip the `Payload` folder, and rename to `Facebook_extenstion_removed.ipa` 34 | 35 | 2. Inject Code to `Facebook_extenstion_removed.ipa` 36 | - Build `DyldXcodeProject`, make sure the target is selected to real device (NOT iPhone Simulators), copy the result framework's binary file to a folder named `DyldsForInjection` 37 | - Use the script provide in `DyldPatcher`, patch the binary we generated, to `Facebook_extenstion_removed.ipa`, the patched file is named `Facebook_extenstion_removed-patched.ipa` 38 | 39 | cd DyldPatcher 40 | ./patchapp.sh Facebook_extenstion_removed.ipa DyldsForInjection 41 | 42 | 3. Resign `Facebook_extenstion_removed-patched.ipa` 43 | - Use the modified version of `iResign` to resign the file, the result file is `Facebook_extenstion_removed-patched-resigned.ipa`, this version will sign the dyld we injected correctly. 44 | 45 | 4. Install and Run 46 | - Install `Facebook_extenstion_removed-patched-resigned.ipa` via Xcode 47 | - Capture HTTPS requests like other apps with `Charles`! 48 | 49 | -------------------------------------------------------------------------------- /DyldPatcher/patchapp.sh: -------------------------------------------------------------------------------- 1 | 2 | OPTOOL="./optool" 3 | 4 | IPA=$1 5 | DYLIB_FOLDER=$2 6 | TMPDIR=".patchapp.cache" 7 | 8 | # 9 | # Usage / syntax 10 | # 11 | function usage { 12 | if [ "$2" == "" -o "$1" == "" ]; then 13 | cat < [/path/to/your/file.mobileprovision]" 15 | Where 'command' is one of:" 16 | info - Show the information required to create a Provisioning Profile 17 | that matches the specified .ipa file 18 | patch - Inject the current Theos tweak into the specified .ipa file. 19 | Requires that you specify a .mobileprovision file. 20 | 21 | USAGE 22 | fi 23 | } 24 | 25 | # 26 | # 27 | # 28 | 29 | function copy_dylib_and_load { 30 | 31 | 32 | # copy the files into the .app folder 33 | echo '[+] Copying .dylib dependences into "'$TMPDIR/Payload/$APP'"' 34 | cp -rf $DYLIB_FOLDER $TMPDIR/Payload/$APP/Dylibs 35 | 36 | # re-sign Frameworks, too 37 | echo "APPDIR=$APPDIR" 38 | for file in `ls -1 $APPDIR/Dylibs`; do 39 | echo -n ' ' 40 | echo "Install Load: $file -> @executable_path/Dylibs/$file" 41 | $OPTOOL install -c load -p "@executable_path/Dylibs/$file" -t $APPDIR/$APP_BINARY >& /dev/null 42 | done 43 | 44 | 45 | #-------- 46 | 47 | if [ "$?" != "0" ]; then 48 | echo "Failed to inject "${DYLIB##*/}" into $APPDIR/${APP_BINARY}. Can I interest you in debugging the problem?" 49 | exit 1 50 | fi 51 | chmod +x "$APPDIR/$APP_BINARY" 52 | } 53 | 54 | 55 | 56 | # 57 | # Setup all the things. 58 | # 59 | function setup_environment { 60 | if [ "$IPA" == "" ]; then 61 | usage 62 | exit 1 63 | fi 64 | if [ ! -r "$IPA" ]; then 65 | echo "$IPA not found or not readable" 66 | exit 1 67 | fi 68 | 69 | # setup 70 | rm -rf "$TMPDIR" >/dev/null 2>&1 71 | mkdir "$TMPDIR" 72 | SAVED_PATH=`pwd` 73 | 74 | # uncompress the IPA into tmpdir 75 | echo '[+] Unpacking the .ipa file ('"`pwd`/$IPA"')...' 76 | unzip -o -d "$TMPDIR" "$IPA" >/dev/null 2>&1 77 | if [ "$?" != "0" ]; then 78 | echo "Couldn't unzip the IPA file." 79 | exit 1 80 | fi 81 | 82 | cd "$TMPDIR" 83 | cd Payload/*.app 84 | if [ "$?" != "0" ]; then 85 | echo "Couldn't change into Payload folder. Wat." 86 | exit 1 87 | fi 88 | APP=`pwd` 89 | APP=${APP##*/} 90 | APPDIR=$TMPDIR/Payload/$APP 91 | cd "$SAVED_PATH" 92 | BUNDLE_ID=`plutil -convert xml1 -o - $APPDIR/Info.plist|grep -A1 CFBundleIdentifier|tail -n1|cut -f2 -d\>|cut -f1 -d\<`-patched 93 | APP_BINARY=`plutil -convert xml1 -o - $APPDIR/Info.plist|grep -A1 Exec|tail -n1|cut -f2 -d\>|cut -f1 -d\<` 94 | 95 | } 96 | 97 | 98 | # 99 | # Zip folder back into Ipa 100 | # 101 | function repack_ipa { 102 | echo '[+] Repacking the .ipa' 103 | rm -f "${IPA%*.ipa}-patched.ipa" >/dev/null 2>&1 104 | zip -9r "${IPA%*.ipa}-patched.ipa" Payload/ >/dev/null 2>&1 105 | if [ "$?" != "0" ]; then 106 | echo "Failed to compress the app into an .ipa file." 107 | exit 1 108 | fi 109 | IPA=${IPA#../*} 110 | mv "${IPA%*.ipa}-patched.ipa" .. 111 | echo "[+] Wrote \"${IPA%*.ipa}-patched.ipa\"" 112 | echo "[+] Great success!" 113 | } 114 | 115 | # 116 | # Inject the current Theos tweak into the specified .ipa file 117 | # 118 | function ipa_patch { 119 | 120 | setup_environment 121 | 122 | 123 | if [ ! -x "$OPTOOL" ]; then 124 | echo "You need to install optool from here: https://github.com/alexzielenski/optool" 125 | echo "Then update OPTOOL variable in '$0' to reflect the correct path to the optool binary." 126 | exit 1 127 | fi 128 | 129 | copy_dylib_and_load 130 | 131 | cd $TMPDIR 132 | 133 | repack_ipa 134 | 135 | cd - >/dev/null 2>&1 136 | } 137 | 138 | 139 | 140 | ipa_patch 141 | # success! 142 | exit 0 143 | 144 | -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebook/Aspect/Aspects.h: -------------------------------------------------------------------------------- 1 | // 2 | // Aspects.h 3 | // Aspects - A delightful, simple library for aspect oriented programming. 4 | // 5 | // Copyright (c) 2014 Peter Steinberger. Licensed under the MIT license. 6 | // 7 | 8 | #import 9 | 10 | typedef NS_OPTIONS(NSUInteger, AspectOptions) { 11 | AspectPositionAfter = 0, /// Called after the original implementation (default) 12 | AspectPositionInstead = 1, /// Will replace the original implementation. 13 | AspectPositionBefore = 2, /// Called before the original implementation. 14 | 15 | AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution. 16 | }; 17 | 18 | /// Opaque Aspect Token that allows to deregister the hook. 19 | @protocol AspectToken 20 | 21 | /// Deregisters an aspect. 22 | /// @return YES if deregistration is successful, otherwise NO. 23 | - (BOOL)remove; 24 | 25 | @end 26 | 27 | /// The AspectInfo protocol is the first parameter of our block syntax. 28 | @protocol AspectInfo 29 | 30 | /// The instance that is currently hooked. 31 | - (id)instance; 32 | 33 | /// The original invocation of the hooked method. 34 | - (NSInvocation *)originalInvocation; 35 | 36 | /// All method arguments, boxed. This is lazily evaluated. 37 | - (NSArray *)arguments; 38 | 39 | @end 40 | 41 | /** 42 | Aspects uses Objective-C message forwarding to hook into messages. This will create some overhead. Don't add aspects to methods that are called a lot. Aspects is meant for view/controller code that is not called a 1000 times per second. 43 | 44 | Adding aspects returns an opaque token which can be used to deregister again. All calls are thread safe. 45 | */ 46 | @interface NSObject (Aspects) 47 | 48 | /// Adds a block of code before/instead/after the current `selector` for a specific class. 49 | /// 50 | /// @param block Aspects replicates the type signature of the method being hooked. 51 | /// The first parameter will be `id`, followed by all parameters of the method. 52 | /// These parameters are optional and will be filled to match the block signature. 53 | /// You can even use an empty block, or one that simple gets `id`. 54 | /// 55 | /// @note Hooking static methods is not supported. 56 | /// @return A token which allows to later deregister the aspect. 57 | + (id)aspect_hookSelector:(SEL)selector 58 | withOptions:(AspectOptions)options 59 | usingBlock:(id)block 60 | error:(NSError **)error; 61 | 62 | /// Adds a block of code before/instead/after the current `selector` for a specific instance. 63 | - (id)aspect_hookSelector:(SEL)selector 64 | withOptions:(AspectOptions)options 65 | usingBlock:(id)block 66 | error:(NSError **)error; 67 | 68 | @end 69 | 70 | 71 | typedef NS_ENUM(NSUInteger, AspectErrorCode) { 72 | AspectErrorSelectorBlacklisted, /// Selectors like release, retain, autorelease are blacklisted. 73 | AspectErrorDoesNotRespondToSelector, /// Selector could not be found. 74 | AspectErrorSelectorDeallocPosition, /// When hooking dealloc, only AspectPositionBefore is allowed. 75 | AspectErrorSelectorAlreadyHookedInClassHierarchy, /// Statically hooking the same method in subclasses is not allowed. 76 | AspectErrorFailedToAllocateClassPair, /// The runtime failed creating a class pair. 77 | AspectErrorMissingBlockSignature, /// The block misses compile time signature info and can't be called. 78 | AspectErrorIncompatibleBlockSignature, /// The block signature does not match the method or is too large. 79 | 80 | AspectErrorRemoveObjectAlreadyDeallocated = 100 /// (for removing) The object hooked is already deallocated. 81 | }; 82 | 83 | extern NSString *const AspectErrorDomain; 84 | -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebook.xcodeproj/xcuserdata/wutian.xcuserdatad/xcschemes/HackingFacebook.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebook/HackingEntry.m: -------------------------------------------------------------------------------- 1 | // 2 | // HackingEntry.m 3 | // HackingFacebook 4 | // 5 | // Created by wutian on 2017/2/14. 6 | // Copyright © 2017年 Weibo. All rights reserved. 7 | // 8 | 9 | #import "HackingEntry.h" 10 | #import "Aspects.h" 11 | #import "FBSSLPinningVerifier.h" 12 | #import "FBLigerConfig.h" 13 | 14 | #import 15 | 16 | static void SafeLog(NSString * string); 17 | 18 | @implementation HackingEntry 19 | 20 | + (instancetype)sharedEntryPoint 21 | { 22 | static HackingEntry * instance = nil; 23 | static dispatch_once_t onceToken; 24 | dispatch_once(&onceToken, ^{ 25 | instance = [HackingEntry new]; 26 | }); 27 | return instance; 28 | } 29 | 30 | - (void)run 31 | { 32 | NSLog(@"+[HackingEntry run]"); 33 | 34 | { 35 | // Disable the liger network engine, After this, Facebook will fallback to use FBSSLPinningVerifier to do the pinning 36 | 37 | { 38 | // Find FBLigerConfig's init method, this method changes frequently across versions, so we find the longest method to hook 39 | 40 | Class ligerClass = NSClassFromString(@"FBLigerConfig"); 41 | 42 | SEL longestSelector = @selector(init); 43 | 44 | unsigned int mc = 0; 45 | Method * mlist = class_copyMethodList(ligerClass, &mc); 46 | for(int i = 0; i < mc; i++) { 47 | SEL selector = method_getName(mlist[i]); 48 | NSString * selectorName = NSStringFromSelector(selector); 49 | if (selectorName.length > NSStringFromSelector(longestSelector).length) { 50 | longestSelector = selector; 51 | } 52 | NSLog(@"FBLigerConfig find selector: %@", selectorName); 53 | } 54 | 55 | SafeLog([NSString stringWithFormat:@"FBLigerConfig longestSelector is: %@", NSStringFromSelector(longestSelector)]); 56 | 57 | NSError * error = NULL; 58 | [ligerClass aspect_hookSelector:longestSelector withOptions:AspectPositionInstead usingBlock:^(id aspectInfo) { 59 | NSArray * variableNames = [NSStringFromSelector(longestSelector) componentsSeparatedByString:@":"]; 60 | if ([variableNames.lastObject isEqual:@""]) { 61 | variableNames = [variableNames subarrayWithRange:NSMakeRange(0, variableNames.count - 1)]; 62 | } 63 | NSArray * arguments = [aspectInfo arguments]; 64 | if (arguments.count == variableNames.count) { 65 | NSMutableDictionary * mapped = [NSMutableDictionary dictionary]; 66 | [variableNames enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 67 | mapped[obj] = arguments[idx]; 68 | }]; 69 | SafeLog([NSString stringWithFormat:@"FBLigerConfig init-mapped: %@", mapped]); 70 | } 71 | SafeLog([NSString stringWithFormat:@"FBLigerConfig init: %@", arguments]); 72 | 73 | // The prefix is - (id)initConfigWithLigerEnabled:(_Bool)arg1 allRequestsEnabled... 74 | // So we just change the first argument to "NO" 75 | 76 | NSInvocation *invocation = aspectInfo.originalInvocation; 77 | BOOL ligerEnabled = NO; 78 | [invocation setArgument:&ligerEnabled atIndex:2]; 79 | 80 | [invocation invoke]; 81 | 82 | } error:&error]; 83 | if (error) { 84 | NSLog(@"Error Hacking Class FBLigerConfig, Error: %@", error); 85 | } 86 | } 87 | 88 | 89 | // Kill SSL Pinning for FBSSLPinningVerifier 90 | 91 | [NSClassFromString(@"FBSSLPinningVerifier") aspect_hookSelector:@selector(checkPinning:) withOptions:AspectPositionInstead usingBlock:^(id info) { 92 | 93 | NSLog(@"Check Pinnning Called: %@", [info arguments]); 94 | 95 | BOOL success; 96 | NSInvocation *invocation = info.originalInvocation; 97 | [invocation invoke]; 98 | [invocation getReturnValue:&success]; 99 | 100 | if (!success) { 101 | 102 | NSLog(@"Check Pinning Failed, Change to Success"); 103 | 104 | success = YES; 105 | [invocation setReturnValue:&success]; 106 | } 107 | 108 | } error:NULL]; 109 | } 110 | } 111 | 112 | @end 113 | 114 | __attribute__((constructor)) 115 | void WBTEntryPointMain() { 116 | NSLog(@"WBTEntryPointMain()"); 117 | 118 | dispatch_async(dispatch_get_main_queue(), ^{ 119 | [[HackingEntry sharedEntryPoint] run]; 120 | }); 121 | } 122 | 123 | static void SafeLog(NSString * string) { 124 | NSMutableArray * logs = [NSMutableArray array]; 125 | NSString * remain = string; 126 | while (remain.length > 500) { 127 | [logs addObject:[remain substringToIndex:500]]; 128 | remain = [remain substringFromIndex:500]; 129 | } 130 | [logs addObject:remain]; 131 | NSUInteger count = logs.count; 132 | 133 | [logs enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 134 | NSLog(@"(%zd/%zd) %@", idx + 1, count, obj); 135 | }]; 136 | } 137 | 138 | -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebook/ClassDumpHeaders/FBLigerConfig.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by class-dump 3.5 (64 bit). 3 | // 4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard. 5 | // 6 | 7 | #import 8 | 9 | @class NSArray, NSString; 10 | 11 | @interface FBLigerConfig : NSObject 12 | { 13 | _Bool _ligerEnabled; 14 | _Bool _allRequestsEnabled; 15 | _Bool _dnsCacheEnabled; 16 | _Bool _requestProcessLogEnabled; 17 | _Bool _cAresEnabled; 18 | _Bool _persistentSSLCacheEnabled; 19 | _Bool _crossDomainSSLCacheEnabled; 20 | _Bool _httpSessionManagerOriginLimitEnabled; 21 | _Bool _limitVideoDomain; 22 | _Bool _heV4PrefEnabled; 23 | _Bool _tcpPingEnabled; 24 | _Bool _tcpPingCancelOnReply; 25 | _Bool _tcpPingAdaptiveTimeout; 26 | _Bool _tcpReuseEnabled; 27 | _Bool _sslCertKeyLengthVerificationEnforced; 28 | _Bool _sslTimestampVerificationEnabled; 29 | _Bool _zeroProtocolEnabled; 30 | _Bool _zeroProtocolEnforceExpiration; 31 | _Bool _zeroProtocolPersistentCache; 32 | _Bool _zeroRetryEnabled; 33 | _Bool _radioMonitorEnabled; 34 | _Bool _altSvcEnabled; 35 | _Bool _flowControlEnabled; 36 | _Bool _enableCachingPushManager; 37 | int _dnsRequestsOutstanding; 38 | int _persistentSSLCacheCapacity; 39 | int _persistentSSLCacheSyncInterval; 40 | int _zeroTlsFallback; 41 | int _http2PriLevels; 42 | NSString *_dnsFallbackAnswers; 43 | NSString *_persistentSSLCacheFilename; 44 | long long _httpSessionManagerMaxIdleHTTPSessions; 45 | long long _httpSessionManagerMaxIdleSPDYSessions; 46 | long long _httpSessionManagerBackupTimeout; 47 | long long _httpSessionManagerOriginMaxRetries; 48 | long long _tcpPingTimeout; 49 | long long _tcpPingRetries; 50 | unsigned long long _tcpPingRttThreshold; 51 | unsigned long long _tcpInfoCacheTime; 52 | // id _ligerNetworkStatusMonitor; 53 | NSArray *_requestObservers; 54 | // id _ligerConnectionLogger; 55 | // CDUnknownBlockType _proxyUserAgentFormattingBlock; 56 | NSString *_zeroProtocolAeads; 57 | NSString *_zeroProtocolHostnamePolicy; 58 | NSString *_http2PriReqCallPaths; 59 | NSString *_http2PriWeights; 60 | long long _flowControlWindowSize; 61 | NSArray *_pushNotificationCallbacks; 62 | } 63 | 64 | @property(readonly, copy, nonatomic) NSArray *pushNotificationCallbacks; // @synthesize pushNotificationCallbacks=_pushNotificationCallbacks; 65 | @property(readonly, nonatomic) _Bool enableCachingPushManager; // @synthesize enableCachingPushManager=_enableCachingPushManager; 66 | @property(readonly, nonatomic) long long flowControlWindowSize; // @synthesize flowControlWindowSize=_flowControlWindowSize; 67 | @property(readonly, nonatomic) _Bool flowControlEnabled; // @synthesize flowControlEnabled=_flowControlEnabled; 68 | @property(readonly, nonatomic) _Bool altSvcEnabled; // @synthesize altSvcEnabled=_altSvcEnabled; 69 | @property(readonly, nonatomic) _Bool radioMonitorEnabled; // @synthesize radioMonitorEnabled=_radioMonitorEnabled; 70 | @property(readonly, copy, nonatomic) NSString *http2PriWeights; // @synthesize http2PriWeights=_http2PriWeights; 71 | @property(readonly, copy, nonatomic) NSString *http2PriReqCallPaths; // @synthesize http2PriReqCallPaths=_http2PriReqCallPaths; 72 | @property(readonly, nonatomic) int http2PriLevels; // @synthesize http2PriLevels=_http2PriLevels; 73 | @property(readonly, nonatomic) int zeroTlsFallback; // @synthesize zeroTlsFallback=_zeroTlsFallback; 74 | @property(readonly, nonatomic) _Bool zeroRetryEnabled; // @synthesize zeroRetryEnabled=_zeroRetryEnabled; 75 | @property(readonly, copy, nonatomic) NSString *zeroProtocolHostnamePolicy; // @synthesize zeroProtocolHostnamePolicy=_zeroProtocolHostnamePolicy; 76 | @property(readonly, copy, nonatomic) NSString *zeroProtocolAeads; // @synthesize zeroProtocolAeads=_zeroProtocolAeads; 77 | @property(readonly, nonatomic) _Bool zeroProtocolPersistentCache; // @synthesize zeroProtocolPersistentCache=_zeroProtocolPersistentCache; 78 | @property(readonly, nonatomic) _Bool zeroProtocolEnforceExpiration; // @synthesize zeroProtocolEnforceExpiration=_zeroProtocolEnforceExpiration; 79 | @property(readonly, nonatomic) _Bool zeroProtocolEnabled; // @synthesize zeroProtocolEnabled=_zeroProtocolEnabled; 80 | @property(readonly, nonatomic) _Bool sslTimestampVerificationEnabled; // @synthesize sslTimestampVerificationEnabled=_sslTimestampVerificationEnabled; 81 | @property(readonly, nonatomic) _Bool sslCertKeyLengthVerificationEnforced; // @synthesize sslCertKeyLengthVerificationEnforced=_sslCertKeyLengthVerificationEnforced; 82 | //@property(readonly, copy, nonatomic) CDUnknownBlockType proxyUserAgentFormattingBlock; // @synthesize proxyUserAgentFormattingBlock=_proxyUserAgentFormattingBlock; 83 | //@property(nonatomic) id ligerConnectionLogger; // @synthesize ligerConnectionLogger=_ligerConnectionLogger; 84 | @property(copy, nonatomic) NSArray *requestObservers; // @synthesize requestObservers=_requestObservers; 85 | //@property(nonatomic) id ligerNetworkStatusMonitor; // @synthesize ligerNetworkStatusMonitor=_ligerNetworkStatusMonitor; 86 | @property(readonly, nonatomic) _Bool tcpReuseEnabled; // @synthesize tcpReuseEnabled=_tcpReuseEnabled; 87 | @property(readonly, nonatomic) unsigned long long tcpInfoCacheTime; // @synthesize tcpInfoCacheTime=_tcpInfoCacheTime; 88 | @property(readonly, nonatomic) _Bool tcpPingAdaptiveTimeout; // @synthesize tcpPingAdaptiveTimeout=_tcpPingAdaptiveTimeout; 89 | @property(readonly, nonatomic) unsigned long long tcpPingRttThreshold; // @synthesize tcpPingRttThreshold=_tcpPingRttThreshold; 90 | @property(readonly, nonatomic) _Bool tcpPingCancelOnReply; // @synthesize tcpPingCancelOnReply=_tcpPingCancelOnReply; 91 | @property(readonly, nonatomic) long long tcpPingRetries; // @synthesize tcpPingRetries=_tcpPingRetries; 92 | @property(readonly, nonatomic) long long tcpPingTimeout; // @synthesize tcpPingTimeout=_tcpPingTimeout; 93 | @property(readonly, nonatomic) _Bool tcpPingEnabled; // @synthesize tcpPingEnabled=_tcpPingEnabled; 94 | @property(readonly, nonatomic) _Bool heV4PrefEnabled; // @synthesize heV4PrefEnabled=_heV4PrefEnabled; 95 | @property(readonly, nonatomic) _Bool limitVideoDomain; // @synthesize limitVideoDomain=_limitVideoDomain; 96 | @property(readonly, nonatomic) long long httpSessionManagerOriginMaxRetries; // @synthesize httpSessionManagerOriginMaxRetries=_httpSessionManagerOriginMaxRetries; 97 | @property(readonly, nonatomic) _Bool httpSessionManagerOriginLimitEnabled; // @synthesize httpSessionManagerOriginLimitEnabled=_httpSessionManagerOriginLimitEnabled; 98 | @property(readonly, nonatomic) long long httpSessionManagerBackupTimeout; // @synthesize httpSessionManagerBackupTimeout=_httpSessionManagerBackupTimeout; 99 | @property(readonly, nonatomic) long long httpSessionManagerMaxIdleSPDYSessions; // @synthesize httpSessionManagerMaxIdleSPDYSessions=_httpSessionManagerMaxIdleSPDYSessions; 100 | @property(readonly, nonatomic) long long httpSessionManagerMaxIdleHTTPSessions; // @synthesize httpSessionManagerMaxIdleHTTPSessions=_httpSessionManagerMaxIdleHTTPSessions; 101 | @property(readonly, nonatomic) _Bool crossDomainSSLCacheEnabled; // @synthesize crossDomainSSLCacheEnabled=_crossDomainSSLCacheEnabled; 102 | @property(readonly, nonatomic) int persistentSSLCacheSyncInterval; // @synthesize persistentSSLCacheSyncInterval=_persistentSSLCacheSyncInterval; 103 | @property(readonly, nonatomic) int persistentSSLCacheCapacity; // @synthesize persistentSSLCacheCapacity=_persistentSSLCacheCapacity; 104 | @property(copy, nonatomic) NSString *persistentSSLCacheFilename; // @synthesize persistentSSLCacheFilename=_persistentSSLCacheFilename; 105 | @property(readonly, nonatomic) _Bool persistentSSLCacheEnabled; // @synthesize persistentSSLCacheEnabled=_persistentSSLCacheEnabled; 106 | @property(readonly, copy, nonatomic) NSString *dnsFallbackAnswers; // @synthesize dnsFallbackAnswers=_dnsFallbackAnswers; 107 | @property(readonly, nonatomic) int dnsRequestsOutstanding; // @synthesize dnsRequestsOutstanding=_dnsRequestsOutstanding; 108 | @property(readonly, nonatomic) _Bool cAresEnabled; // @synthesize cAresEnabled=_cAresEnabled; 109 | @property(readonly, nonatomic) _Bool requestProcessLogEnabled; // @synthesize requestProcessLogEnabled=_requestProcessLogEnabled; 110 | @property(readonly, nonatomic) _Bool dnsCacheEnabled; // @synthesize dnsCacheEnabled=_dnsCacheEnabled; 111 | @property(readonly, nonatomic) _Bool allRequestsEnabled; // @synthesize allRequestsEnabled=_allRequestsEnabled; 112 | @property(readonly, nonatomic) _Bool ligerEnabled; // @synthesize ligerEnabled=_ligerEnabled; 113 | - (_Bool)isEqualToFBLigerConfig:(id)arg1; 114 | - (id)initDefaultConfigWithProxyUserAgentFormattingBlock:(id)arg1; 115 | - (id)initConfigWithLigerEnabled:(_Bool)arg1 allRequestsEnabled:(_Bool)arg2 dnsCacheEnabled:(_Bool)arg3 requestProcessLogEnabled:(_Bool)arg4 cAresEnabled:(_Bool)arg5 dnsRequestsOutstanding:(int)arg6 dnsFallbackAnswers:(id)arg7 persistentSSLCacheEnabled:(_Bool)arg8 persistentSSLCacheFilename:(id)arg9 persistentSSLCacheCapacity:(int)arg10 persistentSSLCacheSyncInterval:(int)arg11 crossDomainSSLCacheEnabled:(_Bool)arg12 httpSessionManagerMaxIdleHTTPSessions:(long long)arg13 httpSessionManagerMaxIdleSPDYSessions:(long long)arg14 httpSessionManagerBackupTimeout:(long long)arg15 httpSessionManagerOriginLimitEnabled:(_Bool)arg16 httpSessionManagerOriginMaxRetries:(long long)arg17 limitVideoDomain:(_Bool)arg18 heV4PrefEnabled:(_Bool)arg19 tcpPingEnabled:(_Bool)arg20 tcpPingRetries:(long long)arg21 tcpPingTimeout:(long long)arg22 tcpPingCancelOnReply:(_Bool)arg23 tcpPingRttThreshold:(unsigned long long)arg24 tcpPingAdaptiveTimeout:(_Bool)arg25 tcpInfoCacheTime:(unsigned long long)arg26 tcpReuseEnabled:(_Bool)arg27 ligerNetworkStatusMonitor:(id)arg28 requestObservers:(id)arg29 ligerConnectionLogger:(id)arg30 sslCertKeyLengthVerificationEnforced:(_Bool)arg31 sslTimestampVerificationEnabled:(_Bool)arg32 zeroProtocolEnabled:(_Bool)arg33 zeroProtocolEnforceExpiration:(_Bool)arg34 zeroProtocolPersistentCache:(_Bool)arg35 zeroProtocolAeads:(id)arg36 zeroProtocolHostnamePolicy:(id)arg37 zeroRetryEnabled:(_Bool)arg38 zeroTlsFallback:(int)arg39 http2PriLevels:(int)arg40 http2PriReqCallPaths:(id)arg41 http2PriWeights:(id)arg42 radioMonitorEnabled:(_Bool)arg43 altSvcEnabled:(_Bool)arg44 flowControlEnabled:(_Bool)arg45 flowControlWindowSize:(long long)arg46 enableCachingPushManager:(_Bool)arg47 pushNotificationCallbacks:(id)arg48 proxyUserAgentFormattingBlock:(id)arg49; 116 | 117 | @end 118 | 119 | -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebook/Aspect/README.md: -------------------------------------------------------------------------------- 1 | Aspects v1.4.2 [![Build Status](https://travis-ci.org/steipete/Aspects.svg?branch=master)](https://travis-ci.org/steipete/Aspects) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 2 | ============== 3 | 4 | A delightful, simple library for aspect oriented programming by [@steipete](http://twitter.com/steipete). 5 | 6 | **Think of Aspects as method swizzling on steroids. It allows you to add code to existing methods per class or per instance**, whilst thinking of the insertion point e.g. before/instead/after. Aspects automatically deals with calling super and is easier to use than regular method swizzling. 7 | 8 | This is stable and used in hundreds of apps since it's part of [PSPDFKit, an iOS PDF framework that ships with apps like Dropbox or Evernote](http://pspdfkit.com), and now I finally made it open source. 9 | 10 | Aspects extends `NSObject` with the following methods: 11 | 12 | ``` objc 13 | /// Adds a block of code before/instead/after the current `selector` for a specific class. 14 | /// 15 | /// @param block Aspects replicates the type signature of the method being hooked. 16 | /// The first parameter will be `id`, followed by all parameters of the method. 17 | /// These parameters are optional and will be filled to match the block signature. 18 | /// You can even use an empty block, or one that simple gets `id`. 19 | /// 20 | /// @note Hooking static methods is not supported. 21 | /// @return A token which allows to later deregister the aspect. 22 | + (id)aspect_hookSelector:(SEL)selector 23 | withOptions:(AspectOptions)options 24 | usingBlock:(id)block 25 | error:(NSError **)error; 26 | 27 | /// Adds a block of code before/instead/after the current `selector` for a specific instance. 28 | - (id)aspect_hookSelector:(SEL)selector 29 | withOptions:(AspectOptions)options 30 | usingBlock:(id)block 31 | error:(NSError **)error; 32 | 33 | /// Deregister an aspect. 34 | /// @return YES if deregistration is successful, otherwise NO. 35 | id aspect = ...; 36 | [aspect remove]; 37 | ``` 38 | 39 | Adding aspects returns an opaque token of type `AspectToken` which can be used to deregister again. All calls are thread-safe. 40 | 41 | Aspects uses Objective-C message forwarding to hook into messages. This will create some overhead. Don't add aspects to methods that are called a lot. Aspects is meant for view/controller code that is not called 1000 times per second. 42 | 43 | Aspects calls and matches block arguments. Blocks without arguments are supported as well. The first block argument will be of type `id`. 44 | 45 | When to use Aspects 46 | ------------------- 47 | Aspect-oriented programming (AOP) is used to encapsulate "cross-cutting" concerns. These are the kind of requirements that *cut-across* many modules in your system, and so cannot be encapsulated using normal object oriented programming. Some examples of these kinds of requirements: 48 | 49 | * *Whenever* a user invokes a method on the service client, security should be checked. 50 | * *Whenever* a user interacts with the store, a genius suggestion should be presented, based on their interaction. 51 | * *All* calls should be logged. 52 | 53 | If we implemented the above requirements using regular OOP there'd be some drawbacks: 54 | 55 | Good OOP says a class should have a single responsibility, however adding on extra *cross-cutting* requirements means a class that is taking on other responsibilites. For example you might have a **StoreClient** that is supposed to be all about making purchases from an online store. Add in some cross-cutting requirements and it might also have to take on the roles of logging, security and recommendations. This is not great because: 56 | 57 | * Our StoreClient is now harder to understand and maintain. 58 | * These cross-cutting requirements are duplicated and spread throughout our app. 59 | 60 | AOP lets us modularize these cross-cutting requirements, and then cleanly identify all of the places they should be applied. As shown in the examples above cross-cutting requirements can be either technical or business focused in nature. 61 | 62 | ## Here are some concrete examples: 63 | 64 | 65 | Aspects can be used to **dynamically add logging** for debug builds only: 66 | 67 | ``` objc 68 | [UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(id aspectInfo, BOOL animated) { 69 | NSLog(@"View Controller %@ will appear animated: %tu", aspectInfo.instance, animated); 70 | } error:NULL]; 71 | ``` 72 | 73 | ------------------- 74 | It can be used to greatly simplify your analytics setup: 75 | https://github.com/orta/ARAnalytics 76 | 77 | ------------------- 78 | You can check if methods are really being called in your test cases: 79 | ``` objc 80 | - (void)testExample { 81 | TestClass *testClass = [TestClass new]; 82 | TestClass *testClass2 = [TestClass new]; 83 | 84 | __block BOOL testCallCalled = NO; 85 | [testClass aspect_hookSelector:@selector(testCall) withOptions:AspectPositionAfter usingBlock:^{ 86 | testCallCalled = YES; 87 | } error:NULL]; 88 | 89 | [testClass2 testCallAndExecuteBlock:^{ 90 | [testClass testCall]; 91 | } error:NULL]; 92 | XCTAssertTrue(testCallCalled, @"Calling testCallAndExecuteBlock must call testCall"); 93 | } 94 | ``` 95 | ------------------- 96 | It can be really useful for debugging. Here I was curious when exactly the tap gesture changed state: 97 | 98 | ``` objc 99 | [_singleTapGesture aspect_hookSelector:@selector(setState:) withOptions:AspectPositionAfter usingBlock:^(id aspectInfo) { 100 | NSLog(@"%@: %@", aspectInfo.instance, aspectInfo.arguments); 101 | } error:NULL]; 102 | ``` 103 | 104 | ------------------- 105 | Another convenient use case is adding handlers for classes that you don't own. I've written it for use in [PSPDFKit](http://pspdfkit.com), where we require notifications when a view controller is being dismissed modally. This includes UIKit view controllers like `MFMailComposeViewController` and `UIImagePickerController`. We could have created subclasses for each of these controllers, but this would be quite a lot of unnecessary code. Aspects gives you a simpler solution for this problem: 106 | 107 | ``` objc 108 | @implementation UIViewController (DismissActionHook) 109 | 110 | // Will add a dismiss action once the controller gets dismissed. 111 | - (void)pspdf_addWillDismissAction:(void (^)(void))action { 112 | PSPDFAssert(action != NULL); 113 | 114 | [self aspect_hookSelector:@selector(viewWillDisappear:) withOptions:AspectPositionAfter usingBlock:^(id aspectInfo) { 115 | if ([aspectInfo.instance isBeingDismissed]) { 116 | action(); 117 | } 118 | } error:NULL]; 119 | } 120 | 121 | @end 122 | ``` 123 | 124 | Debugging 125 | --------- 126 | Aspects identifies itself nicely in the stack trace, so it's easy to see if a method has been hooked: 127 | 128 | 129 | 130 | Using Aspects with non-void return types 131 | ---------------------------------------- 132 | 133 | You can use the invocation object to customize the return value: 134 | 135 | ``` objc 136 | [PSPDFDrawView aspect_hookSelector:@selector(shouldProcessTouches:withEvent:) withOptions:AspectPositionInstead usingBlock:^(id info, NSSet *touches, UIEvent *event) { 137 | // Call original implementation. 138 | BOOL processTouches; 139 | NSInvocation *invocation = info.originalInvocation; 140 | [invocation invoke]; 141 | [invocation getReturnValue:&processTouches]; 142 | 143 | if (processTouches) { 144 | processTouches = pspdf_stylusShouldProcessTouches(touches, event); 145 | [invocation setReturnValue:&processTouches]; 146 | } 147 | } error:NULL]; 148 | ``` 149 | 150 | Installation 151 | ------------ 152 | The simplest option is to use `pod "Aspects"`. 153 | 154 | You can also add the two files `Aspects.h/m` to your project. There are no further requirements. 155 | 156 | Compatibility and Limitations 157 | ----------------------------- 158 | Aspects uses quite some runtime trickery to achieve what it does. You can mostly mix this with regular method swizzling. 159 | 160 | An important limitation is that for class-based hooking, a method can only be hooked once within the subclass hierarchy. [See #2](https://github.com/steipete/Aspects/issues/2) 161 | This does not apply for objects that are hooked. Aspects creates a dynamic subclass here and has full control. 162 | 163 | KVO works if observers are created after your calls `aspect_hookSelector:` It most likely will crash the other way around. Still looking for workarounds here - any help appreciated. 164 | 165 | Because of ugly implementation details on the ObjC runtime, methods that return unions that also contain structs might not work correctly unless this code runs on the arm64 runtime. 166 | 167 | Credits 168 | ------- 169 | The idea to use `_objc_msgForward` and parts of the `NSInvocation` argument selection is from the excellent [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) from the GitHub guys. [This article](http://codeshaker.blogspot.co.at/2012/01/aop-delivered.html) explains how it works under the hood. 170 | 171 | 172 | Supported iOS & SDK Versions 173 | ----------------------------- 174 | 175 | * Aspects requires ARC. 176 | * Aspects is tested with iOS 7+ and OS X 10.7 or higher. 177 | 178 | License 179 | ------- 180 | MIT licensed, Copyright (c) 2014 Peter Steinberger, steipete@gmail.com, [@steipete](http://twitter.com/steipete) 181 | 182 | 183 | Release Notes 184 | ----------------- 185 | 186 | Version 1.4.2 187 | 188 | - Allow to hook different subclasses. 189 | - Smaller tweaks. 190 | 191 | Version 1.4.1 192 | 193 | - Rename error codes. 194 | 195 | Version 1.4.0 196 | 197 | - Add support for block signatures that match method signatures. (thanks to @nickynick) 198 | 199 | Version 1.3.1 200 | 201 | - Add support for OS X 10.7 or higher. (thanks to @ashfurrow) 202 | 203 | Version 1.3.0 204 | 205 | - Add automatic deregistration. 206 | - Checks if the selector exists before trying to hook. 207 | - Improved dealloc hooking. (no more unsafe_unretained needed) 208 | - Better examples. 209 | - Always log errors. 210 | 211 | Version 1.2.0 212 | 213 | - Adds error parameter. 214 | - Improvements in subclassing registration tracking. 215 | 216 | Version 1.1.0 217 | 218 | - Renamed the files from NSObject+Aspects.m/h to just Aspects.m/h. 219 | - Removing now works via calling `remove` on the aspect token. 220 | - Allow hooking dealloc. 221 | - Fixes infinite loop if the same method is hooked for multiple classes. Hooking will only work for one class in the hierarchy. 222 | - Additional checks to prevent things like hooking retain/release/autorelease or forwardInvocation: 223 | - The original implementation of forwardInvocation is now correctly preserved. 224 | - Classes are properly cleaned up and restored to the original state after the last hook is deregistered. 225 | - Lots and lots of new test cases! 226 | 227 | Version 1.0.1 228 | 229 | - Minor tweaks and documentation improvements. 230 | 231 | Version 1.0.0 232 | 233 | - Initial release 234 | -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4C8EAD45185B74B90095DC6B /* IRHelpWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C8EAD44185B74B90095DC6B /* IRHelpWindow.m */; }; 11 | 5B9E4C4C166AB3F7006D9F8F /* IRTextFieldDrag.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B9E4C4B166AB3F7006D9F8F /* IRTextFieldDrag.m */; }; 12 | C115EE2B13815024003B9ACC /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C115EE2A13815024003B9ACC /* Cocoa.framework */; }; 13 | C115EE3513815024003B9ACC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C115EE3313815024003B9ACC /* InfoPlist.strings */; }; 14 | C115EE3813815024003B9ACC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C115EE3713815024003B9ACC /* main.m */; }; 15 | C115EE3E13815024003B9ACC /* iReSignAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C115EE3D13815024003B9ACC /* iReSignAppDelegate.m */; }; 16 | C115EE4113815024003B9ACC /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = C115EE3F13815024003B9ACC /* MainMenu.xib */; }; 17 | C142471F138174E500F5B51F /* ResourceRules.plist in Resources */ = {isa = PBXBuildFile; fileRef = C142471E138174E500F5B51F /* ResourceRules.plist */; }; 18 | C14247301381B75B00F5B51F /* Icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = C142472F1381B75B00F5B51F /* Icon.icns */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 4C8EAD43185B74B90095DC6B /* IRHelpWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IRHelpWindow.h; sourceTree = ""; }; 23 | 4C8EAD44185B74B90095DC6B /* IRHelpWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IRHelpWindow.m; sourceTree = ""; }; 24 | 5B9E4C4A166AB3F7006D9F8F /* IRTextFieldDrag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IRTextFieldDrag.h; sourceTree = ""; }; 25 | 5B9E4C4B166AB3F7006D9F8F /* IRTextFieldDrag.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IRTextFieldDrag.m; sourceTree = ""; }; 26 | C115EE2613815024003B9ACC /* iReSign.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iReSign.app; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | C115EE2A13815024003B9ACC /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 28 | C115EE2D13815024003B9ACC /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 29 | C115EE2E13815024003B9ACC /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 30 | C115EE2F13815024003B9ACC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 31 | C115EE3213815024003B9ACC /* iReSign-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "iReSign-Info.plist"; sourceTree = ""; }; 32 | C115EE3413815024003B9ACC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 33 | C115EE3613815024003B9ACC /* iReSign-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "iReSign-Prefix.pch"; sourceTree = ""; }; 34 | C115EE3713815024003B9ACC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 35 | C115EE3C13815024003B9ACC /* iReSignAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = iReSignAppDelegate.h; sourceTree = ""; }; 36 | C115EE3D13815024003B9ACC /* iReSignAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iReSignAppDelegate.m; sourceTree = ""; }; 37 | C115EE4013815024003B9ACC /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; }; 38 | C142471E138174E500F5B51F /* ResourceRules.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ResourceRules.plist; sourceTree = ""; }; 39 | C142472F1381B75B00F5B51F /* Icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = Icon.icns; path = ../Icon.icns; sourceTree = ""; }; 40 | DF1BF30B18D76F3800AF6E6F /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.xib"; sourceTree = ""; }; 41 | DF1BF30C18D76F3800AF6E6F /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; 42 | /* End PBXFileReference section */ 43 | 44 | /* Begin PBXFrameworksBuildPhase section */ 45 | C115EE2313815024003B9ACC /* Frameworks */ = { 46 | isa = PBXFrameworksBuildPhase; 47 | buildActionMask = 2147483647; 48 | files = ( 49 | C115EE2B13815024003B9ACC /* Cocoa.framework in Frameworks */, 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXFrameworksBuildPhase section */ 54 | 55 | /* Begin PBXGroup section */ 56 | C115EE1B13815023003B9ACC = { 57 | isa = PBXGroup; 58 | children = ( 59 | C115EE3013815024003B9ACC /* iReSign */, 60 | C115EE2913815024003B9ACC /* Frameworks */, 61 | C115EE2713815024003B9ACC /* Products */, 62 | ); 63 | sourceTree = ""; 64 | }; 65 | C115EE2713815024003B9ACC /* Products */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | C115EE2613815024003B9ACC /* iReSign.app */, 69 | ); 70 | name = Products; 71 | sourceTree = ""; 72 | }; 73 | C115EE2913815024003B9ACC /* Frameworks */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | C115EE2A13815024003B9ACC /* Cocoa.framework */, 77 | C115EE2C13815024003B9ACC /* Other Frameworks */, 78 | ); 79 | name = Frameworks; 80 | sourceTree = ""; 81 | }; 82 | C115EE2C13815024003B9ACC /* Other Frameworks */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | C115EE2D13815024003B9ACC /* AppKit.framework */, 86 | C115EE2E13815024003B9ACC /* CoreData.framework */, 87 | C115EE2F13815024003B9ACC /* Foundation.framework */, 88 | ); 89 | name = "Other Frameworks"; 90 | sourceTree = ""; 91 | }; 92 | C115EE3013815024003B9ACC /* iReSign */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | C115EE3C13815024003B9ACC /* iReSignAppDelegate.h */, 96 | C115EE3D13815024003B9ACC /* iReSignAppDelegate.m */, 97 | C115EE3F13815024003B9ACC /* MainMenu.xib */, 98 | 5B9E4C4A166AB3F7006D9F8F /* IRTextFieldDrag.h */, 99 | 5B9E4C4B166AB3F7006D9F8F /* IRTextFieldDrag.m */, 100 | 4C8EAD43185B74B90095DC6B /* IRHelpWindow.h */, 101 | 4C8EAD44185B74B90095DC6B /* IRHelpWindow.m */, 102 | C115EE3113815024003B9ACC /* Supporting Files */, 103 | ); 104 | path = iReSign; 105 | sourceTree = ""; 106 | }; 107 | C115EE3113815024003B9ACC /* Supporting Files */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | C142472F1381B75B00F5B51F /* Icon.icns */, 111 | C142471E138174E500F5B51F /* ResourceRules.plist */, 112 | C115EE3213815024003B9ACC /* iReSign-Info.plist */, 113 | C115EE3313815024003B9ACC /* InfoPlist.strings */, 114 | C115EE3613815024003B9ACC /* iReSign-Prefix.pch */, 115 | C115EE3713815024003B9ACC /* main.m */, 116 | ); 117 | name = "Supporting Files"; 118 | sourceTree = ""; 119 | }; 120 | /* End PBXGroup section */ 121 | 122 | /* Begin PBXNativeTarget section */ 123 | C115EE2513815024003B9ACC /* iReSign */ = { 124 | isa = PBXNativeTarget; 125 | buildConfigurationList = C115EE4413815024003B9ACC /* Build configuration list for PBXNativeTarget "iReSign" */; 126 | buildPhases = ( 127 | C115EE2213815024003B9ACC /* Sources */, 128 | C115EE2313815024003B9ACC /* Frameworks */, 129 | C115EE2413815024003B9ACC /* Resources */, 130 | ); 131 | buildRules = ( 132 | ); 133 | dependencies = ( 134 | ); 135 | name = iReSign; 136 | productName = iReSign; 137 | productReference = C115EE2613815024003B9ACC /* iReSign.app */; 138 | productType = "com.apple.product-type.application"; 139 | }; 140 | /* End PBXNativeTarget section */ 141 | 142 | /* Begin PBXProject section */ 143 | C115EE1D13815023003B9ACC /* Project object */ = { 144 | isa = PBXProject; 145 | attributes = { 146 | LastUpgradeCheck = 0500; 147 | ORGANIZATIONNAME = nil; 148 | }; 149 | buildConfigurationList = C115EE2013815023003B9ACC /* Build configuration list for PBXProject "iReSign" */; 150 | compatibilityVersion = "Xcode 3.2"; 151 | developmentRegion = English; 152 | hasScannedForEncodings = 0; 153 | knownRegions = ( 154 | en, 155 | "zh-Hans", 156 | ); 157 | mainGroup = C115EE1B13815023003B9ACC; 158 | productRefGroup = C115EE2713815024003B9ACC /* Products */; 159 | projectDirPath = ""; 160 | projectRoot = ""; 161 | targets = ( 162 | C115EE2513815024003B9ACC /* iReSign */, 163 | ); 164 | }; 165 | /* End PBXProject section */ 166 | 167 | /* Begin PBXResourcesBuildPhase section */ 168 | C115EE2413815024003B9ACC /* Resources */ = { 169 | isa = PBXResourcesBuildPhase; 170 | buildActionMask = 2147483647; 171 | files = ( 172 | C115EE3513815024003B9ACC /* InfoPlist.strings in Resources */, 173 | C115EE4113815024003B9ACC /* MainMenu.xib in Resources */, 174 | C142471F138174E500F5B51F /* ResourceRules.plist in Resources */, 175 | C14247301381B75B00F5B51F /* Icon.icns in Resources */, 176 | ); 177 | runOnlyForDeploymentPostprocessing = 0; 178 | }; 179 | /* End PBXResourcesBuildPhase section */ 180 | 181 | /* Begin PBXSourcesBuildPhase section */ 182 | C115EE2213815024003B9ACC /* Sources */ = { 183 | isa = PBXSourcesBuildPhase; 184 | buildActionMask = 2147483647; 185 | files = ( 186 | C115EE3813815024003B9ACC /* main.m in Sources */, 187 | 4C8EAD45185B74B90095DC6B /* IRHelpWindow.m in Sources */, 188 | C115EE3E13815024003B9ACC /* iReSignAppDelegate.m in Sources */, 189 | 5B9E4C4C166AB3F7006D9F8F /* IRTextFieldDrag.m in Sources */, 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | }; 193 | /* End PBXSourcesBuildPhase section */ 194 | 195 | /* Begin PBXVariantGroup section */ 196 | C115EE3313815024003B9ACC /* InfoPlist.strings */ = { 197 | isa = PBXVariantGroup; 198 | children = ( 199 | C115EE3413815024003B9ACC /* en */, 200 | DF1BF30C18D76F3800AF6E6F /* zh-Hans */, 201 | ); 202 | name = InfoPlist.strings; 203 | sourceTree = ""; 204 | }; 205 | C115EE3F13815024003B9ACC /* MainMenu.xib */ = { 206 | isa = PBXVariantGroup; 207 | children = ( 208 | C115EE4013815024003B9ACC /* en */, 209 | DF1BF30B18D76F3800AF6E6F /* zh-Hans */, 210 | ); 211 | name = MainMenu.xib; 212 | sourceTree = ""; 213 | }; 214 | /* End PBXVariantGroup section */ 215 | 216 | /* Begin XCBuildConfiguration section */ 217 | C115EE4213815024003B9ACC /* Debug */ = { 218 | isa = XCBuildConfiguration; 219 | buildSettings = { 220 | CLANG_WARN_BOOL_CONVERSION = YES; 221 | CLANG_WARN_CONSTANT_CONVERSION = YES; 222 | CLANG_WARN_EMPTY_BODY = YES; 223 | CLANG_WARN_ENUM_CONVERSION = YES; 224 | CLANG_WARN_INT_CONVERSION = YES; 225 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 226 | GCC_C_LANGUAGE_STANDARD = gnu99; 227 | GCC_OPTIMIZATION_LEVEL = 0; 228 | GCC_PREPROCESSOR_DEFINITIONS = DEBUG; 229 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 230 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 231 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 232 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 233 | GCC_WARN_UNDECLARED_SELECTOR = YES; 234 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 235 | GCC_WARN_UNUSED_FUNCTION = YES; 236 | GCC_WARN_UNUSED_VARIABLE = YES; 237 | MACOSX_DEPLOYMENT_TARGET = 10.6; 238 | ONLY_ACTIVE_ARCH = YES; 239 | SDKROOT = macosx; 240 | }; 241 | name = Debug; 242 | }; 243 | C115EE4313815024003B9ACC /* Release */ = { 244 | isa = XCBuildConfiguration; 245 | buildSettings = { 246 | CLANG_WARN_BOOL_CONVERSION = YES; 247 | CLANG_WARN_CONSTANT_CONVERSION = YES; 248 | CLANG_WARN_EMPTY_BODY = YES; 249 | CLANG_WARN_ENUM_CONVERSION = YES; 250 | CLANG_WARN_INT_CONVERSION = YES; 251 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 252 | GCC_C_LANGUAGE_STANDARD = gnu99; 253 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 254 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 255 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 256 | GCC_WARN_UNDECLARED_SELECTOR = YES; 257 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 258 | GCC_WARN_UNUSED_FUNCTION = YES; 259 | GCC_WARN_UNUSED_VARIABLE = YES; 260 | MACOSX_DEPLOYMENT_TARGET = 10.6; 261 | ONLY_ACTIVE_ARCH = NO; 262 | SDKROOT = macosx; 263 | }; 264 | name = Release; 265 | }; 266 | C115EE4513815024003B9ACC /* Debug */ = { 267 | isa = XCBuildConfiguration; 268 | buildSettings = { 269 | ALWAYS_SEARCH_USER_PATHS = NO; 270 | CLANG_ENABLE_OBJC_ARC = YES; 271 | COMBINE_HIDPI_IMAGES = YES; 272 | COPY_PHASE_STRIP = NO; 273 | GCC_DYNAMIC_NO_PIC = NO; 274 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 275 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 276 | GCC_PREFIX_HEADER = "iReSign/iReSign-Prefix.pch"; 277 | INFOPLIST_FILE = "iReSign/iReSign-Info.plist"; 278 | MACOSX_DEPLOYMENT_TARGET = 10.6.8; 279 | PRODUCT_NAME = "$(TARGET_NAME)"; 280 | WRAPPER_EXTENSION = app; 281 | }; 282 | name = Debug; 283 | }; 284 | C115EE4613815024003B9ACC /* Release */ = { 285 | isa = XCBuildConfiguration; 286 | buildSettings = { 287 | ALWAYS_SEARCH_USER_PATHS = NO; 288 | CLANG_ENABLE_OBJC_ARC = YES; 289 | COMBINE_HIDPI_IMAGES = YES; 290 | COPY_PHASE_STRIP = YES; 291 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 292 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 293 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 294 | GCC_PREFIX_HEADER = "iReSign/iReSign-Prefix.pch"; 295 | INFOPLIST_FILE = "iReSign/iReSign-Info.plist"; 296 | MACOSX_DEPLOYMENT_TARGET = 10.6.8; 297 | PRODUCT_NAME = "$(TARGET_NAME)"; 298 | WRAPPER_EXTENSION = app; 299 | }; 300 | name = Release; 301 | }; 302 | /* End XCBuildConfiguration section */ 303 | 304 | /* Begin XCConfigurationList section */ 305 | C115EE2013815023003B9ACC /* Build configuration list for PBXProject "iReSign" */ = { 306 | isa = XCConfigurationList; 307 | buildConfigurations = ( 308 | C115EE4213815024003B9ACC /* Debug */, 309 | C115EE4313815024003B9ACC /* Release */, 310 | ); 311 | defaultConfigurationIsVisible = 0; 312 | defaultConfigurationName = Release; 313 | }; 314 | C115EE4413815024003B9ACC /* Build configuration list for PBXNativeTarget "iReSign" */ = { 315 | isa = XCConfigurationList; 316 | buildConfigurations = ( 317 | C115EE4513815024003B9ACC /* Debug */, 318 | C115EE4613815024003B9ACC /* Release */, 319 | ); 320 | defaultConfigurationIsVisible = 0; 321 | defaultConfigurationName = Release; 322 | }; 323 | /* End XCConfigurationList section */ 324 | }; 325 | rootObject = C115EE1D13815023003B9ACC /* Project object */; 326 | } 327 | -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebook.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C6091DAA1E52BC0E003F0FF2 /* HackingFacebook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C6091DA01E52BC0E003F0FF2 /* HackingFacebook.framework */; }; 11 | C6091DAF1E52BC0E003F0FF2 /* HackingFacebookTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C6091DAE1E52BC0E003F0FF2 /* HackingFacebookTests.m */; }; 12 | C6091DB11E52BC0E003F0FF2 /* HackingFacebook.h in Headers */ = {isa = PBXBuildFile; fileRef = C6091DA31E52BC0E003F0FF2 /* HackingFacebook.h */; settings = {ATTRIBUTES = (Public, ); }; }; 13 | C6091DBC1E52BC34003F0FF2 /* FBLigerConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = C6091DBB1E52BC34003F0FF2 /* FBLigerConfig.h */; }; 14 | C6091DBE1E52BC39003F0FF2 /* FBSSLPinningVerifier.h in Headers */ = {isa = PBXBuildFile; fileRef = C6091DBD1E52BC39003F0FF2 /* FBSSLPinningVerifier.h */; }; 15 | C6091DC21E52BC55003F0FF2 /* Aspects.h in Headers */ = {isa = PBXBuildFile; fileRef = C6091DC01E52BC55003F0FF2 /* Aspects.h */; }; 16 | C6091DC31E52BC55003F0FF2 /* Aspects.m in Sources */ = {isa = PBXBuildFile; fileRef = C6091DC11E52BC55003F0FF2 /* Aspects.m */; }; 17 | C6091DC61E52BC6C003F0FF2 /* HackingEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = C6091DC41E52BC6C003F0FF2 /* HackingEntry.h */; }; 18 | C6091DC71E52BC6C003F0FF2 /* HackingEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = C6091DC51E52BC6C003F0FF2 /* HackingEntry.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | C6091DAB1E52BC0E003F0FF2 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = C6091D971E52BC0D003F0FF2 /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = C6091D9F1E52BC0E003F0FF2; 27 | remoteInfo = HackingFacebook; 28 | }; 29 | /* End PBXContainerItemProxy section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | C6091DA01E52BC0E003F0FF2 /* HackingFacebook.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HackingFacebook.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | C6091DA31E52BC0E003F0FF2 /* HackingFacebook.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HackingFacebook.h; sourceTree = ""; }; 34 | C6091DA41E52BC0E003F0FF2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | C6091DA91E52BC0E003F0FF2 /* HackingFacebookTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HackingFacebookTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | C6091DAE1E52BC0E003F0FF2 /* HackingFacebookTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HackingFacebookTests.m; sourceTree = ""; }; 37 | C6091DB01E52BC0E003F0FF2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | C6091DBB1E52BC34003F0FF2 /* FBLigerConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBLigerConfig.h; sourceTree = ""; }; 39 | C6091DBD1E52BC39003F0FF2 /* FBSSLPinningVerifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSSLPinningVerifier.h; sourceTree = ""; }; 40 | C6091DC01E52BC55003F0FF2 /* Aspects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Aspects.h; sourceTree = ""; }; 41 | C6091DC11E52BC55003F0FF2 /* Aspects.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Aspects.m; sourceTree = ""; }; 42 | C6091DC41E52BC6C003F0FF2 /* HackingEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HackingEntry.h; sourceTree = ""; }; 43 | C6091DC51E52BC6C003F0FF2 /* HackingEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HackingEntry.m; sourceTree = ""; }; 44 | /* End PBXFileReference section */ 45 | 46 | /* Begin PBXFrameworksBuildPhase section */ 47 | C6091D9C1E52BC0E003F0FF2 /* Frameworks */ = { 48 | isa = PBXFrameworksBuildPhase; 49 | buildActionMask = 2147483647; 50 | files = ( 51 | ); 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | C6091DA61E52BC0E003F0FF2 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | C6091DAA1E52BC0E003F0FF2 /* HackingFacebook.framework in Frameworks */, 59 | ); 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | /* End PBXFrameworksBuildPhase section */ 63 | 64 | /* Begin PBXGroup section */ 65 | C6091D961E52BC0D003F0FF2 = { 66 | isa = PBXGroup; 67 | children = ( 68 | C6091DA21E52BC0E003F0FF2 /* HackingFacebook */, 69 | C6091DAD1E52BC0E003F0FF2 /* HackingFacebookTests */, 70 | C6091DA11E52BC0E003F0FF2 /* Products */, 71 | ); 72 | sourceTree = ""; 73 | }; 74 | C6091DA11E52BC0E003F0FF2 /* Products */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | C6091DA01E52BC0E003F0FF2 /* HackingFacebook.framework */, 78 | C6091DA91E52BC0E003F0FF2 /* HackingFacebookTests.xctest */, 79 | ); 80 | name = Products; 81 | sourceTree = ""; 82 | }; 83 | C6091DA21E52BC0E003F0FF2 /* HackingFacebook */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | C6091DBF1E52BC47003F0FF2 /* Aspect */, 87 | C6091DBA1E52BC13003F0FF2 /* ClassDumpHeaders */, 88 | C6091DA31E52BC0E003F0FF2 /* HackingFacebook.h */, 89 | C6091DA41E52BC0E003F0FF2 /* Info.plist */, 90 | C6091DC41E52BC6C003F0FF2 /* HackingEntry.h */, 91 | C6091DC51E52BC6C003F0FF2 /* HackingEntry.m */, 92 | ); 93 | path = HackingFacebook; 94 | sourceTree = ""; 95 | }; 96 | C6091DAD1E52BC0E003F0FF2 /* HackingFacebookTests */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | C6091DAE1E52BC0E003F0FF2 /* HackingFacebookTests.m */, 100 | C6091DB01E52BC0E003F0FF2 /* Info.plist */, 101 | ); 102 | path = HackingFacebookTests; 103 | sourceTree = ""; 104 | }; 105 | C6091DBA1E52BC13003F0FF2 /* ClassDumpHeaders */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | C6091DBB1E52BC34003F0FF2 /* FBLigerConfig.h */, 109 | C6091DBD1E52BC39003F0FF2 /* FBSSLPinningVerifier.h */, 110 | ); 111 | path = ClassDumpHeaders; 112 | sourceTree = ""; 113 | }; 114 | C6091DBF1E52BC47003F0FF2 /* Aspect */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | C6091DC01E52BC55003F0FF2 /* Aspects.h */, 118 | C6091DC11E52BC55003F0FF2 /* Aspects.m */, 119 | ); 120 | path = Aspect; 121 | sourceTree = ""; 122 | }; 123 | /* End PBXGroup section */ 124 | 125 | /* Begin PBXHeadersBuildPhase section */ 126 | C6091D9D1E52BC0E003F0FF2 /* Headers */ = { 127 | isa = PBXHeadersBuildPhase; 128 | buildActionMask = 2147483647; 129 | files = ( 130 | C6091DBC1E52BC34003F0FF2 /* FBLigerConfig.h in Headers */, 131 | C6091DB11E52BC0E003F0FF2 /* HackingFacebook.h in Headers */, 132 | C6091DC21E52BC55003F0FF2 /* Aspects.h in Headers */, 133 | C6091DBE1E52BC39003F0FF2 /* FBSSLPinningVerifier.h in Headers */, 134 | C6091DC61E52BC6C003F0FF2 /* HackingEntry.h in Headers */, 135 | ); 136 | runOnlyForDeploymentPostprocessing = 0; 137 | }; 138 | /* End PBXHeadersBuildPhase section */ 139 | 140 | /* Begin PBXNativeTarget section */ 141 | C6091D9F1E52BC0E003F0FF2 /* HackingFacebook */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = C6091DB41E52BC0E003F0FF2 /* Build configuration list for PBXNativeTarget "HackingFacebook" */; 144 | buildPhases = ( 145 | C6091D9B1E52BC0E003F0FF2 /* Sources */, 146 | C6091D9C1E52BC0E003F0FF2 /* Frameworks */, 147 | C6091D9D1E52BC0E003F0FF2 /* Headers */, 148 | C6091D9E1E52BC0E003F0FF2 /* Resources */, 149 | ); 150 | buildRules = ( 151 | ); 152 | dependencies = ( 153 | ); 154 | name = HackingFacebook; 155 | productName = HackingFacebook; 156 | productReference = C6091DA01E52BC0E003F0FF2 /* HackingFacebook.framework */; 157 | productType = "com.apple.product-type.framework"; 158 | }; 159 | C6091DA81E52BC0E003F0FF2 /* HackingFacebookTests */ = { 160 | isa = PBXNativeTarget; 161 | buildConfigurationList = C6091DB71E52BC0E003F0FF2 /* Build configuration list for PBXNativeTarget "HackingFacebookTests" */; 162 | buildPhases = ( 163 | C6091DA51E52BC0E003F0FF2 /* Sources */, 164 | C6091DA61E52BC0E003F0FF2 /* Frameworks */, 165 | C6091DA71E52BC0E003F0FF2 /* Resources */, 166 | ); 167 | buildRules = ( 168 | ); 169 | dependencies = ( 170 | C6091DAC1E52BC0E003F0FF2 /* PBXTargetDependency */, 171 | ); 172 | name = HackingFacebookTests; 173 | productName = HackingFacebookTests; 174 | productReference = C6091DA91E52BC0E003F0FF2 /* HackingFacebookTests.xctest */; 175 | productType = "com.apple.product-type.bundle.unit-test"; 176 | }; 177 | /* End PBXNativeTarget section */ 178 | 179 | /* Begin PBXProject section */ 180 | C6091D971E52BC0D003F0FF2 /* Project object */ = { 181 | isa = PBXProject; 182 | attributes = { 183 | LastUpgradeCheck = 0830; 184 | ORGANIZATIONNAME = Weibo; 185 | TargetAttributes = { 186 | C6091D9F1E52BC0E003F0FF2 = { 187 | CreatedOnToolsVersion = 8.3; 188 | DevelopmentTeam = DMJXDB9H6Q; 189 | ProvisioningStyle = Automatic; 190 | }; 191 | C6091DA81E52BC0E003F0FF2 = { 192 | CreatedOnToolsVersion = 8.3; 193 | DevelopmentTeam = DMJXDB9H6Q; 194 | ProvisioningStyle = Automatic; 195 | }; 196 | }; 197 | }; 198 | buildConfigurationList = C6091D9A1E52BC0D003F0FF2 /* Build configuration list for PBXProject "HackingFacebook" */; 199 | compatibilityVersion = "Xcode 3.2"; 200 | developmentRegion = English; 201 | hasScannedForEncodings = 0; 202 | knownRegions = ( 203 | en, 204 | ); 205 | mainGroup = C6091D961E52BC0D003F0FF2; 206 | productRefGroup = C6091DA11E52BC0E003F0FF2 /* Products */; 207 | projectDirPath = ""; 208 | projectRoot = ""; 209 | targets = ( 210 | C6091D9F1E52BC0E003F0FF2 /* HackingFacebook */, 211 | C6091DA81E52BC0E003F0FF2 /* HackingFacebookTests */, 212 | ); 213 | }; 214 | /* End PBXProject section */ 215 | 216 | /* Begin PBXResourcesBuildPhase section */ 217 | C6091D9E1E52BC0E003F0FF2 /* Resources */ = { 218 | isa = PBXResourcesBuildPhase; 219 | buildActionMask = 2147483647; 220 | files = ( 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | }; 224 | C6091DA71E52BC0E003F0FF2 /* Resources */ = { 225 | isa = PBXResourcesBuildPhase; 226 | buildActionMask = 2147483647; 227 | files = ( 228 | ); 229 | runOnlyForDeploymentPostprocessing = 0; 230 | }; 231 | /* End PBXResourcesBuildPhase section */ 232 | 233 | /* Begin PBXSourcesBuildPhase section */ 234 | C6091D9B1E52BC0E003F0FF2 /* Sources */ = { 235 | isa = PBXSourcesBuildPhase; 236 | buildActionMask = 2147483647; 237 | files = ( 238 | C6091DC71E52BC6C003F0FF2 /* HackingEntry.m in Sources */, 239 | C6091DC31E52BC55003F0FF2 /* Aspects.m in Sources */, 240 | ); 241 | runOnlyForDeploymentPostprocessing = 0; 242 | }; 243 | C6091DA51E52BC0E003F0FF2 /* Sources */ = { 244 | isa = PBXSourcesBuildPhase; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | C6091DAF1E52BC0E003F0FF2 /* HackingFacebookTests.m in Sources */, 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | /* End PBXSourcesBuildPhase section */ 252 | 253 | /* Begin PBXTargetDependency section */ 254 | C6091DAC1E52BC0E003F0FF2 /* PBXTargetDependency */ = { 255 | isa = PBXTargetDependency; 256 | target = C6091D9F1E52BC0E003F0FF2 /* HackingFacebook */; 257 | targetProxy = C6091DAB1E52BC0E003F0FF2 /* PBXContainerItemProxy */; 258 | }; 259 | /* End PBXTargetDependency section */ 260 | 261 | /* Begin XCBuildConfiguration section */ 262 | C6091DB21E52BC0E003F0FF2 /* Debug */ = { 263 | isa = XCBuildConfiguration; 264 | buildSettings = { 265 | ALWAYS_SEARCH_USER_PATHS = NO; 266 | CLANG_ANALYZER_NONNULL = YES; 267 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 268 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 269 | CLANG_CXX_LIBRARY = "libc++"; 270 | CLANG_ENABLE_MODULES = YES; 271 | CLANG_ENABLE_OBJC_ARC = YES; 272 | CLANG_WARN_BOOL_CONVERSION = YES; 273 | CLANG_WARN_CONSTANT_CONVERSION = YES; 274 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 275 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 276 | CLANG_WARN_EMPTY_BODY = YES; 277 | CLANG_WARN_ENUM_CONVERSION = YES; 278 | CLANG_WARN_INFINITE_RECURSION = YES; 279 | CLANG_WARN_INT_CONVERSION = YES; 280 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 281 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 282 | CLANG_WARN_UNREACHABLE_CODE = YES; 283 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 284 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 285 | COPY_PHASE_STRIP = NO; 286 | CURRENT_PROJECT_VERSION = 1; 287 | DEBUG_INFORMATION_FORMAT = dwarf; 288 | ENABLE_STRICT_OBJC_MSGSEND = YES; 289 | ENABLE_TESTABILITY = YES; 290 | GCC_C_LANGUAGE_STANDARD = gnu99; 291 | GCC_DYNAMIC_NO_PIC = NO; 292 | GCC_NO_COMMON_BLOCKS = YES; 293 | GCC_OPTIMIZATION_LEVEL = 0; 294 | GCC_PREPROCESSOR_DEFINITIONS = ( 295 | "DEBUG=1", 296 | "$(inherited)", 297 | ); 298 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 299 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 300 | GCC_WARN_UNDECLARED_SELECTOR = YES; 301 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 302 | GCC_WARN_UNUSED_FUNCTION = YES; 303 | GCC_WARN_UNUSED_VARIABLE = YES; 304 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 305 | MTL_ENABLE_DEBUG_INFO = YES; 306 | ONLY_ACTIVE_ARCH = YES; 307 | SDKROOT = iphoneos; 308 | TARGETED_DEVICE_FAMILY = "1,2"; 309 | VERSIONING_SYSTEM = "apple-generic"; 310 | VERSION_INFO_PREFIX = ""; 311 | }; 312 | name = Debug; 313 | }; 314 | C6091DB31E52BC0E003F0FF2 /* Release */ = { 315 | isa = XCBuildConfiguration; 316 | buildSettings = { 317 | ALWAYS_SEARCH_USER_PATHS = NO; 318 | CLANG_ANALYZER_NONNULL = YES; 319 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 320 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 321 | CLANG_CXX_LIBRARY = "libc++"; 322 | CLANG_ENABLE_MODULES = YES; 323 | CLANG_ENABLE_OBJC_ARC = YES; 324 | CLANG_WARN_BOOL_CONVERSION = YES; 325 | CLANG_WARN_CONSTANT_CONVERSION = YES; 326 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 327 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 328 | CLANG_WARN_EMPTY_BODY = YES; 329 | CLANG_WARN_ENUM_CONVERSION = YES; 330 | CLANG_WARN_INFINITE_RECURSION = YES; 331 | CLANG_WARN_INT_CONVERSION = YES; 332 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 333 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 334 | CLANG_WARN_UNREACHABLE_CODE = YES; 335 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 336 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 337 | COPY_PHASE_STRIP = NO; 338 | CURRENT_PROJECT_VERSION = 1; 339 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 340 | ENABLE_NS_ASSERTIONS = NO; 341 | ENABLE_STRICT_OBJC_MSGSEND = YES; 342 | GCC_C_LANGUAGE_STANDARD = gnu99; 343 | GCC_NO_COMMON_BLOCKS = YES; 344 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 345 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 346 | GCC_WARN_UNDECLARED_SELECTOR = YES; 347 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 348 | GCC_WARN_UNUSED_FUNCTION = YES; 349 | GCC_WARN_UNUSED_VARIABLE = YES; 350 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 351 | MTL_ENABLE_DEBUG_INFO = NO; 352 | SDKROOT = iphoneos; 353 | TARGETED_DEVICE_FAMILY = "1,2"; 354 | VALIDATE_PRODUCT = YES; 355 | VERSIONING_SYSTEM = "apple-generic"; 356 | VERSION_INFO_PREFIX = ""; 357 | }; 358 | name = Release; 359 | }; 360 | C6091DB51E52BC0E003F0FF2 /* Debug */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | CODE_SIGN_IDENTITY = ""; 364 | DEFINES_MODULE = YES; 365 | DEVELOPMENT_TEAM = DMJXDB9H6Q; 366 | DYLIB_COMPATIBILITY_VERSION = 1; 367 | DYLIB_CURRENT_VERSION = 1; 368 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 369 | INFOPLIST_FILE = HackingFacebook/Info.plist; 370 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 371 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 372 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 373 | ONLY_ACTIVE_ARCH = NO; 374 | PRODUCT_BUNDLE_IDENTIFIER = com.weibo.HackingFacebook; 375 | PRODUCT_NAME = "$(TARGET_NAME)"; 376 | SKIP_INSTALL = YES; 377 | }; 378 | name = Debug; 379 | }; 380 | C6091DB61E52BC0E003F0FF2 /* Release */ = { 381 | isa = XCBuildConfiguration; 382 | buildSettings = { 383 | CODE_SIGN_IDENTITY = ""; 384 | DEFINES_MODULE = YES; 385 | DEVELOPMENT_TEAM = DMJXDB9H6Q; 386 | DYLIB_COMPATIBILITY_VERSION = 1; 387 | DYLIB_CURRENT_VERSION = 1; 388 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 389 | INFOPLIST_FILE = HackingFacebook/Info.plist; 390 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 391 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 392 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 393 | PRODUCT_BUNDLE_IDENTIFIER = com.weibo.HackingFacebook; 394 | PRODUCT_NAME = "$(TARGET_NAME)"; 395 | SKIP_INSTALL = YES; 396 | }; 397 | name = Release; 398 | }; 399 | C6091DB81E52BC0E003F0FF2 /* Debug */ = { 400 | isa = XCBuildConfiguration; 401 | buildSettings = { 402 | DEVELOPMENT_TEAM = DMJXDB9H6Q; 403 | INFOPLIST_FILE = HackingFacebookTests/Info.plist; 404 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 405 | PRODUCT_BUNDLE_IDENTIFIER = com.weibo.HackingFacebookTests; 406 | PRODUCT_NAME = "$(TARGET_NAME)"; 407 | }; 408 | name = Debug; 409 | }; 410 | C6091DB91E52BC0E003F0FF2 /* Release */ = { 411 | isa = XCBuildConfiguration; 412 | buildSettings = { 413 | DEVELOPMENT_TEAM = DMJXDB9H6Q; 414 | INFOPLIST_FILE = HackingFacebookTests/Info.plist; 415 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 416 | PRODUCT_BUNDLE_IDENTIFIER = com.weibo.HackingFacebookTests; 417 | PRODUCT_NAME = "$(TARGET_NAME)"; 418 | }; 419 | name = Release; 420 | }; 421 | /* End XCBuildConfiguration section */ 422 | 423 | /* Begin XCConfigurationList section */ 424 | C6091D9A1E52BC0D003F0FF2 /* Build configuration list for PBXProject "HackingFacebook" */ = { 425 | isa = XCConfigurationList; 426 | buildConfigurations = ( 427 | C6091DB21E52BC0E003F0FF2 /* Debug */, 428 | C6091DB31E52BC0E003F0FF2 /* Release */, 429 | ); 430 | defaultConfigurationIsVisible = 0; 431 | defaultConfigurationName = Release; 432 | }; 433 | C6091DB41E52BC0E003F0FF2 /* Build configuration list for PBXNativeTarget "HackingFacebook" */ = { 434 | isa = XCConfigurationList; 435 | buildConfigurations = ( 436 | C6091DB51E52BC0E003F0FF2 /* Debug */, 437 | C6091DB61E52BC0E003F0FF2 /* Release */, 438 | ); 439 | defaultConfigurationIsVisible = 0; 440 | defaultConfigurationName = Release; 441 | }; 442 | C6091DB71E52BC0E003F0FF2 /* Build configuration list for PBXNativeTarget "HackingFacebookTests" */ = { 443 | isa = XCConfigurationList; 444 | buildConfigurations = ( 445 | C6091DB81E52BC0E003F0FF2 /* Debug */, 446 | C6091DB91E52BC0E003F0FF2 /* Release */, 447 | ); 448 | defaultConfigurationIsVisible = 0; 449 | defaultConfigurationName = Release; 450 | }; 451 | /* End XCConfigurationList section */ 452 | }; 453 | rootObject = C6091D971E52BC0D003F0FF2 /* Project object */; 454 | } 455 | -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign/zh-Hans.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 146 | 158 | 169 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | iReSign 允许您使用自己的苹果开发者证书重新签名任何未加密的ipa文件,只要遵循如下操作: 274 | 275 | 1. 使用 “浏览” 按键找到需要签名的ipa或者输入需要签名的ipa文件路径。 276 | 277 | 2. 在 “钥匙串访问” 中找到证书全名,如 iPhone Developer: Firstname Lastname (XXXXXXXXXX) 278 | 279 | 3. 点击 “重新签名!" 并等待。 经过重新签名的文件将和原文件放置在同一目录下。 280 | 281 | 282 | 283 | 284 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign/en.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 146 | 158 | 169 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | iReSign allows you to re-sign any unencrypted ipa-file with any certificate for which you hold the corresponding private key. 274 | 275 | 1. Drag your unsigned .ipa file to the top box, or use the browse button. 276 | 277 | 2. Enter your full certificate name from Keychain Access, for example "iPhone Developer: Firstname Lastname (XXXXXXXXXX)" in the bottom box. 278 | 279 | 3. Click ReSign! and wait. The resigned file will be saved in the same folder as the original file. 280 | 281 | 282 | 283 | 284 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | -------------------------------------------------------------------------------- /iReSign/iReSign/iReSign/iReSignAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // iReSignAppDelegate.m 3 | // iReSign 4 | // 5 | // Created by Maciej Swic on 2011-05-16. 6 | // Copyright (c) 2011 Maciej Swic, Licensed under the MIT License. 7 | // See README.md for details 8 | // 9 | 10 | #import "iReSignAppDelegate.h" 11 | 12 | static NSString *kKeyPrefsBundleIDChange = @"keyBundleIDChange"; 13 | 14 | static NSString *kKeyBundleIDPlistApp = @"CFBundleIdentifier"; 15 | static NSString *kKeyBundleIDPlistiTunesArtwork = @"softwareVersionBundleId"; 16 | static NSString *kKeyInfoPlistApplicationProperties = @"ApplicationProperties"; 17 | static NSString *kKeyInfoPlistApplicationPath = @"ApplicationPath"; 18 | static NSString *kFrameworksDirName = @"Frameworks"; 19 | static NSString *kPayloadDirName = @"Payload"; 20 | static NSString *kProductsDirName = @"Products"; 21 | static NSString *kInfoPlistFilename = @"Info.plist"; 22 | static NSString *kiTunesMetadataFileName = @"iTunesMetadata"; 23 | 24 | @implementation iReSignAppDelegate 25 | 26 | @synthesize window,workingPath; 27 | 28 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 29 | { 30 | [flurry setAlphaValue:0.5]; 31 | 32 | defaults = [NSUserDefaults standardUserDefaults]; 33 | 34 | // Look up available signing certificates 35 | [self getCerts]; 36 | 37 | if ([defaults valueForKey:@"ENTITLEMENT_PATH"]) 38 | // [entitlementField setStringValue:[defaults valueForKey:@"ENTITLEMENT_PATH"]]; 39 | if ([defaults valueForKey:@"MOBILEPROVISION_PATH"]) 40 | [provisioningPathField setStringValue:[defaults valueForKey:@"MOBILEPROVISION_PATH"]]; 41 | 42 | if (![[NSFileManager defaultManager] fileExistsAtPath:@"/usr/bin/zip"]) { 43 | [self showAlertOfKind:NSCriticalAlertStyle WithTitle:@"Error" AndMessage:@"This app cannot run without the zip utility present at /usr/bin/zip"]; 44 | exit(0); 45 | } 46 | if (![[NSFileManager defaultManager] fileExistsAtPath:@"/usr/bin/unzip"]) { 47 | [self showAlertOfKind:NSCriticalAlertStyle WithTitle:@"Error" AndMessage:@"This app cannot run without the unzip utility present at /usr/bin/unzip"]; 48 | exit(0); 49 | } 50 | if (![[NSFileManager defaultManager] fileExistsAtPath:@"/usr/bin/codesign"]) { 51 | [self showAlertOfKind:NSCriticalAlertStyle WithTitle:@"Error" AndMessage:@"This app cannot run without the codesign utility present at /usr/bin/codesign"]; 52 | exit(0); 53 | } 54 | } 55 | 56 | 57 | - (IBAction)resign:(id)sender { 58 | //Save cert name 59 | [defaults setValue:[NSNumber numberWithInteger:[certComboBox indexOfSelectedItem]] forKey:@"CERT_INDEX"]; 60 | [defaults setValue:[entitlementField stringValue] forKey:@"ENTITLEMENT_PATH"]; 61 | [defaults setValue:[provisioningPathField stringValue] forKey:@"MOBILEPROVISION_PATH"]; 62 | [defaults setValue:[bundleIDField stringValue] forKey:kKeyPrefsBundleIDChange]; 63 | [defaults synchronize]; 64 | 65 | codesigningResult = nil; 66 | verificationResult = nil; 67 | 68 | sourcePath = [pathField stringValue]; 69 | workingPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"com.appulize.iresign"]; 70 | 71 | if ([certComboBox objectValue]) { 72 | if (([[[sourcePath pathExtension] lowercaseString] isEqualToString:@"ipa"]) || 73 | ([[[sourcePath pathExtension] lowercaseString] isEqualToString:@"xcarchive"])) { 74 | [self disableControls]; 75 | 76 | NSLog(@"Setting up working directory in %@",workingPath); 77 | [statusLabel setHidden:NO]; 78 | [statusLabel setStringValue:@"Setting up working directory"]; 79 | 80 | [[NSFileManager defaultManager] removeItemAtPath:workingPath error:nil]; 81 | 82 | [[NSFileManager defaultManager] createDirectoryAtPath:workingPath withIntermediateDirectories:TRUE attributes:nil error:nil]; 83 | 84 | if ([[[sourcePath pathExtension] lowercaseString] isEqualToString:@"ipa"]) { 85 | if (sourcePath && [sourcePath length] > 0) { 86 | NSLog(@"Unzipping %@",sourcePath); 87 | [statusLabel setStringValue:@"Extracting original app"]; 88 | } 89 | 90 | unzipTask = [[NSTask alloc] init]; 91 | [unzipTask setLaunchPath:@"/usr/bin/unzip"]; 92 | [unzipTask setArguments:[NSArray arrayWithObjects:@"-q", sourcePath, @"-d", workingPath, nil]]; 93 | 94 | [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(checkUnzip:) userInfo:nil repeats:TRUE]; 95 | 96 | [unzipTask launch]; 97 | } 98 | else { 99 | NSString* payloadPath = [workingPath stringByAppendingPathComponent:kPayloadDirName]; 100 | 101 | NSLog(@"Setting up %@ path in %@", kPayloadDirName, payloadPath); 102 | [statusLabel setStringValue:[NSString stringWithFormat:@"Setting up %@ path", kPayloadDirName]]; 103 | 104 | [[NSFileManager defaultManager] createDirectoryAtPath:payloadPath withIntermediateDirectories:TRUE attributes:nil error:nil]; 105 | 106 | NSLog(@"Retrieving %@", kInfoPlistFilename); 107 | [statusLabel setStringValue:[NSString stringWithFormat:@"Retrieving %@", kInfoPlistFilename]]; 108 | 109 | NSString* infoPListPath = [sourcePath stringByAppendingPathComponent:kInfoPlistFilename]; 110 | 111 | NSDictionary* infoPListDict = [NSDictionary dictionaryWithContentsOfFile:infoPListPath]; 112 | 113 | if (infoPListDict != nil) { 114 | NSString* applicationPath = nil; 115 | 116 | NSDictionary* applicationPropertiesDict = [infoPListDict objectForKey:kKeyInfoPlistApplicationProperties]; 117 | 118 | if (applicationPropertiesDict != nil) { 119 | applicationPath = [applicationPropertiesDict objectForKey:kKeyInfoPlistApplicationPath]; 120 | } 121 | 122 | if (applicationPath != nil) { 123 | applicationPath = [[sourcePath stringByAppendingPathComponent:kProductsDirName] stringByAppendingPathComponent:applicationPath]; 124 | 125 | NSLog(@"Copying %@ to %@ path in %@", applicationPath, kPayloadDirName, payloadPath); 126 | [statusLabel setStringValue:[NSString stringWithFormat:@"Copying .xcarchive app to %@ path", kPayloadDirName]]; 127 | 128 | copyTask = [[NSTask alloc] init]; 129 | [copyTask setLaunchPath:@"/bin/cp"]; 130 | [copyTask setArguments:[NSArray arrayWithObjects:@"-r", applicationPath, payloadPath, nil]]; 131 | 132 | [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(checkCopy:) userInfo:nil repeats:TRUE]; 133 | 134 | [copyTask launch]; 135 | } 136 | else { 137 | [self showAlertOfKind:NSCriticalAlertStyle WithTitle:@"Error" AndMessage:[NSString stringWithFormat:@"Unable to parse %@", kInfoPlistFilename]]; 138 | [self enableControls]; 139 | [statusLabel setStringValue:@"Ready"]; 140 | } 141 | } 142 | else { 143 | [self showAlertOfKind:NSCriticalAlertStyle WithTitle:@"Error" AndMessage:[NSString stringWithFormat:@"Retrieve %@ failed", kInfoPlistFilename]]; 144 | [self enableControls]; 145 | [statusLabel setStringValue:@"Ready"]; 146 | } 147 | } 148 | } 149 | else if ([[NSFileManager defaultManager] fileExistsAtPath:[sourcePath stringByAppendingPathComponent:kPayloadDirName]]) { 150 | [self proceedWithAppFolder]; 151 | } 152 | else { 153 | [self showAlertOfKind:NSCriticalAlertStyle WithTitle:@"Error" AndMessage:@"You must choose an *.ipa or *.xcarchive file"]; 154 | [self enableControls]; 155 | [statusLabel setStringValue:@"Please try again"]; 156 | } 157 | } else { 158 | [self showAlertOfKind:NSCriticalAlertStyle WithTitle:@"Error" AndMessage:@"You must choose an signing certificate from dropdown."]; 159 | [self enableControls]; 160 | [statusLabel setStringValue:@"Please try again"]; 161 | } 162 | } 163 | 164 | - (void)checkUnzip:(NSTimer *)timer { 165 | if ([unzipTask isRunning] == 0) { 166 | [timer invalidate]; 167 | unzipTask = nil; 168 | 169 | if ([[NSFileManager defaultManager] fileExistsAtPath:[workingPath stringByAppendingPathComponent:kPayloadDirName]]) { 170 | NSLog(@"Unzipping done"); 171 | [statusLabel setStringValue:@"Original app extracted"]; 172 | 173 | if (changeBundleIDCheckbox.state == NSOnState) { 174 | [self doBundleIDChange:bundleIDField.stringValue]; 175 | } 176 | 177 | if ([[provisioningPathField stringValue] isEqualTo:@""]) { 178 | [self doCodeSigning]; 179 | } else { 180 | [self doProvisioning]; 181 | } 182 | } else { 183 | [self showAlertOfKind:NSCriticalAlertStyle WithTitle:@"Error" AndMessage:@"Unzip failed"]; 184 | [self enableControls]; 185 | [statusLabel setStringValue:@"Ready"]; 186 | } 187 | } 188 | } 189 | 190 | - (void)proceedWithAppFolder 191 | { 192 | NSString * resigned = [sourcePath stringByAppendingString:@"_temp"]; 193 | NSFileManager * fm = [NSFileManager defaultManager]; 194 | if ([fm fileExistsAtPath:resigned]) { 195 | [fm removeItemAtPath:resigned error:NULL]; 196 | } 197 | [fm copyItemAtPath:sourcePath toPath:resigned error:NULL]; 198 | workingPath = resigned; 199 | 200 | NSLog(@"Unzipping done"); 201 | [statusLabel setStringValue:@"Original app extracted"]; 202 | 203 | [self doBundleIDChange:@"com.wutian.facebook"]; 204 | 205 | if ([[provisioningPathField stringValue] isEqualTo:@""]) { 206 | [self doCodeSigning]; 207 | } else { 208 | [self doProvisioning]; 209 | } 210 | } 211 | 212 | - (void)checkCopy:(NSTimer *)timer { 213 | if ([copyTask isRunning] == 0) { 214 | [timer invalidate]; 215 | copyTask = nil; 216 | 217 | NSLog(@"Copy done"); 218 | [statusLabel setStringValue:@".xcarchive app copied"]; 219 | 220 | if (changeBundleIDCheckbox.state == NSOnState) { 221 | [self doBundleIDChange:bundleIDField.stringValue]; 222 | } 223 | 224 | if ([[provisioningPathField stringValue] isEqualTo:@""]) { 225 | [self doCodeSigning]; 226 | } else { 227 | [self doProvisioning]; 228 | } 229 | } 230 | } 231 | 232 | - (BOOL)doBundleIDChange:(NSString *)newBundleID { 233 | BOOL success = YES; 234 | 235 | success &= [self doAppBundleIDChange:newBundleID]; 236 | success &= [self doITunesMetadataBundleIDChange:newBundleID]; 237 | 238 | return success; 239 | } 240 | 241 | 242 | - (BOOL)doITunesMetadataBundleIDChange:(NSString *)newBundleID { 243 | NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:workingPath error:nil]; 244 | NSString *infoPlistPath = nil; 245 | 246 | for (NSString *file in dirContents) { 247 | if ([[[file pathExtension] lowercaseString] isEqualToString:@"plist"]) { 248 | infoPlistPath = [workingPath stringByAppendingPathComponent:file]; 249 | break; 250 | } 251 | } 252 | 253 | return [self changeBundleIDForFile:infoPlistPath bundleIDKey:kKeyBundleIDPlistiTunesArtwork newBundleID:newBundleID plistOutOptions:NSPropertyListXMLFormat_v1_0]; 254 | 255 | } 256 | 257 | - (BOOL)doAppBundleIDChange:(NSString *)newBundleID { 258 | NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[workingPath stringByAppendingPathComponent:kPayloadDirName] error:nil]; 259 | NSString *infoPlistPath = nil; 260 | 261 | for (NSString *file in dirContents) { 262 | if ([[[file pathExtension] lowercaseString] isEqualToString:@"app"]) { 263 | infoPlistPath = [[[workingPath stringByAppendingPathComponent:kPayloadDirName] 264 | stringByAppendingPathComponent:file] 265 | stringByAppendingPathComponent:kInfoPlistFilename]; 266 | break; 267 | } 268 | } 269 | 270 | return [self changeBundleIDForFile:infoPlistPath bundleIDKey:kKeyBundleIDPlistApp newBundleID:newBundleID plistOutOptions:NSPropertyListBinaryFormat_v1_0]; 271 | } 272 | 273 | - (BOOL)changeBundleIDForFile:(NSString *)filePath bundleIDKey:(NSString *)bundleIDKey newBundleID:(NSString *)newBundleID plistOutOptions:(NSPropertyListWriteOptions)options { 274 | 275 | NSMutableDictionary *plist = nil; 276 | 277 | if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { 278 | plist = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath]; 279 | [plist setObject:newBundleID forKey:bundleIDKey]; 280 | 281 | NSData *xmlData = [NSPropertyListSerialization dataWithPropertyList:plist format:options options:kCFPropertyListImmutable error:nil]; 282 | 283 | return [xmlData writeToFile:filePath atomically:YES]; 284 | 285 | } 286 | 287 | return NO; 288 | } 289 | 290 | 291 | - (void)doProvisioning { 292 | NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[workingPath stringByAppendingPathComponent:kPayloadDirName] error:nil]; 293 | 294 | for (NSString *file in dirContents) { 295 | if ([[[file pathExtension] lowercaseString] isEqualToString:@"app"]) { 296 | appPath = [[workingPath stringByAppendingPathComponent:kPayloadDirName] stringByAppendingPathComponent:file]; 297 | if ([[NSFileManager defaultManager] fileExistsAtPath:[appPath stringByAppendingPathComponent:@"embedded.mobileprovision"]]) { 298 | NSLog(@"Found embedded.mobileprovision, deleting."); 299 | [[NSFileManager defaultManager] removeItemAtPath:[appPath stringByAppendingPathComponent:@"embedded.mobileprovision"] error:nil]; 300 | } 301 | break; 302 | } 303 | } 304 | 305 | NSString *targetPath = [appPath stringByAppendingPathComponent:@"embedded.mobileprovision"]; 306 | 307 | provisioningTask = [[NSTask alloc] init]; 308 | [provisioningTask setLaunchPath:@"/bin/cp"]; 309 | [provisioningTask setArguments:[NSArray arrayWithObjects:[provisioningPathField stringValue], targetPath, nil]]; 310 | 311 | [provisioningTask launch]; 312 | 313 | [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(checkProvisioning:) userInfo:nil repeats:TRUE]; 314 | } 315 | 316 | - (void)checkProvisioning:(NSTimer *)timer { 317 | if ([provisioningTask isRunning] == 0) { 318 | [timer invalidate]; 319 | provisioningTask = nil; 320 | 321 | NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[workingPath stringByAppendingPathComponent:kPayloadDirName] error:nil]; 322 | 323 | for (NSString *file in dirContents) { 324 | if ([[[file pathExtension] lowercaseString] isEqualToString:@"app"]) { 325 | appPath = [[workingPath stringByAppendingPathComponent:kPayloadDirName] stringByAppendingPathComponent:file]; 326 | if ([[NSFileManager defaultManager] fileExistsAtPath:[appPath stringByAppendingPathComponent:@"embedded.mobileprovision"]]) { 327 | 328 | BOOL identifierOK = FALSE; 329 | NSString *identifierInProvisioning = @""; 330 | 331 | NSString *embeddedProvisioning = [NSString stringWithContentsOfFile:[appPath stringByAppendingPathComponent:@"embedded.mobileprovision"] encoding:NSASCIIStringEncoding error:nil]; 332 | NSArray* embeddedProvisioningLines = [embeddedProvisioning componentsSeparatedByCharactersInSet: 333 | [NSCharacterSet newlineCharacterSet]]; 334 | 335 | for (int i = 0; i < [embeddedProvisioningLines count]; i++) { 336 | if ([[embeddedProvisioningLines objectAtIndex:i] rangeOfString:@"application-identifier"].location != NSNotFound) { 337 | 338 | NSInteger fromPosition = [[embeddedProvisioningLines objectAtIndex:i+1] rangeOfString:@""].location + 8; 339 | 340 | NSInteger toPosition = [[embeddedProvisioningLines objectAtIndex:i+1] rangeOfString:@""].location; 341 | 342 | NSRange range; 343 | range.location = fromPosition; 344 | range.length = toPosition-fromPosition; 345 | 346 | NSString *fullIdentifier = [[embeddedProvisioningLines objectAtIndex:i+1] substringWithRange:range]; 347 | 348 | NSArray *identifierComponents = [fullIdentifier componentsSeparatedByString:@"."]; 349 | 350 | if ([[identifierComponents lastObject] isEqualTo:@"*"]) { 351 | identifierOK = TRUE; 352 | } 353 | 354 | for (int i = 1; i < [identifierComponents count]; i++) { 355 | identifierInProvisioning = [identifierInProvisioning stringByAppendingString:[identifierComponents objectAtIndex:i]]; 356 | if (i < [identifierComponents count]-1) { 357 | identifierInProvisioning = [identifierInProvisioning stringByAppendingString:@"."]; 358 | } 359 | } 360 | break; 361 | } 362 | } 363 | 364 | NSLog(@"Mobileprovision identifier: %@",identifierInProvisioning); 365 | 366 | NSDictionary *infoplist = [NSDictionary dictionaryWithContentsOfFile:[appPath stringByAppendingPathComponent:@"Info.plist"]]; 367 | if ([identifierInProvisioning isEqualTo:[infoplist objectForKey:kKeyBundleIDPlistApp]]) { 368 | NSLog(@"Identifiers match"); 369 | identifierOK = TRUE; 370 | } 371 | 372 | if (identifierOK) { 373 | NSLog(@"Provisioning completed."); 374 | [statusLabel setStringValue:@"Provisioning completed"]; 375 | [self doEntitlementsFixing]; 376 | } else { 377 | [self showAlertOfKind:NSCriticalAlertStyle WithTitle:@"Error" AndMessage:@"Product identifiers don't match"]; 378 | [self enableControls]; 379 | [statusLabel setStringValue:@"Ready"]; 380 | } 381 | } else { 382 | [self showAlertOfKind:NSCriticalAlertStyle WithTitle:@"Error" AndMessage:@"Provisioning failed"]; 383 | [self enableControls]; 384 | [statusLabel setStringValue:@"Ready"]; 385 | } 386 | break; 387 | } 388 | } 389 | } 390 | } 391 | 392 | - (void)doEntitlementsFixing 393 | { 394 | if (![entitlementField.stringValue isEqualToString:@""] || [provisioningPathField.stringValue isEqualToString:@""]) { 395 | [self doCodeSigning]; 396 | return; // Using a pre-made entitlements file or we're not re-provisioning. 397 | } 398 | 399 | [statusLabel setStringValue:@"Generating entitlements"]; 400 | 401 | if (appPath) { 402 | generateEntitlementsTask = [[NSTask alloc] init]; 403 | [generateEntitlementsTask setLaunchPath:@"/usr/bin/security"]; 404 | [generateEntitlementsTask setArguments:@[@"cms", @"-D", @"-i", provisioningPathField.stringValue]]; 405 | [generateEntitlementsTask setCurrentDirectoryPath:workingPath]; 406 | 407 | [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(checkEntitlementsFix:) userInfo:nil repeats:TRUE]; 408 | 409 | NSPipe *pipe=[NSPipe pipe]; 410 | [generateEntitlementsTask setStandardOutput:pipe]; 411 | [generateEntitlementsTask setStandardError:pipe]; 412 | NSFileHandle *handle = [pipe fileHandleForReading]; 413 | 414 | [generateEntitlementsTask launch]; 415 | 416 | [NSThread detachNewThreadSelector:@selector(watchEntitlements:) 417 | toTarget:self withObject:handle]; 418 | } 419 | } 420 | 421 | - (void)watchEntitlements:(NSFileHandle*)streamHandle { 422 | @autoreleasepool { 423 | entitlementsResult = [[NSString alloc] initWithData:[streamHandle readDataToEndOfFile] encoding:NSASCIIStringEncoding]; 424 | } 425 | } 426 | 427 | - (void)checkEntitlementsFix:(NSTimer *)timer { 428 | if ([generateEntitlementsTask isRunning] == 0) { 429 | [timer invalidate]; 430 | generateEntitlementsTask = nil; 431 | NSLog(@"Entitlements fixed done"); 432 | [statusLabel setStringValue:@"Entitlements generated"]; 433 | [self doEntitlementsEdit]; 434 | } 435 | } 436 | 437 | - (void)doEntitlementsEdit 438 | { 439 | //macOS 10.12 bug: /usr/bin/security appends a junk line at the top of the XML file. 440 | if ([entitlementsResult containsString:@"SecPolicySetValue"]) 441 | { 442 | NSRange newlineRange = [entitlementsResult rangeOfString:@"\n"]; 443 | if(newlineRange.location != NSNotFound) { 444 | entitlementsResult = [entitlementsResult substringFromIndex:newlineRange.location]; 445 | } 446 | } 447 | //end macOS 10.12 bug fix. 448 | 449 | NSDictionary* entitlements = entitlementsResult.propertyList; 450 | entitlements = entitlements[@"Entitlements"]; 451 | NSString* filePath = [workingPath stringByAppendingPathComponent:@"entitlements.plist"]; 452 | NSData *xmlData = [NSPropertyListSerialization dataWithPropertyList:entitlements format:NSPropertyListXMLFormat_v1_0 options:kCFPropertyListImmutable error:nil]; 453 | if(![xmlData writeToFile:filePath atomically:YES]) { 454 | NSLog(@"Error writing entitlements file."); 455 | [self showAlertOfKind:NSCriticalAlertStyle WithTitle:@"Error" AndMessage:@"Failed entitlements generation"]; 456 | [self enableControls]; 457 | [statusLabel setStringValue:@"Ready"]; 458 | } 459 | else { 460 | entitlementField.stringValue = filePath; 461 | [self doCodeSigning]; 462 | } 463 | } 464 | 465 | - (void)doCodeSigning { 466 | appPath = nil; 467 | frameworksDirPath = nil; 468 | hasFrameworks = NO; 469 | frameworks = [[NSMutableArray alloc] init]; 470 | 471 | NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[workingPath stringByAppendingPathComponent:kPayloadDirName] error:nil]; 472 | 473 | for (NSString *file in dirContents) { 474 | if ([[[file pathExtension] lowercaseString] isEqualToString:@"app"]) { 475 | appPath = [[workingPath stringByAppendingPathComponent:kPayloadDirName] stringByAppendingPathComponent:file]; 476 | frameworksDirPath = [appPath stringByAppendingPathComponent:kFrameworksDirName]; 477 | NSLog(@"Found %@",appPath); 478 | appName = file; 479 | NSString * dylibsPath = [appPath stringByAppendingPathComponent:@"Dylibs"]; 480 | if ([[NSFileManager defaultManager] fileExistsAtPath:frameworksDirPath]) { 481 | NSLog(@"Found %@",frameworksDirPath); 482 | hasFrameworks = YES; 483 | NSArray *frameworksContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:frameworksDirPath error:nil]; 484 | for (NSString *frameworkFile in frameworksContents) { 485 | NSString *extension = [[frameworkFile pathExtension] lowercaseString]; 486 | if ([extension isEqualTo:@"framework"] || [extension isEqualTo:@"dylib"]) { 487 | frameworkPath = [frameworksDirPath stringByAppendingPathComponent:frameworkFile]; 488 | NSLog(@"Found %@",frameworkPath); 489 | [frameworks addObject:frameworkPath]; 490 | } 491 | } 492 | } 493 | 494 | if ([[NSFileManager defaultManager] fileExistsAtPath:dylibsPath]) { 495 | hasFrameworks = YES; 496 | NSArray * dylibsContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dylibsPath error:nil]; 497 | for (NSString * file in dylibsContents) { 498 | [frameworks addObject:[dylibsPath stringByAppendingPathComponent:file]]; 499 | } 500 | } 501 | 502 | [statusLabel setStringValue:[NSString stringWithFormat:@"Codesigning %@",file]]; 503 | break; 504 | } 505 | } 506 | 507 | if (appPath) { 508 | if (hasFrameworks) { 509 | [self signFile:[frameworks lastObject]]; 510 | [frameworks removeLastObject]; 511 | } else { 512 | [self signFile:appPath]; 513 | } 514 | } 515 | } 516 | 517 | - (void)signFile:(NSString*)filePath { 518 | NSLog(@"Codesigning %@", filePath); 519 | [statusLabel setStringValue:[NSString stringWithFormat:@"Codesigning %@",filePath]]; 520 | 521 | NSMutableArray *arguments = [NSMutableArray arrayWithObjects:@"-fs", [certComboBox objectValue], nil]; 522 | NSDictionary *systemVersionDictionary = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]; 523 | NSString * systemVersion = [systemVersionDictionary objectForKey:@"ProductVersion"]; 524 | NSArray * version = [systemVersion componentsSeparatedByString:@"."]; 525 | if ([version[0] intValue]<10 || ([version[0] intValue]==10 && ([version[1] intValue]<9 || ([version[1] intValue]==9 && [version[2] intValue]<5)))) { 526 | 527 | /* 528 | Before OSX 10.9, code signing requires a version 1 signature. 529 | The resource envelope is necessary. 530 | To ensure it is added, append the resource flag to the arguments. 531 | */ 532 | 533 | NSString *resourceRulesPath = [[NSBundle mainBundle] pathForResource:@"ResourceRules" ofType:@"plist"]; 534 | NSString *resourceRulesArgument = [NSString stringWithFormat:@"--resource-rules=%@",resourceRulesPath]; 535 | [arguments addObject:resourceRulesArgument]; 536 | } else { 537 | 538 | /* 539 | For OSX 10.9 and later, code signing requires a version 2 signature. 540 | The resource envelope is obsolete. 541 | To ensure it is ignored, remove the resource key from the Info.plist file. 542 | */ 543 | 544 | NSString *infoPath = [NSString stringWithFormat:@"%@/Info.plist", filePath]; 545 | NSMutableDictionary *infoDict = [NSMutableDictionary dictionaryWithContentsOfFile:infoPath]; 546 | [infoDict removeObjectForKey:@"CFBundleResourceSpecification"]; 547 | [infoDict writeToFile:infoPath atomically:YES]; 548 | [arguments addObject:@"--no-strict"]; // http://stackoverflow.com/a/26204757 549 | } 550 | 551 | if (![[entitlementField stringValue] isEqualToString:@""]) { 552 | [arguments addObject:[NSString stringWithFormat:@"--entitlements=%@", [entitlementField stringValue]]]; 553 | } 554 | 555 | [arguments addObjectsFromArray:[NSArray arrayWithObjects:filePath, nil]]; 556 | 557 | codesignTask = [[NSTask alloc] init]; 558 | [codesignTask setLaunchPath:@"/usr/bin/codesign"]; 559 | [codesignTask setArguments:arguments]; 560 | 561 | [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(checkCodesigning:) userInfo:nil repeats:TRUE]; 562 | 563 | 564 | NSPipe *pipe=[NSPipe pipe]; 565 | [codesignTask setStandardOutput:pipe]; 566 | [codesignTask setStandardError:pipe]; 567 | NSFileHandle *handle=[pipe fileHandleForReading]; 568 | 569 | [codesignTask launch]; 570 | 571 | [NSThread detachNewThreadSelector:@selector(watchCodesigning:) 572 | toTarget:self withObject:handle]; 573 | } 574 | 575 | - (void)watchCodesigning:(NSFileHandle*)streamHandle { 576 | @autoreleasepool { 577 | 578 | codesigningResult = [[NSString alloc] initWithData:[streamHandle readDataToEndOfFile] encoding:NSASCIIStringEncoding]; 579 | 580 | } 581 | } 582 | 583 | - (void)checkCodesigning:(NSTimer *)timer { 584 | if ([codesignTask isRunning] == 0) { 585 | [timer invalidate]; 586 | codesignTask = nil; 587 | if (frameworks.count > 0) { 588 | [self signFile:[frameworks lastObject]]; 589 | [frameworks removeLastObject]; 590 | } else if (hasFrameworks) { 591 | hasFrameworks = NO; 592 | [self signFile:appPath]; 593 | } else { 594 | NSLog(@"Codesigning done"); 595 | [statusLabel setStringValue:@"Codesigning completed"]; 596 | [self doVerifySignature]; 597 | } 598 | } 599 | } 600 | 601 | - (void)doVerifySignature { 602 | if (appPath) { 603 | verifyTask = [[NSTask alloc] init]; 604 | [verifyTask setLaunchPath:@"/usr/bin/codesign"]; 605 | [verifyTask setArguments:[NSArray arrayWithObjects:@"-v", appPath, nil]]; 606 | 607 | [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(checkVerificationProcess:) userInfo:nil repeats:TRUE]; 608 | 609 | NSLog(@"Verifying %@",appPath); 610 | [statusLabel setStringValue:[NSString stringWithFormat:@"Verifying %@",appName]]; 611 | 612 | NSPipe *pipe=[NSPipe pipe]; 613 | [verifyTask setStandardOutput:pipe]; 614 | [verifyTask setStandardError:pipe]; 615 | NSFileHandle *handle=[pipe fileHandleForReading]; 616 | 617 | [verifyTask launch]; 618 | 619 | [NSThread detachNewThreadSelector:@selector(watchVerificationProcess:) 620 | toTarget:self withObject:handle]; 621 | } 622 | } 623 | 624 | - (void)watchVerificationProcess:(NSFileHandle*)streamHandle { 625 | @autoreleasepool { 626 | 627 | verificationResult = [[NSString alloc] initWithData:[streamHandle readDataToEndOfFile] encoding:NSASCIIStringEncoding]; 628 | 629 | } 630 | } 631 | 632 | - (void)checkVerificationProcess:(NSTimer *)timer { 633 | if ([verifyTask isRunning] == 0) { 634 | [timer invalidate]; 635 | verifyTask = nil; 636 | if ([verificationResult length] == 0) { 637 | NSLog(@"Verification done"); 638 | [statusLabel setStringValue:@"Verification completed"]; 639 | [self doZip]; 640 | } else { 641 | NSString *error = [[codesigningResult stringByAppendingString:@"\n\n"] stringByAppendingString:verificationResult]; 642 | [self showAlertOfKind:NSCriticalAlertStyle WithTitle:@"Signing failed" AndMessage:error]; 643 | [self enableControls]; 644 | [statusLabel setStringValue:@"Please try again"]; 645 | } 646 | } 647 | } 648 | 649 | - (void)doZip { 650 | if (appPath) { 651 | NSArray *destinationPathComponents = [sourcePath pathComponents]; 652 | NSString *destinationPath = @""; 653 | 654 | for (int i = 0; i < ([destinationPathComponents count]-1); i++) { 655 | destinationPath = [destinationPath stringByAppendingPathComponent:[destinationPathComponents objectAtIndex:i]]; 656 | } 657 | 658 | fileName = [sourcePath lastPathComponent]; 659 | fileName = [fileName substringToIndex:([fileName length] - ([[sourcePath pathExtension] length] + 1))]; 660 | fileName = [fileName stringByAppendingString:@"-resigned"]; 661 | fileName = [fileName stringByAppendingPathExtension:@"ipa"]; 662 | 663 | destinationPath = [destinationPath stringByAppendingPathComponent:fileName]; 664 | 665 | NSLog(@"Dest: %@",destinationPath); 666 | 667 | zipTask = [[NSTask alloc] init]; 668 | [zipTask setLaunchPath:@"/usr/bin/zip"]; 669 | [zipTask setCurrentDirectoryPath:workingPath]; 670 | [zipTask setArguments:[NSArray arrayWithObjects:@"-qry", destinationPath, @".", nil]]; 671 | 672 | [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(checkZip:) userInfo:nil repeats:TRUE]; 673 | 674 | NSLog(@"Zipping %@", destinationPath); 675 | [statusLabel setStringValue:[NSString stringWithFormat:@"Saving %@",fileName]]; 676 | 677 | [zipTask launch]; 678 | } 679 | } 680 | 681 | - (void)checkZip:(NSTimer *)timer { 682 | if ([zipTask isRunning] == 0) { 683 | [timer invalidate]; 684 | zipTask = nil; 685 | NSLog(@"Zipping done"); 686 | [statusLabel setStringValue:[NSString stringWithFormat:@"Saved %@",fileName]]; 687 | 688 | [[NSFileManager defaultManager] removeItemAtPath:workingPath error:nil]; 689 | 690 | [self enableControls]; 691 | 692 | NSString *result = [[codesigningResult stringByAppendingString:@"\n\n"] stringByAppendingString:verificationResult]; 693 | NSLog(@"Codesigning result: %@",result); 694 | } 695 | } 696 | 697 | - (IBAction)browse:(id)sender { 698 | NSOpenPanel* openDlg = [NSOpenPanel openPanel]; 699 | 700 | [openDlg setCanChooseFiles:TRUE]; 701 | [openDlg setCanChooseDirectories:FALSE]; 702 | [openDlg setAllowsMultipleSelection:FALSE]; 703 | [openDlg setAllowsOtherFileTypes:FALSE]; 704 | [openDlg setAllowedFileTypes:@[@"ipa", @"IPA", @"xcarchive"]]; 705 | 706 | if ([openDlg runModal] == NSOKButton) 707 | { 708 | NSString* fileNameOpened = [[[openDlg URLs] objectAtIndex:0] path]; 709 | [pathField setStringValue:fileNameOpened]; 710 | } 711 | } 712 | 713 | - (IBAction)provisioningBrowse:(id)sender { 714 | NSOpenPanel* openDlg = [NSOpenPanel openPanel]; 715 | 716 | [openDlg setCanChooseFiles:TRUE]; 717 | [openDlg setCanChooseDirectories:FALSE]; 718 | [openDlg setAllowsMultipleSelection:FALSE]; 719 | [openDlg setAllowsOtherFileTypes:FALSE]; 720 | [openDlg setAllowedFileTypes:@[@"mobileprovision", @"MOBILEPROVISION"]]; 721 | 722 | if ([openDlg runModal] == NSOKButton) 723 | { 724 | NSString* fileNameOpened = [[[openDlg URLs] objectAtIndex:0] path]; 725 | [provisioningPathField setStringValue:fileNameOpened]; 726 | } 727 | } 728 | 729 | - (IBAction)entitlementBrowse:(id)sender { 730 | NSOpenPanel* openDlg = [NSOpenPanel openPanel]; 731 | 732 | [openDlg setCanChooseFiles:TRUE]; 733 | [openDlg setCanChooseDirectories:FALSE]; 734 | [openDlg setAllowsMultipleSelection:FALSE]; 735 | [openDlg setAllowsOtherFileTypes:FALSE]; 736 | [openDlg setAllowedFileTypes:@[@"plist", @"PLIST"]]; 737 | 738 | if ([openDlg runModal] == NSOKButton) 739 | { 740 | NSString* fileNameOpened = [[[openDlg URLs] objectAtIndex:0] path]; 741 | [entitlementField setStringValue:fileNameOpened]; 742 | } 743 | } 744 | 745 | - (IBAction)changeBundleIDPressed:(id)sender { 746 | 747 | if (sender != changeBundleIDCheckbox) { 748 | return; 749 | } 750 | 751 | bundleIDField.enabled = changeBundleIDCheckbox.state == NSOnState; 752 | } 753 | 754 | - (void)disableControls { 755 | [pathField setEnabled:FALSE]; 756 | [entitlementField setEnabled:FALSE]; 757 | [browseButton setEnabled:FALSE]; 758 | [resignButton setEnabled:FALSE]; 759 | [provisioningBrowseButton setEnabled:NO]; 760 | [provisioningPathField setEnabled:NO]; 761 | [changeBundleIDCheckbox setEnabled:NO]; 762 | [bundleIDField setEnabled:NO]; 763 | [certComboBox setEnabled:NO]; 764 | 765 | [flurry startAnimation:self]; 766 | [flurry setAlphaValue:1.0]; 767 | } 768 | 769 | - (void)enableControls { 770 | [pathField setEnabled:TRUE]; 771 | [entitlementField setEnabled:TRUE]; 772 | [browseButton setEnabled:TRUE]; 773 | [resignButton setEnabled:TRUE]; 774 | [provisioningBrowseButton setEnabled:YES]; 775 | [provisioningPathField setEnabled:YES]; 776 | [changeBundleIDCheckbox setEnabled:YES]; 777 | [bundleIDField setEnabled:changeBundleIDCheckbox.state == NSOnState]; 778 | [certComboBox setEnabled:YES]; 779 | 780 | [flurry stopAnimation:self]; 781 | [flurry setAlphaValue:0.5]; 782 | } 783 | 784 | -(NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox { 785 | NSInteger count = 0; 786 | if ([aComboBox isEqual:certComboBox]) { 787 | count = [certComboBoxItems count]; 788 | } 789 | return count; 790 | } 791 | 792 | - (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index { 793 | id item = nil; 794 | if ([aComboBox isEqual:certComboBox]) { 795 | item = [certComboBoxItems objectAtIndex:index]; 796 | } 797 | return item; 798 | } 799 | 800 | - (void)getCerts { 801 | 802 | getCertsResult = nil; 803 | 804 | NSLog(@"Getting Certificate IDs"); 805 | [statusLabel setStringValue:@"Getting Signing Certificate IDs"]; 806 | 807 | certTask = [[NSTask alloc] init]; 808 | [certTask setLaunchPath:@"/usr/bin/security"]; 809 | [certTask setArguments:[NSArray arrayWithObjects:@"find-identity", @"-v", @"-p", @"codesigning", nil]]; 810 | 811 | [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(checkCerts:) userInfo:nil repeats:TRUE]; 812 | 813 | NSPipe *pipe=[NSPipe pipe]; 814 | [certTask setStandardOutput:pipe]; 815 | [certTask setStandardError:pipe]; 816 | NSFileHandle *handle=[pipe fileHandleForReading]; 817 | 818 | [certTask launch]; 819 | 820 | [NSThread detachNewThreadSelector:@selector(watchGetCerts:) toTarget:self withObject:handle]; 821 | } 822 | 823 | - (void)watchGetCerts:(NSFileHandle*)streamHandle { 824 | @autoreleasepool { 825 | 826 | NSString *securityResult = [[NSString alloc] initWithData:[streamHandle readDataToEndOfFile] encoding:NSASCIIStringEncoding]; 827 | // Verify the security result 828 | if (securityResult == nil || securityResult.length < 1) { 829 | // Nothing in the result, return 830 | return; 831 | } 832 | NSArray *rawResult = [securityResult componentsSeparatedByString:@"\""]; 833 | NSMutableArray *tempGetCertsResult = [NSMutableArray arrayWithCapacity:20]; 834 | for (int i = 0; i <= [rawResult count] - 2; i+=2) { 835 | 836 | NSLog(@"i:%d", i+1); 837 | if (rawResult.count - 1 < i + 1) { 838 | // Invalid array, don't add an object to that position 839 | } else { 840 | // Valid object 841 | [tempGetCertsResult addObject:[rawResult objectAtIndex:i+1]]; 842 | } 843 | } 844 | 845 | certComboBoxItems = [NSMutableArray arrayWithArray:tempGetCertsResult]; 846 | 847 | [certComboBox reloadData]; 848 | 849 | } 850 | } 851 | 852 | - (void)checkCerts:(NSTimer *)timer { 853 | if ([certTask isRunning] == 0) { 854 | [timer invalidate]; 855 | certTask = nil; 856 | 857 | if ([certComboBoxItems count] > 0) { 858 | NSLog(@"Get Certs done"); 859 | [statusLabel setStringValue:@"Signing Certificate IDs extracted"]; 860 | 861 | if ([defaults valueForKey:@"CERT_INDEX"]) { 862 | 863 | NSInteger selectedIndex = [[defaults valueForKey:@"CERT_INDEX"] integerValue]; 864 | if (selectedIndex != -1) { 865 | NSString *selectedItem = [self comboBox:certComboBox objectValueForItemAtIndex:selectedIndex]; 866 | [certComboBox setObjectValue:selectedItem]; 867 | [certComboBox selectItemAtIndex:selectedIndex]; 868 | } 869 | 870 | [self enableControls]; 871 | } 872 | } else { 873 | [self showAlertOfKind:NSCriticalAlertStyle WithTitle:@"Error" AndMessage:@"Getting Certificate ID's failed"]; 874 | [self enableControls]; 875 | [statusLabel setStringValue:@"Ready"]; 876 | } 877 | } 878 | } 879 | 880 | // If the application dock icon is clicked, reopen the window 881 | - (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag { 882 | // Make sure the window is visible 883 | if (![self.window isVisible]) { 884 | // Window isn't shown, show it 885 | [self.window makeKeyAndOrderFront:self]; 886 | } 887 | 888 | // Return YES 889 | return YES; 890 | } 891 | 892 | #pragma mark - Alert Methods 893 | 894 | /* NSRunAlerts are being deprecated in 10.9 */ 895 | 896 | // Show a critical alert 897 | - (void)showAlertOfKind:(NSAlertStyle)style WithTitle:(NSString *)title AndMessage:(NSString *)message { 898 | NSAlert *alert = [[NSAlert alloc] init]; 899 | [alert addButtonWithTitle:@"OK"]; 900 | [alert setMessageText:title]; 901 | [alert setInformativeText:message]; 902 | [alert setAlertStyle:style]; 903 | [alert runModal]; 904 | } 905 | 906 | @end 907 | -------------------------------------------------------------------------------- /DyldXcodeProject/HackingFacebook/Aspect/Aspects.m: -------------------------------------------------------------------------------- 1 | // 2 | // Aspects.m 3 | // Aspects - A delightful, simple library for aspect oriented programming. 4 | // 5 | // Copyright (c) 2014 Peter Steinberger. Licensed under the MIT license. 6 | // 7 | 8 | #import "Aspects.h" 9 | #import 10 | #import 11 | #import 12 | 13 | #define AspectLog(...) 14 | //#define AspectLog(...) do { NSLog(__VA_ARGS__); }while(0) 15 | #define AspectLogError(...) do { NSLog(__VA_ARGS__); }while(0) 16 | 17 | // Block internals. 18 | typedef NS_OPTIONS(int, AspectBlockFlags) { 19 | AspectBlockFlagsHasCopyDisposeHelpers = (1 << 25), 20 | AspectBlockFlagsHasSignature = (1 << 30) 21 | }; 22 | typedef struct _AspectBlock { 23 | __unused Class isa; 24 | AspectBlockFlags flags; 25 | __unused int reserved; 26 | void (__unused *invoke)(struct _AspectBlock *block, ...); 27 | struct { 28 | unsigned long int reserved; 29 | unsigned long int size; 30 | // requires AspectBlockFlagsHasCopyDisposeHelpers 31 | void (*copy)(void *dst, const void *src); 32 | void (*dispose)(const void *); 33 | // requires AspectBlockFlagsHasSignature 34 | const char *signature; 35 | const char *layout; 36 | } *descriptor; 37 | // imported variables 38 | } *AspectBlockRef; 39 | 40 | @interface AspectInfo : NSObject 41 | - (id)initWithInstance:(__unsafe_unretained id)instance invocation:(NSInvocation *)invocation; 42 | @property (nonatomic, unsafe_unretained, readonly) id instance; 43 | @property (nonatomic, strong, readonly) NSArray *arguments; 44 | @property (nonatomic, strong, readonly) NSInvocation *originalInvocation; 45 | @end 46 | 47 | // Tracks a single aspect. 48 | @interface AspectIdentifier : NSObject 49 | + (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError **)error; 50 | - (BOOL)invokeWithInfo:(id)info; 51 | @property (nonatomic, assign) SEL selector; 52 | @property (nonatomic, strong) id block; 53 | @property (nonatomic, strong) NSMethodSignature *blockSignature; 54 | @property (nonatomic, weak) id object; 55 | @property (nonatomic, assign) AspectOptions options; 56 | @end 57 | 58 | // Tracks all aspects for an object/class. 59 | @interface AspectsContainer : NSObject 60 | - (void)addAspect:(AspectIdentifier *)aspect withOptions:(AspectOptions)injectPosition; 61 | - (BOOL)removeAspect:(id)aspect; 62 | - (BOOL)hasAspects; 63 | @property (atomic, copy) NSArray *beforeAspects; 64 | @property (atomic, copy) NSArray *insteadAspects; 65 | @property (atomic, copy) NSArray *afterAspects; 66 | @end 67 | 68 | @interface AspectTracker : NSObject 69 | - (id)initWithTrackedClass:(Class)trackedClass; 70 | @property (nonatomic, strong) Class trackedClass; 71 | @property (nonatomic, readonly) NSString *trackedClassName; 72 | @property (nonatomic, strong) NSMutableSet *selectorNames; 73 | @property (nonatomic, strong) NSMutableDictionary *selectorNamesToSubclassTrackers; 74 | - (void)addSubclassTracker:(AspectTracker *)subclassTracker hookingSelectorName:(NSString *)selectorName; 75 | - (void)removeSubclassTracker:(AspectTracker *)subclassTracker hookingSelectorName:(NSString *)selectorName; 76 | - (BOOL)subclassHasHookedSelectorName:(NSString *)selectorName; 77 | - (NSSet *)subclassTrackersHookingSelectorName:(NSString *)selectorName; 78 | @end 79 | 80 | @interface NSInvocation (Aspects) 81 | - (NSArray *)aspects_arguments; 82 | @end 83 | 84 | #define AspectPositionFilter 0x07 85 | 86 | #define AspectError(errorCode, errorDescription) do { \ 87 | AspectLogError(@"Aspects: %@", errorDescription); \ 88 | if (error) { *error = [NSError errorWithDomain:AspectErrorDomain code:errorCode userInfo:@{NSLocalizedDescriptionKey: errorDescription}]; }}while(0) 89 | 90 | NSString *const AspectErrorDomain = @"AspectErrorDomain"; 91 | static NSString *const AspectsSubclassSuffix = @"_Aspects_"; 92 | static NSString *const AspectsMessagePrefix = @"aspects_"; 93 | 94 | @implementation NSObject (Aspects) 95 | 96 | /////////////////////////////////////////////////////////////////////////////////////////// 97 | #pragma mark - Public Aspects API 98 | 99 | + (id)aspect_hookSelector:(SEL)selector 100 | withOptions:(AspectOptions)options 101 | usingBlock:(id)block 102 | error:(NSError **)error { 103 | return aspect_add((id)self, selector, options, block, error); 104 | } 105 | 106 | /// @return A token which allows to later deregister the aspect. 107 | - (id)aspect_hookSelector:(SEL)selector 108 | withOptions:(AspectOptions)options 109 | usingBlock:(id)block 110 | error:(NSError **)error { 111 | return aspect_add(self, selector, options, block, error); 112 | } 113 | 114 | /////////////////////////////////////////////////////////////////////////////////////////// 115 | #pragma mark - Private Helper 116 | 117 | static id aspect_add(id self, SEL selector, AspectOptions options, id block, NSError **error) { 118 | NSCParameterAssert(self); 119 | NSCParameterAssert(selector); 120 | NSCParameterAssert(block); 121 | 122 | __block AspectIdentifier *identifier = nil; 123 | aspect_performLocked(^{ 124 | if (aspect_isSelectorAllowedAndTrack(self, selector, options, error)) { 125 | AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector); 126 | identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error]; 127 | if (identifier) { 128 | [aspectContainer addAspect:identifier withOptions:options]; 129 | 130 | // Modify the class to allow message interception. 131 | aspect_prepareClassAndHookSelector(self, selector, error); 132 | } 133 | } 134 | }); 135 | return identifier; 136 | } 137 | 138 | static BOOL aspect_remove(AspectIdentifier *aspect, NSError **error) { 139 | NSCAssert([aspect isKindOfClass:AspectIdentifier.class], @"Must have correct type."); 140 | 141 | __block BOOL success = NO; 142 | aspect_performLocked(^{ 143 | id self = aspect.object; // strongify 144 | if (self) { 145 | AspectsContainer *aspectContainer = aspect_getContainerForObject(self, aspect.selector); 146 | success = [aspectContainer removeAspect:aspect]; 147 | 148 | aspect_cleanupHookedClassAndSelector(self, aspect.selector); 149 | // destroy token 150 | aspect.object = nil; 151 | aspect.block = nil; 152 | aspect.selector = NULL; 153 | }else { 154 | NSString *errrorDesc = [NSString stringWithFormat:@"Unable to deregister hook. Object already deallocated: %@", aspect]; 155 | AspectError(AspectErrorRemoveObjectAlreadyDeallocated, errrorDesc); 156 | } 157 | }); 158 | return success; 159 | } 160 | 161 | static void aspect_performLocked(dispatch_block_t block) { 162 | static OSSpinLock aspect_lock = OS_SPINLOCK_INIT; 163 | OSSpinLockLock(&aspect_lock); 164 | block(); 165 | OSSpinLockUnlock(&aspect_lock); 166 | } 167 | 168 | static SEL aspect_aliasForSelector(SEL selector) { 169 | NSCParameterAssert(selector); 170 | return NSSelectorFromString([AspectsMessagePrefix stringByAppendingFormat:@"_%@", NSStringFromSelector(selector)]); 171 | } 172 | 173 | static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error) { 174 | AspectBlockRef layout = (__bridge void *)block; 175 | if (!(layout->flags & AspectBlockFlagsHasSignature)) { 176 | NSString *description = [NSString stringWithFormat:@"The block %@ doesn't contain a type signature.", block]; 177 | AspectError(AspectErrorMissingBlockSignature, description); 178 | return nil; 179 | } 180 | void *desc = layout->descriptor; 181 | desc += 2 * sizeof(unsigned long int); 182 | if (layout->flags & AspectBlockFlagsHasCopyDisposeHelpers) { 183 | desc += 2 * sizeof(void *); 184 | } 185 | if (!desc) { 186 | NSString *description = [NSString stringWithFormat:@"The block %@ doesn't has a type signature.", block]; 187 | AspectError(AspectErrorMissingBlockSignature, description); 188 | return nil; 189 | } 190 | const char *signature = (*(const char **)desc); 191 | return [NSMethodSignature signatureWithObjCTypes:signature]; 192 | } 193 | 194 | static BOOL aspect_isCompatibleBlockSignature(NSMethodSignature *blockSignature, id object, SEL selector, NSError **error) { 195 | NSCParameterAssert(blockSignature); 196 | NSCParameterAssert(object); 197 | NSCParameterAssert(selector); 198 | 199 | BOOL signaturesMatch = YES; 200 | NSMethodSignature *methodSignature = [[object class] instanceMethodSignatureForSelector:selector]; 201 | if (blockSignature.numberOfArguments > methodSignature.numberOfArguments) { 202 | signaturesMatch = NO; 203 | }else { 204 | if (blockSignature.numberOfArguments > 1) { 205 | const char *blockType = [blockSignature getArgumentTypeAtIndex:1]; 206 | if (blockType[0] != '@') { 207 | signaturesMatch = NO; 208 | } 209 | } 210 | // Argument 0 is self/block, argument 1 is SEL or id. We start comparing at argument 2. 211 | // The block can have less arguments than the method, that's ok. 212 | if (signaturesMatch) { 213 | for (NSUInteger idx = 2; idx < blockSignature.numberOfArguments; idx++) { 214 | const char *methodType = [methodSignature getArgumentTypeAtIndex:idx]; 215 | const char *blockType = [blockSignature getArgumentTypeAtIndex:idx]; 216 | // Only compare parameter, not the optional type data. 217 | if (!methodType || !blockType || methodType[0] != blockType[0]) { 218 | signaturesMatch = NO; break; 219 | } 220 | } 221 | } 222 | } 223 | 224 | if (!signaturesMatch) { 225 | NSString *description = [NSString stringWithFormat:@"Block signature %@ doesn't match %@.", blockSignature, methodSignature]; 226 | AspectError(AspectErrorIncompatibleBlockSignature, description); 227 | return NO; 228 | } 229 | return YES; 230 | } 231 | 232 | /////////////////////////////////////////////////////////////////////////////////////////// 233 | #pragma mark - Class + Selector Preparation 234 | 235 | static BOOL aspect_isMsgForwardIMP(IMP impl) { 236 | return impl == _objc_msgForward 237 | #if !defined(__arm64__) 238 | || impl == (IMP)_objc_msgForward_stret 239 | #endif 240 | ; 241 | } 242 | 243 | static IMP aspect_getMsgForwardIMP(NSObject *self, SEL selector) { 244 | IMP msgForwardIMP = _objc_msgForward; 245 | #if !defined(__arm64__) 246 | // As an ugly internal runtime implementation detail in the 32bit runtime, we need to determine of the method we hook returns a struct or anything larger than id. 247 | // https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/000-Introduction/introduction.html 248 | // https://github.com/ReactiveCocoa/ReactiveCocoa/issues/783 249 | // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf (Section 5.4) 250 | Method method = class_getInstanceMethod(self.class, selector); 251 | const char *encoding = method_getTypeEncoding(method); 252 | BOOL methodReturnsStructValue = encoding[0] == _C_STRUCT_B; 253 | if (methodReturnsStructValue) { 254 | @try { 255 | NSUInteger valueSize = 0; 256 | NSGetSizeAndAlignment(encoding, &valueSize, NULL); 257 | 258 | if (valueSize == 1 || valueSize == 2 || valueSize == 4 || valueSize == 8) { 259 | methodReturnsStructValue = NO; 260 | } 261 | } @catch (__unused NSException *e) {} 262 | } 263 | if (methodReturnsStructValue) { 264 | msgForwardIMP = (IMP)_objc_msgForward_stret; 265 | } 266 | #endif 267 | return msgForwardIMP; 268 | } 269 | 270 | static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError **error) { 271 | NSCParameterAssert(selector); 272 | Class klass = aspect_hookClass(self, error); 273 | Method targetMethod = class_getInstanceMethod(klass, selector); 274 | IMP targetMethodIMP = method_getImplementation(targetMethod); 275 | if (!aspect_isMsgForwardIMP(targetMethodIMP)) { 276 | // Make a method alias for the existing method implementation, it not already copied. 277 | const char *typeEncoding = method_getTypeEncoding(targetMethod); 278 | SEL aliasSelector = aspect_aliasForSelector(selector); 279 | if (![klass instancesRespondToSelector:aliasSelector]) { 280 | __unused BOOL addedAlias = class_addMethod(klass, aliasSelector, method_getImplementation(targetMethod), typeEncoding); 281 | NSCAssert(addedAlias, @"Original implementation for %@ is already copied to %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass); 282 | } 283 | 284 | // We use forwardInvocation to hook in. 285 | class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding); 286 | AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector)); 287 | } 288 | } 289 | 290 | // Will undo the runtime changes made. 291 | static void aspect_cleanupHookedClassAndSelector(NSObject *self, SEL selector) { 292 | NSCParameterAssert(self); 293 | NSCParameterAssert(selector); 294 | 295 | Class klass = object_getClass(self); 296 | BOOL isMetaClass = class_isMetaClass(klass); 297 | if (isMetaClass) { 298 | klass = (Class)self; 299 | } 300 | 301 | // Check if the method is marked as forwarded and undo that. 302 | Method targetMethod = class_getInstanceMethod(klass, selector); 303 | IMP targetMethodIMP = method_getImplementation(targetMethod); 304 | if (aspect_isMsgForwardIMP(targetMethodIMP)) { 305 | // Restore the original method implementation. 306 | const char *typeEncoding = method_getTypeEncoding(targetMethod); 307 | SEL aliasSelector = aspect_aliasForSelector(selector); 308 | Method originalMethod = class_getInstanceMethod(klass, aliasSelector); 309 | IMP originalIMP = method_getImplementation(originalMethod); 310 | NSCAssert(originalMethod, @"Original implementation for %@ not found %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass); 311 | 312 | class_replaceMethod(klass, selector, originalIMP, typeEncoding); 313 | AspectLog(@"Aspects: Removed hook for -[%@ %@].", klass, NSStringFromSelector(selector)); 314 | } 315 | 316 | // Deregister global tracked selector 317 | aspect_deregisterTrackedSelector(self, selector); 318 | 319 | // Get the aspect container and check if there are any hooks remaining. Clean up if there are not. 320 | AspectsContainer *container = aspect_getContainerForObject(self, selector); 321 | if (!container.hasAspects) { 322 | // Destroy the container 323 | aspect_destroyContainerForObject(self, selector); 324 | 325 | // Figure out how the class was modified to undo the changes. 326 | NSString *className = NSStringFromClass(klass); 327 | if ([className hasSuffix:AspectsSubclassSuffix]) { 328 | Class originalClass = NSClassFromString([className stringByReplacingOccurrencesOfString:AspectsSubclassSuffix withString:@""]); 329 | NSCAssert(originalClass != nil, @"Original class must exist"); 330 | object_setClass(self, originalClass); 331 | AspectLog(@"Aspects: %@ has been restored.", NSStringFromClass(originalClass)); 332 | 333 | // We can only dispose the class pair if we can ensure that no instances exist using our subclass. 334 | // Since we don't globally track this, we can't ensure this - but there's also not much overhead in keeping it around. 335 | //objc_disposeClassPair(object.class); 336 | }else { 337 | // Class is most likely swizzled in place. Undo that. 338 | if (isMetaClass) { 339 | aspect_undoSwizzleClassInPlace((Class)self); 340 | }else if (self.class != klass) { 341 | aspect_undoSwizzleClassInPlace(klass); 342 | } 343 | } 344 | } 345 | } 346 | 347 | /////////////////////////////////////////////////////////////////////////////////////////// 348 | #pragma mark - Hook Class 349 | 350 | static Class aspect_hookClass(NSObject *self, NSError **error) { 351 | NSCParameterAssert(self); 352 | Class statedClass = self.class; 353 | Class baseClass = object_getClass(self); 354 | NSString *className = NSStringFromClass(baseClass); 355 | 356 | // Already subclassed 357 | if ([className hasSuffix:AspectsSubclassSuffix]) { 358 | return baseClass; 359 | 360 | // We swizzle a class object, not a single object. 361 | }else if (class_isMetaClass(baseClass)) { 362 | return aspect_swizzleClassInPlace((Class)self); 363 | // Probably a KVO'ed class. Swizzle in place. Also swizzle meta classes in place. 364 | }else if (statedClass != baseClass) { 365 | return aspect_swizzleClassInPlace(baseClass); 366 | } 367 | 368 | // Default case. Create dynamic subclass. 369 | const char *subclassName = [className stringByAppendingString:AspectsSubclassSuffix].UTF8String; 370 | Class subclass = objc_getClass(subclassName); 371 | 372 | if (subclass == nil) { 373 | subclass = objc_allocateClassPair(baseClass, subclassName, 0); 374 | if (subclass == nil) { 375 | NSString *errrorDesc = [NSString stringWithFormat:@"objc_allocateClassPair failed to allocate class %s.", subclassName]; 376 | AspectError(AspectErrorFailedToAllocateClassPair, errrorDesc); 377 | return nil; 378 | } 379 | 380 | aspect_swizzleForwardInvocation(subclass); 381 | aspect_hookedGetClass(subclass, statedClass); 382 | aspect_hookedGetClass(object_getClass(subclass), statedClass); 383 | objc_registerClassPair(subclass); 384 | } 385 | 386 | object_setClass(self, subclass); 387 | return subclass; 388 | } 389 | 390 | static NSString *const AspectsForwardInvocationSelectorName = @"__aspects_forwardInvocation:"; 391 | static void aspect_swizzleForwardInvocation(Class klass) { 392 | NSCParameterAssert(klass); 393 | // If there is no method, replace will act like class_addMethod. 394 | IMP originalImplementation = class_replaceMethod(klass, @selector(forwardInvocation:), (IMP)__ASPECTS_ARE_BEING_CALLED__, "v@:@"); 395 | if (originalImplementation) { 396 | class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, "v@:@"); 397 | } 398 | AspectLog(@"Aspects: %@ is now aspect aware.", NSStringFromClass(klass)); 399 | } 400 | 401 | static void aspect_undoSwizzleForwardInvocation(Class klass) { 402 | NSCParameterAssert(klass); 403 | Method originalMethod = class_getInstanceMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName)); 404 | Method objectMethod = class_getInstanceMethod(NSObject.class, @selector(forwardInvocation:)); 405 | // There is no class_removeMethod, so the best we can do is to retore the original implementation, or use a dummy. 406 | IMP originalImplementation = method_getImplementation(originalMethod ?: objectMethod); 407 | class_replaceMethod(klass, @selector(forwardInvocation:), originalImplementation, "v@:@"); 408 | 409 | AspectLog(@"Aspects: %@ has been restored.", NSStringFromClass(klass)); 410 | } 411 | 412 | static void aspect_hookedGetClass(Class class, Class statedClass) { 413 | NSCParameterAssert(class); 414 | NSCParameterAssert(statedClass); 415 | Method method = class_getInstanceMethod(class, @selector(class)); 416 | IMP newIMP = imp_implementationWithBlock(^(id self) { 417 | return statedClass; 418 | }); 419 | class_replaceMethod(class, @selector(class), newIMP, method_getTypeEncoding(method)); 420 | } 421 | 422 | /////////////////////////////////////////////////////////////////////////////////////////// 423 | #pragma mark - Swizzle Class In Place 424 | 425 | static void _aspect_modifySwizzledClasses(void (^block)(NSMutableSet *swizzledClasses)) { 426 | static NSMutableSet *swizzledClasses; 427 | static dispatch_once_t pred; 428 | dispatch_once(&pred, ^{ 429 | swizzledClasses = [NSMutableSet new]; 430 | }); 431 | @synchronized(swizzledClasses) { 432 | block(swizzledClasses); 433 | } 434 | } 435 | 436 | static Class aspect_swizzleClassInPlace(Class klass) { 437 | NSCParameterAssert(klass); 438 | NSString *className = NSStringFromClass(klass); 439 | 440 | _aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) { 441 | if (![swizzledClasses containsObject:className]) { 442 | aspect_swizzleForwardInvocation(klass); 443 | [swizzledClasses addObject:className]; 444 | } 445 | }); 446 | return klass; 447 | } 448 | 449 | static void aspect_undoSwizzleClassInPlace(Class klass) { 450 | NSCParameterAssert(klass); 451 | NSString *className = NSStringFromClass(klass); 452 | 453 | _aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) { 454 | if ([swizzledClasses containsObject:className]) { 455 | aspect_undoSwizzleForwardInvocation(klass); 456 | [swizzledClasses removeObject:className]; 457 | } 458 | }); 459 | } 460 | 461 | /////////////////////////////////////////////////////////////////////////////////////////// 462 | #pragma mark - Aspect Invoke Point 463 | 464 | // This is a macro so we get a cleaner stack trace. 465 | #define aspect_invoke(aspects, info) \ 466 | for (AspectIdentifier *aspect in aspects) {\ 467 | [aspect invokeWithInfo:info];\ 468 | if (aspect.options & AspectOptionAutomaticRemoval) { \ 469 | aspectsToRemove = [aspectsToRemove?:@[] arrayByAddingObject:aspect]; \ 470 | } \ 471 | } 472 | 473 | // This is the swizzled forwardInvocation: method. 474 | static void __ASPECTS_ARE_BEING_CALLED__(__unsafe_unretained NSObject *self, SEL selector, NSInvocation *invocation) { 475 | NSCParameterAssert(self); 476 | NSCParameterAssert(invocation); 477 | SEL originalSelector = invocation.selector; 478 | SEL aliasSelector = aspect_aliasForSelector(invocation.selector); 479 | invocation.selector = aliasSelector; 480 | AspectsContainer *objectContainer = objc_getAssociatedObject(self, aliasSelector); 481 | AspectsContainer *classContainer = aspect_getContainerForClass(object_getClass(self), aliasSelector); 482 | AspectInfo *info = [[AspectInfo alloc] initWithInstance:self invocation:invocation]; 483 | NSArray *aspectsToRemove = nil; 484 | 485 | // Before hooks. 486 | aspect_invoke(classContainer.beforeAspects, info); 487 | aspect_invoke(objectContainer.beforeAspects, info); 488 | 489 | // Instead hooks. 490 | BOOL respondsToAlias = YES; 491 | if (objectContainer.insteadAspects.count || classContainer.insteadAspects.count) { 492 | aspect_invoke(classContainer.insteadAspects, info); 493 | aspect_invoke(objectContainer.insteadAspects, info); 494 | }else { 495 | Class klass = object_getClass(invocation.target); 496 | do { 497 | if ((respondsToAlias = [klass instancesRespondToSelector:aliasSelector])) { 498 | [invocation invoke]; 499 | break; 500 | } 501 | }while (!respondsToAlias && (klass = class_getSuperclass(klass))); 502 | } 503 | 504 | // After hooks. 505 | aspect_invoke(classContainer.afterAspects, info); 506 | aspect_invoke(objectContainer.afterAspects, info); 507 | 508 | // If no hooks are installed, call original implementation (usually to throw an exception) 509 | if (!respondsToAlias) { 510 | invocation.selector = originalSelector; 511 | SEL originalForwardInvocationSEL = NSSelectorFromString(AspectsForwardInvocationSelectorName); 512 | if ([self respondsToSelector:originalForwardInvocationSEL]) { 513 | ((void( *)(id, SEL, NSInvocation *))objc_msgSend)(self, originalForwardInvocationSEL, invocation); 514 | }else { 515 | [self doesNotRecognizeSelector:invocation.selector]; 516 | } 517 | } 518 | 519 | // Remove any hooks that are queued for deregistration. 520 | [aspectsToRemove makeObjectsPerformSelector:@selector(remove)]; 521 | } 522 | #undef aspect_invoke 523 | 524 | /////////////////////////////////////////////////////////////////////////////////////////// 525 | #pragma mark - Aspect Container Management 526 | 527 | // Loads or creates the aspect container. 528 | static AspectsContainer *aspect_getContainerForObject(NSObject *self, SEL selector) { 529 | NSCParameterAssert(self); 530 | SEL aliasSelector = aspect_aliasForSelector(selector); 531 | AspectsContainer *aspectContainer = objc_getAssociatedObject(self, aliasSelector); 532 | if (!aspectContainer) { 533 | aspectContainer = [AspectsContainer new]; 534 | objc_setAssociatedObject(self, aliasSelector, aspectContainer, OBJC_ASSOCIATION_RETAIN); 535 | } 536 | return aspectContainer; 537 | } 538 | 539 | static AspectsContainer *aspect_getContainerForClass(Class klass, SEL selector) { 540 | NSCParameterAssert(klass); 541 | AspectsContainer *classContainer = nil; 542 | do { 543 | classContainer = objc_getAssociatedObject(klass, selector); 544 | if (classContainer.hasAspects) break; 545 | }while ((klass = class_getSuperclass(klass))); 546 | 547 | return classContainer; 548 | } 549 | 550 | static void aspect_destroyContainerForObject(id self, SEL selector) { 551 | NSCParameterAssert(self); 552 | SEL aliasSelector = aspect_aliasForSelector(selector); 553 | objc_setAssociatedObject(self, aliasSelector, nil, OBJC_ASSOCIATION_RETAIN); 554 | } 555 | 556 | /////////////////////////////////////////////////////////////////////////////////////////// 557 | #pragma mark - Selector Blacklist Checking 558 | 559 | static NSMutableDictionary *aspect_getSwizzledClassesDict() { 560 | static NSMutableDictionary *swizzledClassesDict; 561 | static dispatch_once_t pred; 562 | dispatch_once(&pred, ^{ 563 | swizzledClassesDict = [NSMutableDictionary new]; 564 | }); 565 | return swizzledClassesDict; 566 | } 567 | 568 | static BOOL aspect_isSelectorAllowedAndTrack(NSObject *self, SEL selector, AspectOptions options, NSError **error) { 569 | static NSSet *disallowedSelectorList; 570 | static dispatch_once_t pred; 571 | dispatch_once(&pred, ^{ 572 | disallowedSelectorList = [NSSet setWithObjects:@"retain", @"release", @"autorelease", @"forwardInvocation:", nil]; 573 | }); 574 | 575 | // Check against the blacklist. 576 | NSString *selectorName = NSStringFromSelector(selector); 577 | if ([disallowedSelectorList containsObject:selectorName]) { 578 | NSString *errorDescription = [NSString stringWithFormat:@"Selector %@ is blacklisted.", selectorName]; 579 | AspectError(AspectErrorSelectorBlacklisted, errorDescription); 580 | return NO; 581 | } 582 | 583 | // Additional checks. 584 | AspectOptions position = options&AspectPositionFilter; 585 | if ([selectorName isEqualToString:@"dealloc"] && position != AspectPositionBefore) { 586 | NSString *errorDesc = @"AspectPositionBefore is the only valid position when hooking dealloc."; 587 | AspectError(AspectErrorSelectorDeallocPosition, errorDesc); 588 | return NO; 589 | } 590 | 591 | if (![self respondsToSelector:selector] && ![self.class instancesRespondToSelector:selector]) { 592 | NSString *errorDesc = [NSString stringWithFormat:@"Unable to find selector -[%@ %@].", NSStringFromClass(self.class), selectorName]; 593 | AspectError(AspectErrorDoesNotRespondToSelector, errorDesc); 594 | return NO; 595 | } 596 | 597 | // Search for the current class and the class hierarchy IF we are modifying a class object 598 | if (class_isMetaClass(object_getClass(self))) { 599 | Class klass = [self class]; 600 | NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict(); 601 | Class currentClass = [self class]; 602 | 603 | AspectTracker *tracker = swizzledClassesDict[currentClass]; 604 | if ([tracker subclassHasHookedSelectorName:selectorName]) { 605 | NSSet *subclassTracker = [tracker subclassTrackersHookingSelectorName:selectorName]; 606 | NSSet *subclassNames = [subclassTracker valueForKey:@"trackedClassName"]; 607 | NSString *errorDescription = [NSString stringWithFormat:@"Error: %@ already hooked subclasses: %@. A method can only be hooked once per class hierarchy.", selectorName, subclassNames]; 608 | AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription); 609 | return NO; 610 | } 611 | 612 | do { 613 | tracker = swizzledClassesDict[currentClass]; 614 | if ([tracker.selectorNames containsObject:selectorName]) { 615 | if (klass == currentClass) { 616 | // Already modified and topmost! 617 | return YES; 618 | } 619 | NSString *errorDescription = [NSString stringWithFormat:@"Error: %@ already hooked in %@. A method can only be hooked once per class hierarchy.", selectorName, NSStringFromClass(currentClass)]; 620 | AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription); 621 | return NO; 622 | } 623 | } while ((currentClass = class_getSuperclass(currentClass))); 624 | 625 | // Add the selector as being modified. 626 | currentClass = klass; 627 | AspectTracker *subclassTracker = nil; 628 | do { 629 | tracker = swizzledClassesDict[currentClass]; 630 | if (!tracker) { 631 | tracker = [[AspectTracker alloc] initWithTrackedClass:currentClass]; 632 | swizzledClassesDict[(id)currentClass] = tracker; 633 | } 634 | if (subclassTracker) { 635 | [tracker addSubclassTracker:subclassTracker hookingSelectorName:selectorName]; 636 | } else { 637 | [tracker.selectorNames addObject:selectorName]; 638 | } 639 | 640 | // All superclasses get marked as having a subclass that is modified. 641 | subclassTracker = tracker; 642 | }while ((currentClass = class_getSuperclass(currentClass))); 643 | } else { 644 | return YES; 645 | } 646 | 647 | return YES; 648 | } 649 | 650 | static void aspect_deregisterTrackedSelector(id self, SEL selector) { 651 | if (!class_isMetaClass(object_getClass(self))) return; 652 | 653 | NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict(); 654 | NSString *selectorName = NSStringFromSelector(selector); 655 | Class currentClass = [self class]; 656 | AspectTracker *subclassTracker = nil; 657 | do { 658 | AspectTracker *tracker = swizzledClassesDict[currentClass]; 659 | if (subclassTracker) { 660 | [tracker removeSubclassTracker:subclassTracker hookingSelectorName:selectorName]; 661 | } else { 662 | [tracker.selectorNames removeObject:selectorName]; 663 | } 664 | if (tracker.selectorNames.count == 0 && tracker.selectorNamesToSubclassTrackers) { 665 | [swizzledClassesDict removeObjectForKey:currentClass]; 666 | } 667 | subclassTracker = tracker; 668 | }while ((currentClass = class_getSuperclass(currentClass))); 669 | } 670 | 671 | @end 672 | 673 | @implementation AspectTracker 674 | 675 | - (id)initWithTrackedClass:(Class)trackedClass { 676 | if (self = [super init]) { 677 | _trackedClass = trackedClass; 678 | _selectorNames = [NSMutableSet new]; 679 | _selectorNamesToSubclassTrackers = [NSMutableDictionary new]; 680 | } 681 | return self; 682 | } 683 | 684 | - (BOOL)subclassHasHookedSelectorName:(NSString *)selectorName { 685 | return self.selectorNamesToSubclassTrackers[selectorName] != nil; 686 | } 687 | 688 | - (void)addSubclassTracker:(AspectTracker *)subclassTracker hookingSelectorName:(NSString *)selectorName { 689 | NSMutableSet *trackerSet = self.selectorNamesToSubclassTrackers[selectorName]; 690 | if (!trackerSet) { 691 | trackerSet = [NSMutableSet new]; 692 | self.selectorNamesToSubclassTrackers[selectorName] = trackerSet; 693 | } 694 | [trackerSet addObject:subclassTracker]; 695 | } 696 | - (void)removeSubclassTracker:(AspectTracker *)subclassTracker hookingSelectorName:(NSString *)selectorName { 697 | NSMutableSet *trackerSet = self.selectorNamesToSubclassTrackers[selectorName]; 698 | [trackerSet removeObject:subclassTracker]; 699 | if (trackerSet.count == 0) { 700 | [self.selectorNamesToSubclassTrackers removeObjectForKey:selectorName]; 701 | } 702 | } 703 | - (NSSet *)subclassTrackersHookingSelectorName:(NSString *)selectorName { 704 | NSMutableSet *hookingSubclassTrackers = [NSMutableSet new]; 705 | for (AspectTracker *tracker in self.selectorNamesToSubclassTrackers[selectorName]) { 706 | if ([tracker.selectorNames containsObject:selectorName]) { 707 | [hookingSubclassTrackers addObject:tracker]; 708 | } 709 | [hookingSubclassTrackers unionSet:[tracker subclassTrackersHookingSelectorName:selectorName]]; 710 | } 711 | return hookingSubclassTrackers; 712 | } 713 | - (NSString *)trackedClassName { 714 | return NSStringFromClass(self.trackedClass); 715 | } 716 | 717 | - (NSString *)description { 718 | return [NSString stringWithFormat:@"<%@: %@, trackedClass: %@, selectorNames:%@, subclass selector names: %@>", self.class, self, NSStringFromClass(self.trackedClass), self.selectorNames, self.selectorNamesToSubclassTrackers.allKeys]; 719 | } 720 | 721 | @end 722 | 723 | /////////////////////////////////////////////////////////////////////////////////////////// 724 | #pragma mark - NSInvocation (Aspects) 725 | 726 | @implementation NSInvocation (Aspects) 727 | 728 | // Thanks to the ReactiveCocoa team for providing a generic solution for this. 729 | - (id)aspect_argumentAtIndex:(NSUInteger)index { 730 | const char *argType = [self.methodSignature getArgumentTypeAtIndex:index]; 731 | // Skip const type qualifier. 732 | if (argType[0] == _C_CONST) argType++; 733 | 734 | #define WRAP_AND_RETURN(type) do { type val = 0; [self getArgument:&val atIndex:(NSInteger)index]; return @(val); } while (0) 735 | if (strcmp(argType, @encode(id)) == 0 || strcmp(argType, @encode(Class)) == 0) { 736 | __autoreleasing id returnObj; 737 | [self getArgument:&returnObj atIndex:(NSInteger)index]; 738 | return returnObj; 739 | } else if (strcmp(argType, @encode(SEL)) == 0) { 740 | SEL selector = 0; 741 | [self getArgument:&selector atIndex:(NSInteger)index]; 742 | return NSStringFromSelector(selector); 743 | } else if (strcmp(argType, @encode(Class)) == 0) { 744 | __autoreleasing Class theClass = Nil; 745 | [self getArgument:&theClass atIndex:(NSInteger)index]; 746 | return theClass; 747 | // Using this list will box the number with the appropriate constructor, instead of the generic NSValue. 748 | } else if (strcmp(argType, @encode(char)) == 0) { 749 | WRAP_AND_RETURN(char); 750 | } else if (strcmp(argType, @encode(int)) == 0) { 751 | WRAP_AND_RETURN(int); 752 | } else if (strcmp(argType, @encode(short)) == 0) { 753 | WRAP_AND_RETURN(short); 754 | } else if (strcmp(argType, @encode(long)) == 0) { 755 | WRAP_AND_RETURN(long); 756 | } else if (strcmp(argType, @encode(long long)) == 0) { 757 | WRAP_AND_RETURN(long long); 758 | } else if (strcmp(argType, @encode(unsigned char)) == 0) { 759 | WRAP_AND_RETURN(unsigned char); 760 | } else if (strcmp(argType, @encode(unsigned int)) == 0) { 761 | WRAP_AND_RETURN(unsigned int); 762 | } else if (strcmp(argType, @encode(unsigned short)) == 0) { 763 | WRAP_AND_RETURN(unsigned short); 764 | } else if (strcmp(argType, @encode(unsigned long)) == 0) { 765 | WRAP_AND_RETURN(unsigned long); 766 | } else if (strcmp(argType, @encode(unsigned long long)) == 0) { 767 | WRAP_AND_RETURN(unsigned long long); 768 | } else if (strcmp(argType, @encode(float)) == 0) { 769 | WRAP_AND_RETURN(float); 770 | } else if (strcmp(argType, @encode(double)) == 0) { 771 | WRAP_AND_RETURN(double); 772 | } else if (strcmp(argType, @encode(BOOL)) == 0) { 773 | WRAP_AND_RETURN(BOOL); 774 | } else if (strcmp(argType, @encode(bool)) == 0) { 775 | WRAP_AND_RETURN(BOOL); 776 | } else if (strcmp(argType, @encode(char *)) == 0) { 777 | WRAP_AND_RETURN(const char *); 778 | } else if (strcmp(argType, @encode(void (^)(void))) == 0) { 779 | __unsafe_unretained id block = nil; 780 | [self getArgument:&block atIndex:(NSInteger)index]; 781 | return [block copy]; 782 | } else { 783 | NSUInteger valueSize = 0; 784 | NSGetSizeAndAlignment(argType, &valueSize, NULL); 785 | 786 | unsigned char valueBytes[valueSize]; 787 | [self getArgument:valueBytes atIndex:(NSInteger)index]; 788 | 789 | return [NSValue valueWithBytes:valueBytes objCType:argType]; 790 | } 791 | return nil; 792 | #undef WRAP_AND_RETURN 793 | } 794 | 795 | - (NSArray *)aspects_arguments { 796 | NSMutableArray *argumentsArray = [NSMutableArray array]; 797 | for (NSUInteger idx = 2; idx < self.methodSignature.numberOfArguments; idx++) { 798 | [argumentsArray addObject:[self aspect_argumentAtIndex:idx] ?: NSNull.null]; 799 | } 800 | return [argumentsArray copy]; 801 | } 802 | 803 | @end 804 | 805 | /////////////////////////////////////////////////////////////////////////////////////////// 806 | #pragma mark - AspectIdentifier 807 | 808 | @implementation AspectIdentifier 809 | 810 | + (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError **)error { 811 | NSCParameterAssert(block); 812 | NSCParameterAssert(selector); 813 | NSMethodSignature *blockSignature = aspect_blockMethodSignature(block, error); // TODO: check signature compatibility, etc. 814 | if (!aspect_isCompatibleBlockSignature(blockSignature, object, selector, error)) { 815 | return nil; 816 | } 817 | 818 | AspectIdentifier *identifier = nil; 819 | if (blockSignature) { 820 | identifier = [AspectIdentifier new]; 821 | identifier.selector = selector; 822 | identifier.block = block; 823 | identifier.blockSignature = blockSignature; 824 | identifier.options = options; 825 | identifier.object = object; // weak 826 | } 827 | return identifier; 828 | } 829 | 830 | - (BOOL)invokeWithInfo:(id)info { 831 | NSInvocation *blockInvocation = [NSInvocation invocationWithMethodSignature:self.blockSignature]; 832 | NSInvocation *originalInvocation = info.originalInvocation; 833 | NSUInteger numberOfArguments = self.blockSignature.numberOfArguments; 834 | 835 | // Be extra paranoid. We already check that on hook registration. 836 | if (numberOfArguments > originalInvocation.methodSignature.numberOfArguments) { 837 | AspectLogError(@"Block has too many arguments. Not calling %@", info); 838 | return NO; 839 | } 840 | 841 | // The `self` of the block will be the AspectInfo. Optional. 842 | if (numberOfArguments > 1) { 843 | [blockInvocation setArgument:&info atIndex:1]; 844 | } 845 | 846 | void *argBuf = NULL; 847 | for (NSUInteger idx = 2; idx < numberOfArguments; idx++) { 848 | const char *type = [originalInvocation.methodSignature getArgumentTypeAtIndex:idx]; 849 | NSUInteger argSize; 850 | NSGetSizeAndAlignment(type, &argSize, NULL); 851 | 852 | if (!(argBuf = reallocf(argBuf, argSize))) { 853 | AspectLogError(@"Failed to allocate memory for block invocation."); 854 | return NO; 855 | } 856 | 857 | [originalInvocation getArgument:argBuf atIndex:idx]; 858 | [blockInvocation setArgument:argBuf atIndex:idx]; 859 | } 860 | 861 | [blockInvocation invokeWithTarget:self.block]; 862 | 863 | if (argBuf != NULL) { 864 | free(argBuf); 865 | } 866 | return YES; 867 | } 868 | 869 | - (NSString *)description { 870 | return [NSString stringWithFormat:@"<%@: %p, SEL:%@ object:%@ options:%tu block:%@ (#%tu args)>", self.class, self, NSStringFromSelector(self.selector), self.object, self.options, self.block, self.blockSignature.numberOfArguments]; 871 | } 872 | 873 | - (BOOL)remove { 874 | return aspect_remove(self, NULL); 875 | } 876 | 877 | @end 878 | 879 | /////////////////////////////////////////////////////////////////////////////////////////// 880 | #pragma mark - AspectsContainer 881 | 882 | @implementation AspectsContainer 883 | 884 | - (BOOL)hasAspects { 885 | return self.beforeAspects.count > 0 || self.insteadAspects.count > 0 || self.afterAspects.count > 0; 886 | } 887 | 888 | - (void)addAspect:(AspectIdentifier *)aspect withOptions:(AspectOptions)options { 889 | NSParameterAssert(aspect); 890 | NSUInteger position = options&AspectPositionFilter; 891 | switch (position) { 892 | case AspectPositionBefore: self.beforeAspects = [(self.beforeAspects ?:@[]) arrayByAddingObject:aspect]; break; 893 | case AspectPositionInstead: self.insteadAspects = [(self.insteadAspects?:@[]) arrayByAddingObject:aspect]; break; 894 | case AspectPositionAfter: self.afterAspects = [(self.afterAspects ?:@[]) arrayByAddingObject:aspect]; break; 895 | } 896 | } 897 | 898 | - (BOOL)removeAspect:(id)aspect { 899 | for (NSString *aspectArrayName in @[NSStringFromSelector(@selector(beforeAspects)), 900 | NSStringFromSelector(@selector(insteadAspects)), 901 | NSStringFromSelector(@selector(afterAspects))]) { 902 | NSArray *array = [self valueForKey:aspectArrayName]; 903 | NSUInteger index = [array indexOfObjectIdenticalTo:aspect]; 904 | if (array && index != NSNotFound) { 905 | NSMutableArray *newArray = [NSMutableArray arrayWithArray:array]; 906 | [newArray removeObjectAtIndex:index]; 907 | [self setValue:newArray forKey:aspectArrayName]; 908 | return YES; 909 | } 910 | } 911 | return NO; 912 | } 913 | 914 | - (NSString *)description { 915 | return [NSString stringWithFormat:@"<%@: %p, before:%@, instead:%@, after:%@>", self.class, self, self.beforeAspects, self.insteadAspects, self.afterAspects]; 916 | } 917 | 918 | @end 919 | 920 | /////////////////////////////////////////////////////////////////////////////////////////// 921 | #pragma mark - AspectInfo 922 | 923 | @implementation AspectInfo 924 | 925 | @synthesize arguments = _arguments; 926 | 927 | - (id)initWithInstance:(__unsafe_unretained id)instance invocation:(NSInvocation *)invocation { 928 | NSCParameterAssert(instance); 929 | NSCParameterAssert(invocation); 930 | if (self = [super init]) { 931 | _instance = instance; 932 | _originalInvocation = invocation; 933 | } 934 | return self; 935 | } 936 | 937 | - (NSArray *)arguments { 938 | // Lazily evaluate arguments, boxing is expensive. 939 | if (!_arguments) { 940 | _arguments = self.originalInvocation.aspects_arguments; 941 | } 942 | return _arguments; 943 | } 944 | 945 | @end 946 | --------------------------------------------------------------------------------