├── Dependencies ├── PLBlockIMP │ ├── iPhone Runtime │ │ └── PLBlockIMP.framework │ │ │ ├── Versions │ │ │ ├── Current │ │ │ └── A │ │ │ │ ├── PLBlockIMP │ │ │ │ ├── Resources │ │ │ │ ├── Info.plist │ │ │ │ └── gentramp.sh │ │ │ │ └── Headers │ │ │ │ ├── PLBlockIMP.h │ │ │ │ ├── blockimp.h │ │ │ │ └── trampoline_table.h │ │ │ ├── Headers │ │ │ ├── PLBlockIMP │ │ │ └── Resources │ └── Mac OS X Runtime │ │ └── PLBlockIMP.framework │ │ ├── Versions │ │ ├── Current │ │ └── A │ │ │ ├── PLBlockIMP │ │ │ ├── Resources │ │ │ ├── English.lproj │ │ │ │ └── InfoPlist.strings │ │ │ ├── Info.plist │ │ │ └── gentramp.sh │ │ │ └── Headers │ │ │ ├── PLBlockIMP.h │ │ │ ├── blockimp.h │ │ │ └── trampoline_table.h │ │ ├── Headers │ │ ├── Resources │ │ └── PLBlockIMP └── README.txt ├── PLPatchMasterTests ├── en.lproj │ └── InfoPlist.strings ├── PLPatchMasterTests-Info.plist └── PLPatchMasterTests.m ├── iOS Test Runner ├── en.lproj │ └── InfoPlist.strings ├── ViewController.h ├── AppDelegate.h ├── main.m ├── iOS Test Runner-Prefix.pch ├── ViewController.m ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Base.lproj │ ├── Main_iPhone.storyboard │ └── Main_iPad.storyboard ├── iOS Test Runner-Info.plist └── AppDelegate.m ├── PLPatchMaster ├── PLPatchMaster-Info.plist ├── NSObject+PLPatchMaster.h ├── PMLog.h ├── blockimp_arm64.tramp ├── PLPatchMaster.h ├── SymbolName.hpp ├── blockimp_x86_64.tramp ├── blockimp_x86_64_stret.tramp ├── blockimp_arm.tramp ├── blockimp_arm_stret.tramp ├── PLPatchMasterImpl.hpp ├── PLBlockLayout.h ├── NSObject+PLPatchMaster.m ├── blockimp_x86_32.tramp ├── blockimp_x86_32_stret.tramp ├── PLPatchMaster.mm ├── SymbolBinder.hpp ├── SymbolBinder.cpp └── PLPatchMasterImpl.mm ├── LICENSE └── README.md /Dependencies/PLBlockIMP/iPhone Runtime/PLBlockIMP.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/Mac OS X Runtime/PLBlockIMP.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/Mac OS X Runtime/PLBlockIMP.framework/Headers: -------------------------------------------------------------------------------- 1 | Versions/Current/Headers -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/iPhone Runtime/PLBlockIMP.framework/Headers: -------------------------------------------------------------------------------- 1 | Versions/Current/Headers -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/Mac OS X Runtime/PLBlockIMP.framework/Resources: -------------------------------------------------------------------------------- 1 | Versions/Current/Resources -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/iPhone Runtime/PLBlockIMP.framework/PLBlockIMP: -------------------------------------------------------------------------------- 1 | Versions/Current/PLBlockIMP -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/iPhone Runtime/PLBlockIMP.framework/Resources: -------------------------------------------------------------------------------- 1 | Versions/Current/Resources -------------------------------------------------------------------------------- /PLPatchMasterTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /iOS Test Runner/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/Mac OS X Runtime/PLBlockIMP.framework/PLBlockIMP: -------------------------------------------------------------------------------- 1 | Versions/Current/PLBlockIMP -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/iPhone Runtime/PLBlockIMP.framework/Versions/A/PLBlockIMP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plausiblelabs/plpatchmaster/HEAD/Dependencies/PLBlockIMP/iPhone Runtime/PLBlockIMP.framework/Versions/A/PLBlockIMP -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/Mac OS X Runtime/PLBlockIMP.framework/Versions/A/PLBlockIMP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plausiblelabs/plpatchmaster/HEAD/Dependencies/PLBlockIMP/Mac OS X Runtime/PLBlockIMP.framework/Versions/A/PLBlockIMP -------------------------------------------------------------------------------- /iOS Test Runner/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // iOS Test Runner 4 | // 5 | // Created by Landon Fuller on 2/17/14. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/Mac OS X Runtime/PLBlockIMP.framework/Versions/A/Resources/English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plausiblelabs/plpatchmaster/HEAD/Dependencies/PLBlockIMP/Mac OS X Runtime/PLBlockIMP.framework/Versions/A/Resources/English.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /iOS Test Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // iOS Test Runner 4 | // 5 | // Created by Landon Fuller on 2/17/14. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /iOS Test Runner/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // iOS Test Runner 4 | // 5 | // Created by Landon Fuller on 2/17/14. 6 | // 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /iOS Test Runner/iOS Test Runner-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /Dependencies/README.txt: -------------------------------------------------------------------------------- 1 | This directory contains source and binary dependencies: 2 | 3 | PLBlockIMP 4 | Description: 5 | Provides generic utilities for generating and managing custom assembly trampolines, 6 | including an independent implementation of imp_implementationWithBlock(). 7 | 8 | Version: 9 | PLBlockIMP 1.0-beta7 built from https://github.com/plausiblelabs/plblockimp 10 | 11 | License: 12 | MIT 13 | 14 | Modifications: 15 | None 16 | -------------------------------------------------------------------------------- /iOS Test Runner/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // iOS Test Runner 4 | // 5 | // Created by Landon Fuller on 2/17/14. 6 | // 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (void)viewDidLoad 18 | { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view, typically from a nib. 21 | } 22 | 23 | - (void)didReceiveMemoryWarning 24 | { 25 | [super didReceiveMemoryWarning]; 26 | // Dispose of any resources that can be recreated. 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /PLPatchMasterTests/PLPatchMasterTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | coop.plausible.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /PLPatchMaster/PLPatchMaster-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | coop.plausible.${PRODUCT_NAME:rfc1034Identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /iOS Test Runner/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "ipad", 20 | "size" : "29x29", 21 | "scale" : "1x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "40x40", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "76x76", 41 | "scale" : "1x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "2x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Author: Landon Fuller 2 | 3 | Copyright 2010-2014 Plausible Labs Cooperative, Inc. 4 | All rights reserved. 5 | 6 | Permission is hereby granted, free of charge, 7 | to any person obtaining a copy of this software and associated documentation 8 | files (the "Software"), to deal in the Software without restriction, 9 | including without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to permit 11 | persons to whom the Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /iOS Test Runner/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | } 46 | ], 47 | "info" : { 48 | "version" : 1, 49 | "author" : "xcode" 50 | } 51 | } -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/iPhone Runtime/PLBlockIMP.framework/Versions/A/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildMachineOSBuild 6 | 13B42 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundleExecutable 10 | PLBlockIMP 11 | CFBundleIdentifier 12 | com.yourcompany.PLBlockIMP 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | PLBlockIMP 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | DTCompiler 26 | com.apple.compilers.llvm.clang.1_0 27 | DTPlatformBuild 28 | 5A3005 29 | DTPlatformVersion 30 | GM 31 | DTSDKBuild 32 | 13A595 33 | DTSDKName 34 | macosx10.9 35 | DTXcode 36 | 0502 37 | DTXcodeBuild 38 | 5A3005 39 | 40 | 41 | -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/Mac OS X Runtime/PLBlockIMP.framework/Versions/A/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildMachineOSBuild 6 | 13B42 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundleExecutable 10 | PLBlockIMP 11 | CFBundleIdentifier 12 | com.yourcompany.PLBlockIMP 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | PLBlockIMP 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | DTCompiler 26 | com.apple.compilers.llvm.clang.1_0 27 | DTPlatformBuild 28 | 5A3005 29 | DTPlatformVersion 30 | GM 31 | DTSDKBuild 32 | 13A595 33 | DTSDKName 34 | macosx10.9 35 | DTXcode 36 | 0502 37 | DTXcodeBuild 38 | 5A3005 39 | 40 | 41 | -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/iPhone Runtime/PLBlockIMP.framework/Versions/A/Headers/PLBlockIMP.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Landon Fuller 3 | * 4 | * Copyright 2010-2011 Plausible Labs Cooperative, Inc. 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, 8 | * to any person obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without restriction, 10 | * including without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to permit 12 | * persons to whom the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | #pragma once 28 | #include "blockimp.h" -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/Mac OS X Runtime/PLBlockIMP.framework/Versions/A/Headers/PLBlockIMP.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Landon Fuller 3 | * 4 | * Copyright 2010-2011 Plausible Labs Cooperative, Inc. 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, 8 | * to any person obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without restriction, 10 | * including without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to permit 12 | * persons to whom the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | #pragma once 28 | #include "blockimp.h" -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/iPhone Runtime/PLBlockIMP.framework/Versions/A/Headers/blockimp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Landon Fuller 3 | * 4 | * Copyright 2010-2011 Plausible Labs Cooperative, Inc. 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, 8 | * to any person obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without restriction, 10 | * including without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to permit 12 | * persons to whom the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | #include 28 | 29 | extern IMP pl_imp_implementationWithBlock(void *block); 30 | extern void *pl_imp_getBlock(IMP anImp); 31 | extern BOOL pl_imp_removeBlock(IMP anImp); -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/Mac OS X Runtime/PLBlockIMP.framework/Versions/A/Headers/blockimp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Landon Fuller 3 | * 4 | * Copyright 2010-2011 Plausible Labs Cooperative, Inc. 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, 8 | * to any person obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without restriction, 10 | * including without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to permit 12 | * persons to whom the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | #include 28 | 29 | extern IMP pl_imp_implementationWithBlock(void *block); 30 | extern void *pl_imp_getBlock(IMP anImp); 31 | extern BOOL pl_imp_removeBlock(IMP anImp); -------------------------------------------------------------------------------- /iOS Test Runner/Base.lproj/Main_iPhone.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /iOS Test Runner/Base.lproj/Main_iPad.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /iOS Test Runner/iOS Test Runner-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | coop.plausible.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | Main_iPhone 29 | UIMainStoryboardFile~ipad 30 | Main_iPad 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /PLPatchMaster/NSObject+PLPatchMaster.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Landon Fuller 3 | * 4 | * Copyright (c) 2013-2015 Plausible Labs Cooperative, Inc. 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | 29 | #import 30 | 31 | @interface NSObject (PLPatchMaster) 32 | 33 | + (BOOL) pl_patchSelector: (SEL) selector withReplacementBlock: (id) replacementBlock; 34 | + (BOOL) pl_patchInstanceSelector: (SEL) selector withReplacementBlock: (id) replacementBlock; 35 | 36 | + (void) pl_patchFutureSelector: (SEL) selector withReplacementBlock: (id) replacementBlock; 37 | + (void) pl_patchFutureInstanceSelector: (SEL) selector withReplacementBlock: (id) replacementBlock; 38 | 39 | @end -------------------------------------------------------------------------------- /PLPatchMaster/PMLog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Landon Fuller 3 | * 4 | * Copyright (c) 2015 Landon Fuller . 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | 29 | #pragma once 30 | 31 | #include 32 | 33 | #define PMDoLog(_prefix, fmt, ...) do { \ 34 | fprintf(stderr, _prefix fmt "\n", ## __VA_ARGS__); \ 35 | } while(0) 36 | 37 | #define PMDebug(fmt, ...) do { \ 38 | PMDoLog("[PLPatchMaster] ", fmt, ## __VA_ARGS__); \ 39 | } while(0) 40 | 41 | #define PMLog(fmt, ...) do { \ 42 | PMDoLog("[PLPatchMaster] ", fmt, ## __VA_ARGS__); \ 43 | } while(0) 44 | 45 | #define PMFatal(fmt, ...) do { \ 46 | PMDoLog("[PLPatchMaster] FATAL ERROR: ", fmt, ## __VA_ARGS__); \ 47 | abort(); \ 48 | } while(0) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PLPatchMaster 2 | ----------- 3 | 4 | PLPatchMaster provides an easy-to-use block-based swizzling API, using the block trampoline 5 | library provided by [PLBlockIMP](https://opensource.plausible.coop/src/projects/PLTP/repos/plblockimp), 6 | and a set of custom assembly trampolines for ARMv7, ARMv7s, ARM64, and x86-64. 7 | 8 | PLPatchMaster can apply patches to currently loaded classes, as well as classes 9 | that have not yet been loaded (eg, will be loaded at a future time) by registering 10 | a listener for dyld image events. 11 | 12 | Use it at your own risk; swizzling in production software is rarely, if ever, a particularly 13 | good idea. 14 | 15 | PLPatchMaster is released under the MIT license. 16 | 17 | Basic Use 18 | ----------- 19 | 20 | Use a block to swizzle -[UIWindow sendEvent:]: 21 | 22 | [UIWindow pl_patchInstanceSelector: @selector(sendEvent:) withReplacementBlock: ^(PLPatchIMP *patch, UIEvent *event) { 23 | NSObject *obj = PLPatchGetSelf(patch); 24 | 25 | // Ignore 'remote control' events 26 | if (event.type == UIEventTypeRemoteControl) 27 | return; 28 | 29 | // Forward everything else 30 | return PLPatchIMPFoward(patch, void (*)(id, SEL, UIEvent *), event); 31 | }]; 32 | 33 | Advanced Use 34 | ------------ 35 | 36 | Register a future patch on a class that has not been loaded yet (eg, it's loaded dynamically at runtime): 37 | 38 | [[PLPatchMaster master] patchFutureClassWithName: @"ExFATCameraDeviceManager" withReplacementBlock: ^(PLPatchIMP *patch, id *arg) { 39 | /* Forward the message to the next IMP */ 40 | PLPatchIMPFoward(patch, void (*)(id, SEL, id *)); 41 | 42 | /* Log the event */ 43 | NSLog(@"FAT camera device ejected"); 44 | }]; 45 | 46 | PLPatchMaster registers a listener for dyld image events, and will automatically swizzle the target class when 47 | its Mach-O image is loaded. -------------------------------------------------------------------------------- /iOS Test Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // iOS Test Runner 4 | // 5 | // Created by Landon Fuller on 2/17/14. 6 | // 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/iPhone Runtime/PLBlockIMP.framework/Versions/A/Headers/trampoline_table.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Landon Fuller 3 | * 4 | * Copyright 2010-2011 Plausible Labs Cooperative, Inc. 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, 8 | * to any person obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without restriction, 10 | * including without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to permit 12 | * persons to whom the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | #pragma once 28 | 29 | #ifdef PL_BLOCKIMP_PRIVATE 30 | 31 | #include 32 | #include 33 | 34 | typedef struct pl_trampoline_table pl_trampoline_table; 35 | typedef struct pl_trampoline pl_trampoline; 36 | 37 | /* 38 | * Trampoline table configuration 39 | */ 40 | typedef struct pl_trampoline_table_config { 41 | /* The trampoline size */ 42 | uint32_t trampoline_size; 43 | 44 | /* The page offset at which the trampolines are located. */ 45 | uint32_t page_offset; 46 | 47 | /* The number of trampolines allocated per page. */ 48 | uint32_t trampoline_count; 49 | 50 | /** The template code page. */ 51 | void *template_page; 52 | } pl_trampoline_table_config; 53 | 54 | /* 55 | * A double-linked list of trampoline table entries. 56 | */ 57 | struct pl_trampoline_table { 58 | /* Table configuration */ 59 | pl_trampoline_table_config *config; 60 | 61 | /* Contigious writable and executable pages */ 62 | vm_address_t data_page; 63 | vm_address_t trampoline_page; 64 | 65 | /* free list tracking */ 66 | uint16_t free_count; 67 | pl_trampoline *free_list; 68 | pl_trampoline *free_list_pool; 69 | 70 | pl_trampoline_table *prev; 71 | pl_trampoline_table *next; 72 | }; 73 | 74 | /* 75 | * A linked list of trampoline table entries. 76 | */ 77 | struct pl_trampoline { 78 | /* The actual trampoline. */ 79 | void *(*trampoline)(); 80 | 81 | /** The table in which the entry is allocated. */ 82 | pl_trampoline_table *table; 83 | 84 | /* Next entry in the trampoline list. */ 85 | pl_trampoline *next; 86 | }; 87 | 88 | pl_trampoline *pl_trampoline_alloc (pl_trampoline_table_config *config, pthread_mutex_t *lock, pl_trampoline_table **table_head); 89 | void pl_trampoline_free (pthread_mutex_t *lock, pl_trampoline_table **table_head, pl_trampoline *tramp); 90 | void *pl_trampoline_data_ptr (void *code_ptr); 91 | 92 | #endif /* PL_BLOCKIMP_PRIVATE */ -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/Mac OS X Runtime/PLBlockIMP.framework/Versions/A/Headers/trampoline_table.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Landon Fuller 3 | * 4 | * Copyright 2010-2011 Plausible Labs Cooperative, Inc. 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, 8 | * to any person obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without restriction, 10 | * including without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to permit 12 | * persons to whom the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | #pragma once 28 | 29 | #ifdef PL_BLOCKIMP_PRIVATE 30 | 31 | #include 32 | #include 33 | 34 | typedef struct pl_trampoline_table pl_trampoline_table; 35 | typedef struct pl_trampoline pl_trampoline; 36 | 37 | /* 38 | * Trampoline table configuration 39 | */ 40 | typedef struct pl_trampoline_table_config { 41 | /* The trampoline size */ 42 | uint32_t trampoline_size; 43 | 44 | /* The page offset at which the trampolines are located. */ 45 | uint32_t page_offset; 46 | 47 | /* The number of trampolines allocated per page. */ 48 | uint32_t trampoline_count; 49 | 50 | /** The template code page. */ 51 | void *template_page; 52 | } pl_trampoline_table_config; 53 | 54 | /* 55 | * A double-linked list of trampoline table entries. 56 | */ 57 | struct pl_trampoline_table { 58 | /* Table configuration */ 59 | pl_trampoline_table_config *config; 60 | 61 | /* Contigious writable and executable pages */ 62 | vm_address_t data_page; 63 | vm_address_t trampoline_page; 64 | 65 | /* free list tracking */ 66 | uint16_t free_count; 67 | pl_trampoline *free_list; 68 | pl_trampoline *free_list_pool; 69 | 70 | pl_trampoline_table *prev; 71 | pl_trampoline_table *next; 72 | }; 73 | 74 | /* 75 | * A linked list of trampoline table entries. 76 | */ 77 | struct pl_trampoline { 78 | /* The actual trampoline. */ 79 | void *(*trampoline)(); 80 | 81 | /** The table in which the entry is allocated. */ 82 | pl_trampoline_table *table; 83 | 84 | /* Next entry in the trampoline list. */ 85 | pl_trampoline *next; 86 | }; 87 | 88 | pl_trampoline *pl_trampoline_alloc (pl_trampoline_table_config *config, pthread_mutex_t *lock, pl_trampoline_table **table_head); 89 | void pl_trampoline_free (pthread_mutex_t *lock, pl_trampoline_table **table_head, pl_trampoline *tramp); 90 | void *pl_trampoline_data_ptr (void *code_ptr); 91 | 92 | #endif /* PL_BLOCKIMP_PRIVATE */ -------------------------------------------------------------------------------- /PLPatchMaster/blockimp_arm64.tramp: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ----------------------------------------------------------------------- 4 | # Copyright (c) 2010-2014, Plausible Labs Cooperative, Inc. 5 | # All Rights Reserved. 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining 8 | # a copy of this software and associated documentation files (the 9 | # ``Software''), to deal in the Software without restriction, including 10 | # without limitation the rights to use, copy, modify, merge, publish, 11 | # distribute, sublicense, and/or sell copies of the Software, and to 12 | # permit persons to whom the Software is furnished to do so, subject to 13 | # the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included 16 | # in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | # DEALINGS IN THE SOFTWARE. 26 | # ----------------------------------------------------------------------- 27 | 28 | # Supported architectures 29 | check_architecture () { 30 | case $1 in 31 | arm64) 32 | return 1 33 | ;; 34 | *) 35 | return 0 36 | ;; 37 | esac 38 | } 39 | 40 | # Page size 41 | PAGE_SIZE="16384" 42 | 43 | # The name of this page 44 | PAGE_NAME=pl_blockimp_patch_table_page 45 | 46 | # Prefix to be placed at the start of the trampoline page 47 | trampoline_prefix () { 48 | asm << 'EOF' 49 | _block_tramp_dispatch: 50 | // trampoline address+8 is in lr -- calculate our config page address 51 | sub x12, lr, #0x8 52 | sub x12, x12, #0x4000 53 | 54 | // restore the link register 55 | mov lr, x13 56 | 57 | // Set up our function stack 58 | stp fp, lr, [sp, #-16]! 59 | add fp, sp, 0 60 | sub sp, sp, #32 // Three pointer struct 61 | 62 | // Insert 'self' in the first struct position 63 | str x0, [sp] 64 | 65 | // Load the original IMP from the config page, and move to the second struct position 66 | ldr x0, [x12, #16] 67 | str x0, [sp, #8] 68 | 69 | // Load the original SEL from the config page, and move to the third struct position 70 | ldr x0, [x12, #24] 71 | str x0, [sp, #16] 72 | 73 | // Move our struct to the second parameter, overwriting IMP 74 | mov x1, sp 75 | 76 | // Load the block reference from the config page, and move to the first parameter 77 | ldr x0, [x12] 78 | 79 | // Call the block fptr 80 | ldr x13, [x0, #16] 81 | blr x13 82 | 83 | // Finished! 84 | add sp, fp, 0 85 | ldp fp, lr, [sp], #16 86 | ret lr 87 | EOF 88 | } 89 | 90 | # Generate a single trampoline 91 | trampoline () { 92 | asm << 'EOF' 93 | # Save lr, then jump to the shared prefix implementation 94 | mov x13, lr 95 | bl _block_tramp_dispatch; 96 | // align to 32 bytes (to fit the size of our config entries) 97 | .align 5 98 | EOF 99 | } -------------------------------------------------------------------------------- /PLPatchMaster/PLPatchMaster.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Landon Fuller 3 | * 4 | * Copyright (c) 2013-2015 Plausible Labs Cooperative, Inc. 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | 29 | #import 30 | #import "NSObject+PLPatchMaster.h" 31 | 32 | /** 33 | * IMP patch state, as passed to a replacement block. 34 | */ 35 | typedef struct PLPatchIMP { 36 | /** The original message target. */ 37 | void *self; 38 | 39 | /** The original IMP (eg, the IMP prior to patching) */ 40 | IMP origIMP; 41 | 42 | /** The original SEL. */ 43 | SEL selector; 44 | } PLPatchIMP; 45 | 46 | /** 47 | * Forward a message received by a PLPatchMaster patch block. 48 | * 49 | * @param patch The PLPatchIMP patch argument. 50 | * @param func_type The function type to which the IMP should be cast. 51 | * @param ... All method arguments (Do not include self or _cmd). 52 | */ 53 | #define PLPatchIMPFoward(patch, func_type, ...) ((func_type)patch->origIMP)((__bridge id) patch->self, patch->selector, ##__VA_ARGS__) 54 | 55 | /** 56 | * Return the original 'self' instance from a PLPatchMaster patch block. 57 | * 58 | * @param patch The PLPatchIMP patch argument. 59 | */ 60 | #define PLPatchGetSelf(patch) ((__bridge id) patch->self) 61 | 62 | @class PLPatchMasterImpl; 63 | 64 | extern NSString *kPLPatchImageFoundation; 65 | extern NSString *kPLPatchImageCoreFoundation; 66 | extern NSString *kPLPatchImageLibSystem; 67 | 68 | @interface PLPatchMaster : NSObject { 69 | PLPatchMasterImpl *_impl; 70 | } 71 | 72 | + (instancetype) master; 73 | 74 | - (BOOL) patchClass: (Class) cls selector: (SEL) selector replacementBlock: (id) replacementBlock; 75 | - (BOOL) patchInstancesWithClass: (Class) cls selector: (SEL) selector replacementBlock: (id) replacementBlock; 76 | 77 | - (void) patchFutureClassWithName: (NSString *) className selector: (SEL) selector replacementBlock: (id) replacementBlock; 78 | - (void) patchInstancesWithFutureClassName: (NSString *) className selector: (SEL) selector replacementBlock: (id) replacementBlock; 79 | 80 | - (void) rebindSymbol: (NSString *) symbol fromImage: (NSString *) library replacementAddress: (uintptr_t) replacementAddress; 81 | - (void) rebindSymbol: (NSString *) symbol replacementAddress: (uintptr_t) replacementAddress; 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /PLPatchMaster/SymbolName.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Landon Fuller 3 | * 4 | * Copyright (c) 2015 Landon Fuller . 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | 29 | #pragma once 30 | 31 | #include "PMLog.h" 32 | 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | #include 44 | #include 45 | 46 | namespace patchmaster { 47 | /** 48 | * A single-level or two-level namespaced symbol reference. 49 | */ 50 | class SymbolName { 51 | public: 52 | /** 53 | * Construct a new symbol name. 54 | * 55 | * @param image The install name of the image that exports this symbol, or an empty path to signify 56 | * single-level lookup. 57 | * @param symbol The symbol name. 58 | */ 59 | SymbolName (const char *image, const char *symbol) : _image(image), _symbol(symbol) {} 60 | 61 | /** Return the install name of the image that exports this symbol, or an empty string. If the path is empty, 62 | * single-level namespacing is assumed. */ 63 | const char *image () const { return _image; } 64 | 65 | /** Return the symbol name. */ 66 | const char *symbol () const { return _symbol; } 67 | 68 | /** 69 | * Return true if this symbol name matches the provided name. 70 | */ 71 | bool match (const SymbolName &other) const { 72 | /* If symbol names don't match, there's nothing else to test. */ 73 | if (strcmp(other._symbol, _symbol) != 0) 74 | return false; 75 | 76 | /* If either image is zero-length, they'll match on the first matching symbol regardless of the image. */ 77 | if (*other._image == '\0' || *_image == '\0') 78 | return true; 79 | 80 | /* Check for an image name match */ 81 | return strcmp(_image, other._image) == 0; 82 | } 83 | 84 | private: 85 | /** Install name, or empty string */ 86 | const char *_image; 87 | 88 | /** Symbol name. */ 89 | const char *_symbol; 90 | }; 91 | } /* namespace patchmaster */ -------------------------------------------------------------------------------- /PLPatchMaster/blockimp_x86_64.tramp: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ----------------------------------------------------------------------- 4 | # Copyright (c) 2010-2014, Plausible Labs Cooperative, Inc. 5 | # All Rights Reserved. 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining 8 | # a copy of this software and associated documentation files (the 9 | # ``Software''), to deal in the Software without restriction, including 10 | # without limitation the rights to use, copy, modify, merge, publish, 11 | # distribute, sublicense, and/or sell copies of the Software, and to 12 | # permit persons to whom the Software is furnished to do so, subject to 13 | # the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included 16 | # in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | # DEALINGS IN THE SOFTWARE. 26 | # ----------------------------------------------------------------------- 27 | 28 | # Supported architectures 29 | check_architecture () { 30 | case $1 in 31 | x86_64) 32 | return 1 33 | ;; 34 | *) 35 | return 0 36 | ;; 37 | esac 38 | } 39 | 40 | # Page size 41 | PAGE_SIZE="4096" 42 | 43 | # The name of this page 44 | PAGE_NAME=pl_blockimp_patch_table_page 45 | 46 | # Prefix to be placed at the start of the trampoline page 47 | trampoline_prefix () { 48 | asm << 'EOF' 49 | _block_tramp_dispatch: 50 | // Compute config page location 51 | pop %r11 52 | and $0xffffffffffffffe0, %r11 // truncate to the trampoline start (each is 32 bytes) 53 | sub $0x1000, %r11 // load the config location 54 | 55 | // Set up our function stack 56 | pushq %rbp 57 | movq %rsp, %rbp 58 | subq $32, %rsp // Three pointer struct, plus alignment 59 | 60 | // Insert 'self' in the first struct position 61 | movq %rdi, (%rsp) 62 | 63 | // Load the original IMP from the config page, and move to the second struct position 64 | movq 0x10(%r11), %rdi 65 | movq %rdi, 0x8(%rsp) 66 | 67 | // Load the original SEL from the config page, and move to the third struct position 68 | movq 0x18(%r11), %rdi 69 | movq %rdi, 0x10(%rsp) 70 | 71 | // Move our struct to the second parameter, overwriting IMP 72 | movq %rsp, %rsi 73 | 74 | // Load the block reference from the config page, and move to the first parameter 75 | movq (%r11), %rdi 76 | 77 | // Jump to the block fptr 78 | callq *0x10(%rdi) 79 | 80 | // Finished! 81 | addq $32, %rsp 82 | popq %rbp 83 | ret 84 | 85 | .align 5 // align the trampolines at 32 bytes (required to fit the config pages) 86 | EOF 87 | } 88 | 89 | # Generate a single trampoline 90 | trampoline () { 91 | asm << 'EOF' 92 | // Call into the dispatcher, placing our return address on the stack. 93 | call _block_tramp_dispatch # 5 bytes 94 | .align 5 // align the trampolines at 32 bytes (required to fit the config pages) 95 | EOF 96 | } -------------------------------------------------------------------------------- /PLPatchMaster/blockimp_x86_64_stret.tramp: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ----------------------------------------------------------------------- 4 | # Copyright (c) 2010-2014, Plausible Labs Cooperative, Inc. 5 | # All Rights Reserved. 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining 8 | # a copy of this software and associated documentation files (the 9 | # ``Software''), to deal in the Software without restriction, including 10 | # without limitation the rights to use, copy, modify, merge, publish, 11 | # distribute, sublicense, and/or sell copies of the Software, and to 12 | # permit persons to whom the Software is furnished to do so, subject to 13 | # the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included 16 | # in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | # DEALINGS IN THE SOFTWARE. 26 | # ----------------------------------------------------------------------- 27 | 28 | # Supported architectures 29 | check_architecture () { 30 | case $1 in 31 | x86_64) 32 | return 1 33 | ;; 34 | *) 35 | return 0 36 | ;; 37 | esac 38 | } 39 | 40 | # Page size 41 | PAGE_SIZE="4096" 42 | 43 | # The name of this page 44 | PAGE_NAME=pl_blockimp_patch_table_stret_page 45 | 46 | # Prefix to be placed at the start of the trampoline page 47 | trampoline_prefix () { 48 | asm << 'EOF' 49 | _block_tramp_dispatch: 50 | // Compute config page location 51 | pop %r11 52 | and $0xffffffffffffffe0, %r11 // truncate to the trampoline start (each is 32 bytes) 53 | sub $0x1000, %r11 // load the config location 54 | 55 | // Set up our function stack 56 | pushq %rbp 57 | movq %rsp, %rbp 58 | subq $32, %rsp // Three pointer struct, plus alignment 59 | 60 | // Insert 'self' in the first struct position 61 | movq %rsi, (%rsp) 62 | 63 | // Load the original IMP from the config page, and move to the second struct position 64 | movq 0x10(%r11), %rsi 65 | movq %rsi, 0x8(%rsp) 66 | 67 | // Load the original SEL from the config page, and move to the third struct position 68 | movq 0x18(%r11), %rsi 69 | movq %rsi, 0x10(%rsp) 70 | 71 | // Move our struct to the third parameter, overwriting IMP 72 | movq %rsp, %rdx 73 | 74 | // Load the block reference from the config page, and move to the second parameter 75 | movq (%r11), %rsi 76 | 77 | // Jump to the block fptr 78 | callq *0x10(%rsi) 79 | 80 | // Finished! 81 | addq $32, %rsp 82 | popq %rbp 83 | ret 84 | 85 | .align 5 // align the trampolines at 32 bytes (required to fit the config pages) 86 | EOF 87 | } 88 | 89 | # Generate a single trampoline 90 | trampoline () { 91 | asm << 'EOF' 92 | // Call into the dispatcher, placing our return address on the stack. 93 | call _block_tramp_dispatch # 5 bytes 94 | .align 5 // align the trampolines at 32 bytes (required to fit the config pages) 95 | EOF 96 | } -------------------------------------------------------------------------------- /PLPatchMaster/blockimp_arm.tramp: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ----------------------------------------------------------------------- 4 | # Copyright (c) 2010-2014, Plausible Labs Cooperative, Inc. 5 | # All Rights Reserved. 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining 8 | # a copy of this software and associated documentation files (the 9 | # ``Software''), to deal in the Software without restriction, including 10 | # without limitation the rights to use, copy, modify, merge, publish, 11 | # distribute, sublicense, and/or sell copies of the Software, and to 12 | # permit persons to whom the Software is furnished to do so, subject to 13 | # the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included 16 | # in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | # DEALINGS IN THE SOFTWARE. 26 | # ----------------------------------------------------------------------- 27 | 28 | # Supported architectures 29 | check_architecture () { 30 | case $1 in 31 | armv6) 32 | return 1 33 | ;; 34 | armv7) 35 | return 1 36 | ;; 37 | armv7s) 38 | return 1 39 | ;; 40 | *) 41 | return 0 42 | ;; 43 | esac 44 | } 45 | 46 | # Page size 47 | PAGE_SIZE="4096" 48 | 49 | # The name of this page 50 | PAGE_NAME=pl_blockimp_patch_table_page 51 | 52 | # Prefix to be placed at the start of the trampoline page 53 | trampoline_prefix () { 54 | asm << 'EOF' 55 | _block_tramp_dispatch: 56 | # trampoline address+8 is in r12 -- calculate our config page address 57 | sub r12, #0x8 58 | sub r12, #0x1000 59 | 60 | # Set up our function stack 61 | push {r7, lr} 62 | mov r7, sp 63 | 64 | # Three pointer struct 65 | sub sp, sp, #0x10 66 | 67 | # Insert 'self' in the first struct position 68 | str r0, [sp] 69 | 70 | # Load the original IMP from the config page, and move to the second struct position 71 | ldr r0, [r12, #8] 72 | str r0, [sp, #4] 73 | 74 | # Load the original SEL from the config page, and move to the third struct position 75 | ldr r0, [r12, #12] 76 | str r0, [sp, #8] 77 | 78 | # Move our struct to the second parameter, overwriting IMP 79 | mov r1, sp 80 | 81 | # Load the block reference from the config page, and move to the first parameter 82 | ldr r0, [r12] 83 | 84 | # Call the block fptr 85 | ldr r12, [r0, #0xc] 86 | blx r12 87 | 88 | # Finished! 89 | mov sp, r7 90 | pop {r7, pc} 91 | EOF 92 | } 93 | 94 | # Generate a single trampoline 95 | trampoline () { 96 | asm << 'EOF' 97 | # Save pc+8, then jump to the shared prefix implementation 98 | mov r12, pc 99 | b _block_tramp_dispatch; 100 | # align to 32 bytes (to fit the size of our config entries) 101 | .align 5 102 | EOF 103 | } -------------------------------------------------------------------------------- /PLPatchMaster/blockimp_arm_stret.tramp: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ----------------------------------------------------------------------- 4 | # Copyright (c) 2010-2014, Plausible Labs Cooperative, Inc. 5 | # All Rights Reserved. 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining 8 | # a copy of this software and associated documentation files (the 9 | # ``Software''), to deal in the Software without restriction, including 10 | # without limitation the rights to use, copy, modify, merge, publish, 11 | # distribute, sublicense, and/or sell copies of the Software, and to 12 | # permit persons to whom the Software is furnished to do so, subject to 13 | # the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included 16 | # in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | # DEALINGS IN THE SOFTWARE. 26 | # ----------------------------------------------------------------------- 27 | 28 | # Supported architectures 29 | check_architecture () { 30 | case $1 in 31 | armv6) 32 | return 1 33 | ;; 34 | armv7) 35 | return 1 36 | ;; 37 | armv7s) 38 | return 1 39 | ;; 40 | *) 41 | return 0 42 | ;; 43 | esac 44 | } 45 | 46 | # Page size 47 | PAGE_SIZE="4096" 48 | 49 | # The name of this page 50 | PAGE_NAME=pl_blockimp_patch_table_stret_page 51 | 52 | # Prefix to be placed at the start of the trampoline page 53 | trampoline_prefix () { 54 | asm << 'EOF' 55 | _block_tramp_dispatch: 56 | # trampoline address+8 is in r12 -- calculate our config page address 57 | sub r12, #0x8 58 | sub r12, #0x1000 59 | 60 | # Set up our function stack 61 | push {r7, lr} 62 | mov r7, sp 63 | 64 | # Three pointer struct 65 | sub sp, sp, #0x10 66 | 67 | # Insert 'self' in the first struct position 68 | str r1, [sp] 69 | 70 | # Load the original IMP from the config page, and move to the second struct position 71 | ldr r1, [r12, #8] 72 | str r1, [sp, #4] 73 | 74 | # Load the original SEL from the config page, and move to the third struct position 75 | ldr r1, [r12, #12] 76 | str r1, [sp, #8] 77 | 78 | # Move our struct to the third parameter, overwriting IMP 79 | mov r2, sp 80 | 81 | # Load the block reference from the config page, and move to the second parameter 82 | ldr r1, [r12] 83 | 84 | # Call the block fptr 85 | ldr r12, [r1, #0xc] 86 | blx r12 87 | 88 | # Finished! 89 | mov sp, r7 90 | pop {r7, pc} 91 | EOF 92 | } 93 | 94 | # Generate a single trampoline 95 | trampoline () { 96 | asm << 'EOF' 97 | # Save pc+8, then jump to the shared prefix implementation 98 | mov r12, pc 99 | b _block_tramp_dispatch; 100 | # align to 32 bytes (to fit the size of our config entries) 101 | .align 5 102 | 103 | EOF 104 | } -------------------------------------------------------------------------------- /PLPatchMaster/PLPatchMasterImpl.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Landon Fuller 3 | * 4 | * Copyright (c) 2013-2015 Plausible Labs Cooperative, Inc. 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | 29 | #import 30 | #import 31 | #import "SymbolBinder.hpp" 32 | 33 | using namespace patchmaster; 34 | 35 | /** 36 | * @internal 37 | * 38 | * Table of symbol-based patches; maps the single-level symbol name to the 39 | * fully qualified two-level SymbolNames and associated patch value. 40 | */ 41 | typedef std::map>> PatchTable; 42 | 43 | @interface PLPatchMasterImpl : NSObject { 44 | /** Lock that must be held when mutating or accessing internal state */ 45 | OSSpinLock _lock; 46 | 47 | IMP _callbackFunc; 48 | 49 | /** 50 | * Table of symbol-based patches; maps the single-level symbol name to the 51 | * fully qualified two-level SymbolNames and associated patch value. 52 | */ 53 | PatchTable _symbolPatches; 54 | 55 | /** Maps class -> set -> selector names. Used to keep track of patches that have already been made, 56 | * and thus do not require a _restoreBlock to be registered */ 57 | NSMutableDictionary *_classPatches; 58 | 59 | /** Maps class -> set -> selector names. Used to keep track of patches that have already been made, 60 | * and thus do not require a _restoreBlock to be registered */ 61 | NSMutableDictionary *_instancePatches; 62 | 63 | /** An array of blocks to be executed on dynamic library load; the blocks are responsible 64 | * for applying any pending patches to the newly loaded library */ 65 | NSMutableArray *_pendingPatches; 66 | 67 | /* An array of zero-arg blocks that, when executed, will reverse 68 | * all previously patched methods. */ 69 | NSMutableArray *_restoreBlocks; 70 | } 71 | 72 | - (BOOL) patchClass: (Class) cls selector: (SEL) selector replacementBlock: (id) replacementBlock; 73 | - (BOOL) patchInstancesWithClass: (Class) cls selector: (SEL) selector replacementBlock: (id) replacementBlock; 74 | 75 | - (void) patchFutureClassWithName: (NSString *) className selector: (SEL) selector replacementBlock: (id) replacementBlock; 76 | - (void) patchInstancesWithFutureClassName: (NSString *) className selector: (SEL) selector replacementBlock: (id) replacementBlock; 77 | 78 | - (void) rebindSymbol: (NSString *) symbol fromImage: (NSString *) library replacementAddress: (uintptr_t) replacementAddress; 79 | - (void) rebindSymbol: (NSString *) symbol replacementAddress: (uintptr_t) replacementAddress; 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /PLPatchMasterTests/PLPatchMasterTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PLPatchMasterTests.m 3 | // PLPatchMasterTests 4 | // 5 | // Created by Landon Fuller on 2/17/14. 6 | // 7 | // 8 | 9 | #import 10 | #import "PLPatchMaster.h" 11 | 12 | @interface PLPatchMasterTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation PLPatchMasterTests 17 | 18 | - (NSString *) patchTargetWithArgument: (NSString *) expected { 19 | return expected; 20 | } 21 | 22 | - (void) testBasic { 23 | [PLPatchMasterTests pl_patchInstanceSelector: @selector(patchTargetWithArgument:) withReplacementBlock: ^(PLPatchIMP *patch, NSString *expected) { 24 | NSObject *obj = PLPatchGetSelf(patch); 25 | XCTAssertTrue(obj == self, @"Incorrect 'self'"); 26 | NSString *originalResult = PLPatchIMPFoward(patch, NSString *(*)(id, SEL, NSString *), expected); 27 | return [NSString stringWithFormat: @"[PATCHED]: %@", originalResult]; 28 | }]; 29 | 30 | XCTAssertEqualObjects(@"[PATCHED]: Result", [self patchTargetWithArgument: @"Result"], @"Incorrect value returned"); 31 | } 32 | 33 | struct stret_return { 34 | char value[30]; 35 | }; 36 | 37 | - (struct stret_return) stretPatchTargetWithArgument: (NSString *) expected { 38 | struct stret_return retval; 39 | const char *cstr = [expected UTF8String]; 40 | 41 | assert(strlen(cstr) < sizeof(retval.value)); 42 | strlcpy(retval.value, cstr, sizeof(retval.value)); 43 | 44 | return retval; 45 | } 46 | 47 | - (void) testStret { 48 | [PLPatchMasterTests pl_patchInstanceSelector: @selector(stretPatchTargetWithArgument:) withReplacementBlock: ^(PLPatchIMP *patch, NSString *expected) { 49 | NSObject *obj = PLPatchGetSelf(patch); 50 | XCTAssertTrue(obj == self, @"Incorrect 'self'"); 51 | 52 | struct stret_return retval = PLPatchIMPFoward(patch, struct stret_return (*)(id, SEL, NSString *), expected); 53 | retval.value[0] = 'j'; 54 | return retval; 55 | }]; 56 | 57 | struct stret_return ret; 58 | ret.value[0] = 'f'; 59 | ret.value[1] = '\0'; 60 | 61 | ret = [self stretPatchTargetWithArgument: @"hello"]; 62 | XCTAssertTrue(strcmp(ret.value, "jello") == 0, @"Incorrect value returned: '%s'", ret.value); 63 | } 64 | 65 | static CFIndex patched_CFGetRetainCount (CFTypeRef ref) { 66 | return 0xABBA; 67 | } 68 | 69 | - (void) testRebindSymbol { 70 | /* Fetch the current function pointer */ 71 | CFIndex (*orig)(CFTypeRef) = &CFGetRetainCount; 72 | 73 | /* Rebind */ 74 | [[PLPatchMaster master] rebindSymbol: @"_CFGetRetainCount" fromImage: kPLPatchImageCoreFoundation replacementAddress: (uintptr_t) patched_CFGetRetainCount]; 75 | XCTAssertEqual(0xABBA, CFGetRetainCount((__bridge CFTypeRef) [NSArray array])); 76 | 77 | /* Restore the original */ 78 | [[PLPatchMaster master] rebindSymbol: @"_CFGetRetainCount" fromImage: kPLPatchImageCoreFoundation replacementAddress: (uintptr_t) orig]; 79 | XCTAssertNotEqual(0xABBA, CFGetRetainCount((__bridge CFTypeRef) [NSArray array])); 80 | } 81 | 82 | #if !defined(__i386__) || (defined(__i386__) && TARGET_OS_IPHONE) 83 | - (void) testRebindClassSymbol { 84 | /* This test only works on the ObjC 2.0 runtime; the ObjC 1.0 runtime performs 85 | * class binding itself, rather than via dyld */ 86 | NSString *classSymbol = @"_OBJC_CLASS_$_NSAttributedString"; 87 | 88 | /* Fetch the current class pointer */ 89 | void *orig = (__bridge void *) [NSAttributedString class]; 90 | XCTAssertEqualObjects([NSAttributedString class], (__bridge id) orig); 91 | 92 | /* Rebind */ 93 | [[PLPatchMaster master] rebindSymbol: classSymbol fromImage: @"Foundation" replacementAddress: (uintptr_t) [NSArray class]]; 94 | XCTAssertEqualObjects([NSAttributedString class], [NSArray class]); 95 | XCTAssertNotEqualObjects([NSAttributedString class], (__bridge id) orig); 96 | 97 | /* Restore the original */ 98 | [[PLPatchMaster master] rebindSymbol: classSymbol fromImage: @"Foundation" replacementAddress: (uintptr_t) orig]; 99 | XCTAssertEqualObjects([NSAttributedString class], (__bridge id) orig); 100 | } 101 | #endif 102 | 103 | @end 104 | 105 | -------------------------------------------------------------------------------- /PLPatchMaster/PLBlockLayout.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Block ABI-defined Structures 3 | * Originally acquired frm PLBlocks and compiler_rt 4 | * 5 | * Copyright 2008 - 2009 Apple, Inc. 6 | * Copyright 2009 - 2013 Plausible Labs Cooperative, Inc. 7 | * 8 | * Permission is hereby granted, free of charge, 9 | * to any person obtaining a copy of this software and associated documentation 10 | * files (the "Software"), to deal in the Software without restriction, 11 | * including without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to permit 13 | * persons to whom the Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | * 27 | */ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | /* 34 | * Block Flags 35 | */ 36 | typedef enum { 37 | /** 16-bit block reference count. */ 38 | BLOCK_REFCOUNT_MASK = (0xffff), 39 | 40 | BLOCK_NEEDS_FREE = (1 << 24), 41 | BLOCK_HAS_COPY_DISPOSE = (1 << 25), 42 | 43 | /** Helpers have C++ code. */ 44 | BLOCK_HAS_CTOR = (1 << 26), 45 | 46 | BLOCK_IS_GC = (1 << 27), 47 | BLOCK_IS_GLOBAL = (1 << 28), 48 | 49 | /** Block returns its aggregate value in memory (ie, the block has a structure return type). */ 50 | BLOCK_USE_STRET = (1 << 29), 51 | } block_flags_t; 52 | 53 | 54 | /* 55 | * Block field flags. 56 | */ 57 | typedef enum { 58 | // see function implementation for a more complete description of these fields and combinations 59 | BLOCK_FIELD_IS_OBJECT = 3, // id, NSObject, __attribute__((NSObject)), block, ... 60 | BLOCK_FIELD_IS_BLOCK = 7, // a block variable 61 | BLOCK_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable 62 | BLOCK_FIELD_IS_WEAK = 16, // declared __weak, only used in byref copy helpers 63 | BLOCK_BYREF_CALLER = 128, // called from __block (byref) copy/dispose support routines. 64 | } block_field_flags_t; 65 | 66 | /* 67 | * Block description. 68 | * 69 | * Block descriptions are shared across all instances of a block, and 70 | * provide basic information on the block size, as well as pointers 71 | * to any helper functions necessary to copy or dispose of the block. 72 | */ 73 | struct Block_descriptor { 74 | /** Reserved value */ 75 | unsigned long int reserved; 76 | 77 | /** Total size of the described block, including imported variables. */ 78 | unsigned long int size; 79 | 80 | /** Optional block copy helper. May be NULL. */ 81 | void (*copy)(void *dst, void *src); 82 | 83 | /** Optional block dispose helper. May be NULL. */ 84 | void (*dispose)(void *); 85 | }; 86 | 87 | 88 | /* 89 | * Block instance. 90 | * 91 | * The block layout defines the per-block instance state, which includes 92 | * a reference to the shared block descriptor. 93 | * 94 | * The block's imported variables are allocated following the block 95 | * descriptor member. 96 | */ 97 | struct Block_layout { 98 | /** Pointer to the block's Objective-C class. */ 99 | void *isa; 100 | 101 | /** Block flags. */ 102 | int flags; 103 | 104 | /** Reserved value. */ 105 | int reserved; 106 | 107 | /** Block invocation function. */ 108 | void (*invoke)(void *, ...); 109 | 110 | /** Shared block descriptor. */ 111 | struct Block_descriptor *descriptor; 112 | 113 | // imported variables 114 | }; 115 | 116 | #ifdef __cplusplus 117 | } 118 | #endif -------------------------------------------------------------------------------- /PLPatchMaster/NSObject+PLPatchMaster.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Landon Fuller 3 | * 4 | * Copyright (c) 2013-2015 Plausible Labs Cooperative, Inc. 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | 29 | #import "NSObject+PLPatchMaster.h" 30 | #import "PLPatchMaster.h" 31 | 32 | /** 33 | * Runtime method patching support for NSObject. These are implemented via PLPatchMaster. 34 | */ 35 | @implementation NSObject (PLPatchMaster) 36 | 37 | /** 38 | * Patch the receiver's @a selector class method. The previously registered IMP may be fetched via PLPatchMaster::originalIMP:. 39 | * 40 | * @param selector The selector to patch. 41 | * @param replacementBlock The new implementation for @a selector. The first parameter must be a pointer to PLPatchIMP; the 42 | * remainder of the parameters must match the original method. 43 | * 44 | * @return Returns YES on success, or NO if @a selector is not a defined @a cls method. 45 | */ 46 | + (BOOL) pl_patchSelector: (SEL) selector withReplacementBlock: (id) replacementBlock { 47 | return [[PLPatchMaster master] patchClass: [self class] selector: selector replacementBlock: replacementBlock]; 48 | } 49 | 50 | /** 51 | * Patch the receiver's @a selector instance method. The previously registered IMP may be fetched via PLPatchMaster::originalIMP:. 52 | * 53 | * @param selector The selector to patch. 54 | * @param replacementBlock The new implementation for @a selector. The first parameter must be a pointer to PLPatchIMP; the 55 | * remainder of the parameters must match the original method. 56 | * 57 | * @return Returns YES on success, or NO if @a selector is not a defined @a cls instance method. 58 | */ 59 | + (BOOL) pl_patchInstanceSelector: (SEL) selector withReplacementBlock: (id) replacementBlock { 60 | return [[PLPatchMaster master] patchInstancesWithClass: [self class] selector: selector replacementBlock: replacementBlock]; 61 | } 62 | 63 | /** 64 | * Patch the receiver's @a selector class method, once (and if) @a selector is registered by a loaded Mach-O image. The previously 65 | * registered IMP may be fetched via PLPatchMaster::originalIMP:. 66 | * 67 | * @param selector The selector to patch. 68 | * @param replacementBlock The new implementation for @a selector. The first parameter must be a pointer to PLPatchIMP; the 69 | * remainder of the parameters must match the original method. 70 | * 71 | * @return Returns YES on success, or NO if @a selector is not a defined @a cls method. 72 | */ 73 | + (void) pl_patchFutureSelector: (SEL) selector withReplacementBlock: (id) replacementBlock { 74 | return [[PLPatchMaster master] patchFutureClassWithName: NSStringFromClass([self class]) selector: selector replacementBlock: replacementBlock]; 75 | 76 | } 77 | 78 | /** 79 | * Patch the receiver's @a selector instance method, once (and if) @a selector is registered by a loaded Mach-O image. 80 | * The previously registered IMP may be fetched via PLPatchMaster::originalIMP:. 81 | * 82 | * @param selector The selector to patch. 83 | * @param replacementBlock The new implementation for @a selector. The first parameter must be a pointer to PLPatchIMP; the 84 | * remainder of the parameters must match the original method. 85 | * 86 | * @return Returns YES on success, or NO if @a selector is not a defined @a cls instance method. 87 | */ 88 | + (void) pl_patchFutureInstanceSelector: (SEL) selector withReplacementBlock: (id) replacementBlock { 89 | return [[PLPatchMaster master] patchInstancesWithFutureClassName: NSStringFromClass([self class]) selector: selector replacementBlock: replacementBlock]; 90 | } 91 | 92 | @end 93 | 94 | -------------------------------------------------------------------------------- /PLPatchMaster/blockimp_x86_32.tramp: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ----------------------------------------------------------------------- 4 | # Copyright (c) 2010-2015, Plausible Labs Cooperative, Inc. 5 | # All Rights Reserved. 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining 8 | # a copy of this software and associated documentation files (the 9 | # ``Software''), to deal in the Software without restriction, including 10 | # without limitation the rights to use, copy, modify, merge, publish, 11 | # distribute, sublicense, and/or sell copies of the Software, and to 12 | # permit persons to whom the Software is furnished to do so, subject to 13 | # the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included 16 | # in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | # DEALINGS IN THE SOFTWARE. 26 | # ----------------------------------------------------------------------- 27 | 28 | # Supported architectures 29 | check_architecture () { 30 | case $1 in 31 | i386) 32 | return 1 33 | ;; 34 | *) 35 | return 0 36 | ;; 37 | esac 38 | } 39 | 40 | # Page size 41 | PAGE_SIZE="4096" 42 | 43 | # The name of this page 44 | PAGE_NAME=pl_blockimp_patch_table_page 45 | 46 | # Prefix to be placed at the start of the trampoline page 47 | trampoline_prefix () { 48 | asm << 'EOF' 49 | // Indirect symbols; we have to implement manual PC-relative symbol indirection, as our trampoline page's runtime PC 50 | // will not correspond to the location the linker will use to generate indirect references. 51 | .section __IMPORT,__pointers,non_lazy_symbol_pointers 52 | L_CFDataCreateMutable$non_lazy_ptr: 53 | .indirect_symbol _CFDataCreateMutable 54 | .long 0 55 | 56 | L_CFDataSetLength$non_lazy_ptr: 57 | .indirect_symbol _CFDataSetLength 58 | .long 0 59 | 60 | L_CFDataGetMutableBytePtr$non_lazy_ptr: 61 | .indirect_symbol _CFDataGetMutableBytePtr 62 | .long 0 63 | 64 | L_objc_msgSend$non_lazy_ptr: 65 | .indirect_symbol _objc_msgSend 66 | .long 0 67 | 68 | // Objective-C selector references 69 | .section __TEXT,__cstring,cstring_literals 70 | L_OBJC_METH_VAR_NAME_AUTORELEASE: 71 | .asciz "autorelease" 72 | 73 | .section __OBJC,__message_refs,literal_pointers,no_dead_strip 74 | .align 2 75 | L_OBJC_SELECTOR_REFERENCES_AUTORELEASE: 76 | .long L_OBJC_METH_VAR_NAME_AUTORELEASE 77 | 78 | .text 79 | _block_tramp_dispatch: 80 | // Compute config page location 81 | popl %edx 82 | andl $0xFFFFFFF0, %edx // truncate to the trampoline start (each is 16 bytes) 83 | subl $0x1000, %edx // load the config location 84 | 85 | // Fetch the original trampoline table address for use in PC-relative addressing, saving it in %ecx. 86 | movl 0x4(%edx), %ecx // load the pl_trampoline address from our config 87 | movl 0x4(%ecx), %ecx // load the pl_trampoline_table address from the pl_trampoline 88 | movl (%ecx), %ecx // load the pl_trampoline_table_config address from the pl_trampoline_table 89 | movl 12(%ecx), %ecx // load the template code page address from the pl_trampoline_table 90 | 91 | // Allocate space for our PLPatchIMP structure 92 | // TODO: This is less than ideal -- since we must maintain the original stack layout when calling the 93 | // target ptr, we can't stack allocate a PLPatchIMP structure. 94 | // For now, we use a rather heavy heap allocation (+ autorelease) approach. 95 | 96 | pushl %ebp 97 | movl %esp, %ebp 98 | subl $16, %esp 99 | movl %edi, 8(%esp) // Save caller's registers 100 | movl %esi, 12(%esp) 101 | 102 | movl %edx, %edi // Move the config page address to a callee-saved register 103 | movl %ecx, %esi // Move the trampoline page address to a callee-saved register 104 | 105 | movl $16, 4(%esp) // arg2 - capacity 106 | movl $0, (%esp) // arg1 - allocator 107 | movl L_CFDataCreateMutable$non_lazy_ptr - _block_tramp_dispatch(%esi), %eax 108 | call *%eax 109 | 110 | movl %eax, (%esp) // arg1 - CFDataRef 111 | movl 12(%esp), %ecx 112 | 113 | movl L_CFDataSetLength$non_lazy_ptr - _block_tramp_dispatch(%esi), %eax 114 | call *%eax 115 | 116 | // Add the CFDataRef to the autorelease pool 117 | movl L_OBJC_SELECTOR_REFERENCES_AUTORELEASE - _block_tramp_dispatch(%esi), %eax 118 | movl %eax, 4(%esp) // arg2 - @selector(autorelease) 119 | 120 | movl L_objc_msgSend$non_lazy_ptr - _block_tramp_dispatch(%esi), %eax 121 | call *%eax 122 | 123 | // Fetch the pointer to our PLPatchIMP allocation into %eax 124 | movl L_CFDataGetMutableBytePtr$non_lazy_ptr - _block_tramp_dispatch(%esi), %eax 125 | call *%eax 126 | 127 | // Save the config page address in a non-callee-preserved register before it's overwritten by restoring 128 | // the caller's state 129 | movl %edi, %edx 130 | 131 | // Restore the caller's registers and stack layout 132 | movl 8(%esp), %edi 133 | movl 12(%esp), %esi 134 | addl $0x10, %esp 135 | popl %ebp 136 | 137 | // At this point, we now have space for our PLPatchIMP structure and can proceed as normal 138 | // Load 'self' from the original argument list and insert it in the first struct position 139 | movl 0x4(%esp), %ecx 140 | movl %ecx, (%eax) 141 | 142 | // Load the original IMP from the config page, and move to the second struct position 143 | movl 0x8(%edx), %ecx 144 | movl %ecx, 0x4(%eax) 145 | 146 | // Load the original SEL from the config page, and move to the third struct position 147 | movl 0xC(%edx), %ecx 148 | movl %ecx, 0x8(%eax) 149 | 150 | // Move our struct to the second parameter, overwriting IMP 151 | movl %eax, 0x8(%esp) 152 | 153 | // Load the block reference from the config page, and move to the first parameter, overwriting self 154 | movl (%edx), %ecx 155 | movl %ecx, 0x4(%esp) 156 | 157 | // Jump to the block fptr 158 | jmp *0xc(%ecx) 159 | 160 | .align 4 // align the trampolines at 16 bytes (required to fit the config pages) 161 | EOF 162 | } 163 | 164 | # Generate a single trampoline 165 | trampoline () { 166 | asm << 'EOF' 167 | // Call into the dispatcher, placing our return address on the stack. 168 | call _block_tramp_dispatch # 5 bytes 169 | .align 4 // align the trampolines at 16 bytes (required to fit the config pages) 170 | EOF 171 | } -------------------------------------------------------------------------------- /PLPatchMaster/blockimp_x86_32_stret.tramp: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ----------------------------------------------------------------------- 4 | # Copyright (c) 2010-2015, Plausible Labs Cooperative, Inc. 5 | # All Rights Reserved. 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining 8 | # a copy of this software and associated documentation files (the 9 | # ``Software''), to deal in the Software without restriction, including 10 | # without limitation the rights to use, copy, modify, merge, publish, 11 | # distribute, sublicense, and/or sell copies of the Software, and to 12 | # permit persons to whom the Software is furnished to do so, subject to 13 | # the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included 16 | # in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | # DEALINGS IN THE SOFTWARE. 26 | # ----------------------------------------------------------------------- 27 | 28 | # Supported architectures 29 | check_architecture () { 30 | case $1 in 31 | i386) 32 | return 1 33 | ;; 34 | *) 35 | return 0 36 | ;; 37 | esac 38 | } 39 | 40 | # Page size 41 | PAGE_SIZE="4096" 42 | 43 | # The name of this page 44 | PAGE_NAME=pl_blockimp_patch_table_stret_page 45 | 46 | # Prefix to be placed at the start of the trampoline page 47 | trampoline_prefix () { 48 | asm << 'EOF' 49 | // Indirect symbols; we have to implement manual PC-relative symbol indirection, as our trampoline page's runtime PC 50 | // will not correspond to the location the linker will use to generate indirect references. 51 | .section __IMPORT,__pointers,non_lazy_symbol_pointers 52 | L_CFDataCreateMutable$non_lazy_ptr: 53 | .indirect_symbol _CFDataCreateMutable 54 | .long 0 55 | 56 | L_CFDataSetLength$non_lazy_ptr: 57 | .indirect_symbol _CFDataSetLength 58 | .long 0 59 | 60 | L_CFDataGetMutableBytePtr$non_lazy_ptr: 61 | .indirect_symbol _CFDataGetMutableBytePtr 62 | .long 0 63 | 64 | L_objc_msgSend$non_lazy_ptr: 65 | .indirect_symbol _objc_msgSend 66 | .long 0 67 | 68 | // Objective-C selector references 69 | .section __TEXT,__cstring,cstring_literals 70 | L_OBJC_METH_VAR_NAME_AUTORELEASE: 71 | .asciz "autorelease" 72 | 73 | .section __OBJC,__message_refs,literal_pointers,no_dead_strip 74 | .align 2 75 | L_OBJC_SELECTOR_REFERENCES_AUTORELEASE: 76 | .long L_OBJC_METH_VAR_NAME_AUTORELEASE 77 | 78 | .text 79 | _block_tramp_dispatch: 80 | // Compute config page location 81 | popl %edx 82 | andl $0xFFFFFFF0, %edx // truncate to the trampoline start (each is 16 bytes) 83 | subl $0x1000, %edx // load the config location 84 | 85 | // Fetch the original trampoline table address for use in PC-relative addressing, saving it in %ecx. 86 | movl 0x4(%edx), %ecx // load the pl_trampoline address from our config 87 | movl 0x4(%ecx), %ecx // load the pl_trampoline_table address from the pl_trampoline 88 | movl (%ecx), %ecx // load the pl_trampoline_table_config address from the pl_trampoline_table 89 | movl 12(%ecx), %ecx // load the template code page address from the pl_trampoline_table 90 | 91 | // Allocate space for our PLPatchIMP structure 92 | // TODO: This is less than ideal -- since we must maintain the original stack layout when calling the 93 | // target ptr, we can't stack allocate a PLPatchIMP structure. 94 | // For now, we use a rather heavy heap allocation (+ autorelease) approach. 95 | 96 | pushl %ebp 97 | movl %esp, %ebp 98 | subl $16, %esp 99 | movl %edi, 8(%esp) // Save caller's registers 100 | movl %esi, 12(%esp) 101 | 102 | movl %edx, %edi // Move the config page address to a callee-saved register 103 | movl %ecx, %esi // Move the trampoline page address to a callee-saved register 104 | 105 | movl $16, 4(%esp) // arg2 - capacity 106 | movl $0, (%esp) // arg1 - allocator 107 | movl L_CFDataCreateMutable$non_lazy_ptr - _block_tramp_dispatch(%esi), %eax 108 | call *%eax 109 | 110 | movl %eax, (%esp) // arg1 - CFDataRef 111 | movl 12(%esp), %ecx 112 | 113 | movl L_CFDataSetLength$non_lazy_ptr - _block_tramp_dispatch(%esi), %eax 114 | call *%eax 115 | 116 | // Add the CFDataRef to the autorelease pool 117 | movl L_OBJC_SELECTOR_REFERENCES_AUTORELEASE - _block_tramp_dispatch(%esi), %eax 118 | movl %eax, 4(%esp) // arg2 - @selector(autorelease) 119 | 120 | movl L_objc_msgSend$non_lazy_ptr - _block_tramp_dispatch(%esi), %eax 121 | call *%eax 122 | 123 | // Fetch the pointer to our PLPatchIMP allocation into %eax 124 | movl L_CFDataGetMutableBytePtr$non_lazy_ptr - _block_tramp_dispatch(%esi), %eax 125 | call *%eax 126 | 127 | // Save the config page address in a non-callee-preserved register before it's overwritten by restoring 128 | // the caller's state 129 | movl %edi, %edx 130 | 131 | // Restore the caller's registers and stack layout 132 | movl 8(%esp), %edi 133 | movl 12(%esp), %esi 134 | addl $0x10, %esp 135 | popl %ebp 136 | 137 | // At this point, we now have space for our PLPatchIMP structure and can proceed as normal 138 | // Load 'self' from the original argument list and insert it in the first struct position 139 | movl 0x8(%esp), %ecx 140 | movl %ecx, (%eax) 141 | 142 | // Load the original IMP from the config page, and move to the second struct position 143 | movl 0x8(%edx), %ecx 144 | movl %ecx, 0x4(%eax) 145 | 146 | // Load the original SEL from the config page, and move to the third struct position 147 | movl 0xC(%edx), %ecx 148 | movl %ecx, 0x8(%eax) 149 | 150 | // Move our struct to the third parameter, overwriting IMP 151 | movl %eax, 0xC(%esp) 152 | 153 | // Load the block reference from the config page, and move to the second parameter, overwriting self 154 | movl (%edx), %ecx 155 | movl %ecx, 0x8(%esp) 156 | 157 | // Jump to the block fptr 158 | jmp *0xc(%ecx) 159 | 160 | .align 4 // align the trampolines at 16 bytes (required to fit the config pages) 161 | EOF 162 | } 163 | 164 | # Generate a single trampoline 165 | trampoline () { 166 | asm << 'EOF' 167 | // Call into the dispatcher, placing our return address on the stack. 168 | call _block_tramp_dispatch # 5 bytes 169 | .align 4 // align the trampolines at 16 bytes (required to fit the config pages) 170 | EOF 171 | } -------------------------------------------------------------------------------- /PLPatchMaster/PLPatchMaster.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Landon Fuller 3 | * 4 | * Copyright (c) 2013-2015 Plausible Labs Cooperative, Inc. 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | 29 | #import "PLPatchMaster.h" 30 | #import "PLPatchMasterImpl.hpp" 31 | 32 | /** Foundation.framework's library install name. */ 33 | NSString *kPLPatchImageFoundation = @"/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation"; 34 | 35 | /** CoreFoundation.framework's library install name. */ 36 | NSString *kPLPatchImageCoreFoundation = @"/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"; 37 | 38 | /** libSystem.dylib's library install name. */ 39 | NSString *kPLPatchImageLibSystem = @"/usr/lib/libSystem.B.dylib"; 40 | 41 | /** 42 | * Manages application (and removal) of runtime patches. This class is thread-safe, and may be accessed from any thread. 43 | */ 44 | @implementation PLPatchMaster 45 | 46 | /** 47 | * Return the default patch master. 48 | */ 49 | + (instancetype) master { 50 | static PLPatchMaster *m = nil; 51 | static dispatch_once_t onceToken; 52 | dispatch_once(&onceToken, ^{ 53 | m = [[PLPatchMaster alloc] init];; 54 | }); 55 | 56 | return m; 57 | } 58 | 59 | - (instancetype) init { 60 | if ((self = [super init]) == nil) 61 | return nil; 62 | 63 | /* Default state */ 64 | _impl = [[PLPatchMasterImpl alloc] init]; 65 | 66 | return self; 67 | } 68 | 69 | - (void) dealloc { 70 | [[NSNotificationCenter defaultCenter] removeObserver: self]; 71 | [_impl release]; 72 | [super dealloc]; 73 | } 74 | 75 | 76 | /** 77 | * Patch the class method @a selector of @a className, where @a className may not yet have been loaded, 78 | * or @a selector may not yet have been registered by a category. 79 | * 80 | * This may be used to register patches that will be automatically applied when the bundle or framework 81 | * to which they apply is loaded. 82 | * 83 | * @param className The name of the class to patch. The class may not yet have been loaded. 84 | * @param selector The selector to patch. 85 | * @param replacementBlock The new implementation for @a selector. The first parameter must be a pointer to PLPatchIMP; the 86 | * remainder of the parameters must match the original method. 87 | */ 88 | - (void) patchFutureClassWithName: (NSString *) className selector: (SEL) selector replacementBlock: (id) replacementBlock { 89 | [_impl patchFutureClassWithName: className selector: selector replacementBlock: replacementBlock]; 90 | } 91 | 92 | /** 93 | * Patch the instance method @a selector of @a className, where @a className may not yet have been loaded, 94 | * or @a selector may not yet have been registered by a category. 95 | * 96 | * This may be used to register patches that will be automatically applied when the bundle or framework 97 | * to which they apply is loaded. 98 | * 99 | * @param className The name of the class to patch. The class may not yet have been loaded. 100 | * @param selector The selector to patch. 101 | * @param replacementBlock The new implementation for @a selector. The first parameter must be a pointer to PLPatchIMP; the 102 | * remainder of the parameters must match the original method. 103 | */ 104 | - (void) patchInstancesWithFutureClassName: (NSString *) className selector: (SEL) selector replacementBlock: (id) replacementBlock { 105 | [_impl patchInstancesWithFutureClassName: className selector: selector replacementBlock: replacementBlock]; 106 | } 107 | 108 | /** 109 | * Patch the class method @a selector of @a cls. 110 | * 111 | * @param cls The class to patch. 112 | * @param selector The selector to patch. 113 | * @param replacementBlock The new implementation for @a selector. The first parameter must be a pointer to PLPatchIMP; the 114 | * remainder of the parameters must match the original method. 115 | * 116 | * @return Returns YES on success, or NO if @a selector is not a defined @a cls method. 117 | */ 118 | - (BOOL) patchClass: (Class) cls selector: (SEL) selector replacementBlock: (id) replacementBlock { 119 | return [_impl patchClass: cls selector: selector replacementBlock: replacementBlock]; 120 | } 121 | 122 | /** 123 | * Patch the instance method @a selector of @a cls. 124 | * 125 | * @param cls The class to patch. 126 | * @param selector The selector to patch. 127 | * @param replacementBlock The new implementation for @a selector. The first parameter must be a pointer to PLPatchIMP; the 128 | * remainder of the parameters must match the original method. 129 | * 130 | * @return Returns YES on success, or NO if @a selector is not a defined @a cls instance method. 131 | */ 132 | - (BOOL) patchInstancesWithClass: (Class) cls selector: (SEL) selector replacementBlock: (id) replacementBlock { 133 | return [_impl patchInstancesWithClass: cls selector: selector replacementBlock: replacementBlock]; 134 | } 135 | 136 | /** 137 | * Perform dyld-compatible symbol rebinding of all references to @a symbol defined by @a library across all current 138 | * and future loaded images. 139 | * 140 | * @param symbol The name of the symbol to patch. 141 | * @param library The absolute or relative path (e.g. 'Foundation') to the library responsible for exporting the original symbol. 142 | * @param replacementAddress The new address to which 143 | */ 144 | - (void) rebindSymbol: (NSString *) symbol fromImage: (NSString *) library replacementAddress: (uintptr_t) replacementAddress { 145 | [_impl rebindSymbol: symbol fromImage: library replacementAddress: replacementAddress]; 146 | } 147 | 148 | /** 149 | * Perform dyld-compatible symbol rebinding of all references to @a symbol defined by *any* library across all current 150 | * and future loaded images. 151 | * 152 | * This is essentially equivalent to single-level namespace symbol binding. 153 | * 154 | * @param symbol The name of the symbol to patch. 155 | * @param replacementAddress The new address to which 156 | */ 157 | - (void) rebindSymbol: (NSString *) symbol replacementAddress: (uintptr_t) replacementAddress { 158 | [_impl rebindSymbol: symbol replacementAddress: replacementAddress]; 159 | } 160 | 161 | @end 162 | -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/iPhone Runtime/PLBlockIMP.framework/Versions/A/Resources/gentramp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ----------------------------------------------------------------------- 4 | # gentramp.sh - Copyright (c) 2010-2011, Plausible Labs Cooperative, Inc. 5 | # 6 | # Trampoline Page Generator 7 | # Author: Landon Fuller 8 | # 9 | # Permission is hereby granted, free of charge, to any person obtaining 10 | # a copy of this software and associated documentation files (the 11 | # ``Software''), to deal in the Software without restriction, including 12 | # without limitation the rights to use, copy, modify, merge, publish, 13 | # distribute, sublicense, and/or sell copies of the Software, and to 14 | # permit persons to whom the Software is furnished to do so, subject to 15 | # the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included 18 | # in all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, 21 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | # DEALINGS IN THE SOFTWARE. 28 | # ----------------------------------------------------------------------- 29 | 30 | PROGNAME="$0" 31 | 32 | INPUT_FILE_PATH="$1" 33 | CURRENT_ARCH="$2" 34 | PLATFORM_NAME="$3" 35 | OUTPUT_FILE_PREFIX="$4" 36 | OUTPUT_DIR="$5" 37 | 38 | if [ -z "${INPUT_FILE_PATH}" ] || [ -z "${CURRENT_ARCH}" ] || [ -z "${PLATFORM_NAME}" ] || [ -z "${OUTPUT_FILE_PREFIX}" ] || [ -z "${OUTPUT_DIR}" ]; then 39 | echo "USAGE: $PROGNAME " 40 | echo " For example, '$PROGNAME blockimp_arm.tramp armv7 iphoneos bli_arm build' would read 'blockimp_arm.tramp'" 41 | echo " and generate the following files in 'build':" 42 | echo " - bli_arm_config.c" 43 | echo " - bli_arm.s" 44 | echo " - bli_arm.h" 45 | 46 | exit 1 47 | fi 48 | 49 | SRC_C_OUTPUT="${OUTPUT_DIR}/${OUTPUT_FILE_PREFIX}_config.c" 50 | SRC_OUTPUT="${OUTPUT_DIR}/${OUTPUT_FILE_PREFIX}.s" 51 | HEADER_OUTPUT="${OUTPUT_DIR}/${OUTPUT_FILE_PREFIX}.h" 52 | 53 | # Default implementation 54 | trampoline_prefix () { 55 | return 0 56 | } 57 | 58 | # Import the trampoline definition 59 | . "${INPUT_FILE_PATH}" 60 | 61 | check_required () { 62 | local name=$1 63 | eval "local var=\${$1}" 64 | 65 | if [ -z "${var}" ]; then 66 | echo "Required variable ${name} not defined." 67 | exit 1 68 | fi 69 | } 70 | 71 | check_required PAGE_SIZE 72 | check_required PAGE_NAME 73 | 74 | # Write a header line 75 | header () { 76 | echo "$1" >> "${HEADER_OUTPUT}" 77 | } 78 | 79 | # Write a C source line 80 | src () { 81 | echo "$1" >> "${SRC_C_OUTPUT}" 82 | } 83 | 84 | # Flush the assembler output buffer to disk 85 | ASM_BUFFER="" 86 | asm_flush () { 87 | echo "${ASM_BUFFER}" >> "${SRC_OUTPUT}" 88 | asm_discard 89 | } 90 | 91 | # Write the assembler buffer to disk, but don't discard the contents 92 | asm_write () { 93 | echo "${ASM_BUFFER}" >> "${SRC_OUTPUT}" 94 | } 95 | 96 | # Discard the current assembler output buffer 97 | asm_discard () { 98 | ASM_BUFFER='' 99 | return 0; 100 | } 101 | 102 | # Append data to the assembler output buffer 103 | asm () { 104 | local line="" 105 | while read -r line; do 106 | ASM_BUFFER+=$line 107 | ASM_BUFFER+="\n" 108 | done 109 | } 110 | 111 | # Compute the assembled size of the current assembler buffer 112 | compute_asm_size () { 113 | # Create the temporary assembler file 114 | local output=".globl _byte_count_start\n" 115 | output+="_byte_count_start:\n" 116 | output+="${ASM_BUFFER}" 117 | output+=".globl _byte_count_end\n" 118 | output+="_byte_count_end:\n" 119 | 120 | # Apple's ARM64 as(1) handling is a thin shim to clang's arm64 integrated 121 | # assembler, which does not support documented as(1) behavior. We work 122 | # around clang's file extension assumptions via an explicit -x option; 123 | # see rdar://15162294 for more details. 124 | if [ "${CURRENT_ARCH}" == "arm64" ]; then 125 | local asm_args_extra="-x assembler" 126 | else 127 | local asm_args_extra="" 128 | fi 129 | 130 | local tempfile=`mktemp /tmp/as_bytecount.XXXXXXXX` 131 | echo "${output}" | xcrun --sdk "${PLATFORM_NAME}" as -arch "${CURRENT_ARCH}" $asm_args_extra -o "${tempfile}" - 132 | if [ $? != 0 ]; then 133 | echo "Assembling the trampoline failed" 134 | exit 1 135 | fi 136 | 137 | local byte_size=`xcrun --sdk "${PLATFORM_NAME}" nm -t d -P "${tempfile}" | grep ^_byte_count_end | awk '{print $3}'` 138 | rm -f "${tempfile}" 139 | 140 | echo $byte_size 141 | } 142 | 143 | 144 | # Write out the page header 145 | write_page_decl () { 146 | # Calculate the required alignment 147 | local align=`perl -l -e "print log(${PAGE_SIZE})/log(2)"` 148 | asm << EOF 149 | # GENERATED CODE - DO NOT EDIT" 150 | # This file was generated by $PROGNAME on `date` 151 | 152 | # Write out the trampoline table, aligned to the page boundary 153 | .text 154 | .align ${align} 155 | .globl _${PAGE_NAME} 156 | _${PAGE_NAME}: 157 | EOF 158 | } 159 | 160 | main () { 161 | echo '' > "${SRC_OUTPUT}" 162 | echo '' > "${SRC_C_OUTPUT}" 163 | echo '' > "${HEADER_OUTPUT}" 164 | 165 | # Write out the trampoline header file 166 | header "extern void *${PAGE_NAME};" 167 | header "extern struct pl_trampoline_table_config ${PAGE_NAME}_config;" 168 | 169 | # Don't generate tables for an unsupported arch 170 | check_architecture "${CURRENT_ARCH}" 171 | if [ "$?" != "1" ]; then 172 | return 173 | fi 174 | 175 | # Determine the trampoline prefix size 176 | trampoline_prefix 177 | local prefix_size=$(compute_asm_size) 178 | asm_discard 179 | 180 | # Compute the size of the remaining code page. 181 | local page_avail=`expr $PAGE_SIZE - $prefix_size` 182 | 183 | # Determine the trampoline size 184 | trampoline 185 | local tramp_size=$(compute_asm_size) 186 | asm_discard 187 | if [ "${tramp_size}" = 0 ]; then 188 | echo "Error occured calculating trampoline size; received size of 0" 189 | exit 1 190 | fi 191 | 192 | # Compute the number of of available trampolines. 193 | local trampoline_count=`expr $page_avail / $tramp_size` 194 | echo "Prefix size: ${prefix_size}" 195 | echo "Trampoline size: ${tramp_size}" 196 | echo "Trampolines per page: ${trampoline_count}" 197 | 198 | # Write out the page declaration 199 | write_page_decl 200 | asm_flush 201 | 202 | # Write out the prefix 203 | trampoline_prefix 204 | asm_flush 205 | 206 | # Write out the trampolines 207 | trampoline 208 | local i=0 209 | while [ $i -lt ${trampoline_count} ]; do 210 | asm_write 211 | local i=`expr $i + 1` 212 | done 213 | asm_discard 214 | 215 | # Write out the table configuration 216 | local config_src=`cat << EOF 217 | #define PL_BLOCKIMP_PRIVATE 1 218 | #include 219 | 220 | extern void *${PAGE_NAME}; 221 | pl_trampoline_table_config ${PAGE_NAME}_config = { 222 | .trampoline_size = ${tramp_size}, 223 | .page_offset = ${prefix_size}, 224 | .trampoline_count = ${trampoline_count}, 225 | .template_page = &${PAGE_NAME} 226 | }; 227 | EOF` 228 | src "${config_src}" 229 | } 230 | 231 | main -------------------------------------------------------------------------------- /Dependencies/PLBlockIMP/Mac OS X Runtime/PLBlockIMP.framework/Versions/A/Resources/gentramp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ----------------------------------------------------------------------- 4 | # gentramp.sh - Copyright (c) 2010-2011, Plausible Labs Cooperative, Inc. 5 | # 6 | # Trampoline Page Generator 7 | # Author: Landon Fuller 8 | # 9 | # Permission is hereby granted, free of charge, to any person obtaining 10 | # a copy of this software and associated documentation files (the 11 | # ``Software''), to deal in the Software without restriction, including 12 | # without limitation the rights to use, copy, modify, merge, publish, 13 | # distribute, sublicense, and/or sell copies of the Software, and to 14 | # permit persons to whom the Software is furnished to do so, subject to 15 | # the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included 18 | # in all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, 21 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | # DEALINGS IN THE SOFTWARE. 28 | # ----------------------------------------------------------------------- 29 | 30 | PROGNAME="$0" 31 | 32 | INPUT_FILE_PATH="$1" 33 | CURRENT_ARCH="$2" 34 | PLATFORM_NAME="$3" 35 | OUTPUT_FILE_PREFIX="$4" 36 | OUTPUT_DIR="$5" 37 | 38 | if [ -z "${INPUT_FILE_PATH}" ] || [ -z "${CURRENT_ARCH}" ] || [ -z "${PLATFORM_NAME}" ] || [ -z "${OUTPUT_FILE_PREFIX}" ] || [ -z "${OUTPUT_DIR}" ]; then 39 | echo "USAGE: $PROGNAME " 40 | echo " For example, '$PROGNAME blockimp_arm.tramp armv7 iphoneos bli_arm build' would read 'blockimp_arm.tramp'" 41 | echo " and generate the following files in 'build':" 42 | echo " - bli_arm_config.c" 43 | echo " - bli_arm.s" 44 | echo " - bli_arm.h" 45 | 46 | exit 1 47 | fi 48 | 49 | SRC_C_OUTPUT="${OUTPUT_DIR}/${OUTPUT_FILE_PREFIX}_config.c" 50 | SRC_OUTPUT="${OUTPUT_DIR}/${OUTPUT_FILE_PREFIX}.s" 51 | HEADER_OUTPUT="${OUTPUT_DIR}/${OUTPUT_FILE_PREFIX}.h" 52 | 53 | # Default implementation 54 | trampoline_prefix () { 55 | return 0 56 | } 57 | 58 | # Import the trampoline definition 59 | . "${INPUT_FILE_PATH}" 60 | 61 | check_required () { 62 | local name=$1 63 | eval "local var=\${$1}" 64 | 65 | if [ -z "${var}" ]; then 66 | echo "Required variable ${name} not defined." 67 | exit 1 68 | fi 69 | } 70 | 71 | check_required PAGE_SIZE 72 | check_required PAGE_NAME 73 | 74 | # Write a header line 75 | header () { 76 | echo "$1" >> "${HEADER_OUTPUT}" 77 | } 78 | 79 | # Write a C source line 80 | src () { 81 | echo "$1" >> "${SRC_C_OUTPUT}" 82 | } 83 | 84 | # Flush the assembler output buffer to disk 85 | ASM_BUFFER="" 86 | asm_flush () { 87 | echo "${ASM_BUFFER}" >> "${SRC_OUTPUT}" 88 | asm_discard 89 | } 90 | 91 | # Write the assembler buffer to disk, but don't discard the contents 92 | asm_write () { 93 | echo "${ASM_BUFFER}" >> "${SRC_OUTPUT}" 94 | } 95 | 96 | # Discard the current assembler output buffer 97 | asm_discard () { 98 | ASM_BUFFER='' 99 | return 0; 100 | } 101 | 102 | # Append data to the assembler output buffer 103 | asm () { 104 | local line="" 105 | while read -r line; do 106 | ASM_BUFFER+=$line 107 | ASM_BUFFER+="\n" 108 | done 109 | } 110 | 111 | # Compute the assembled size of the current assembler buffer 112 | compute_asm_size () { 113 | # Create the temporary assembler file 114 | local output=".globl _byte_count_start\n" 115 | output+="_byte_count_start:\n" 116 | output+="${ASM_BUFFER}" 117 | output+=".globl _byte_count_end\n" 118 | output+="_byte_count_end:\n" 119 | 120 | # Apple's ARM64 as(1) handling is a thin shim to clang's arm64 integrated 121 | # assembler, which does not support documented as(1) behavior. We work 122 | # around clang's file extension assumptions via an explicit -x option; 123 | # see rdar://15162294 for more details. 124 | if [ "${CURRENT_ARCH}" == "arm64" ]; then 125 | local asm_args_extra="-x assembler" 126 | else 127 | local asm_args_extra="" 128 | fi 129 | 130 | local tempfile=`mktemp /tmp/as_bytecount.XXXXXXXX` 131 | echo "${output}" | xcrun --sdk "${PLATFORM_NAME}" as -arch "${CURRENT_ARCH}" $asm_args_extra -o "${tempfile}" - 132 | if [ $? != 0 ]; then 133 | echo "Assembling the trampoline failed" 134 | exit 1 135 | fi 136 | 137 | local byte_size=`xcrun --sdk "${PLATFORM_NAME}" nm -t d -P "${tempfile}" | grep ^_byte_count_end | awk '{print $3}'` 138 | rm -f "${tempfile}" 139 | 140 | echo $byte_size 141 | } 142 | 143 | 144 | # Write out the page header 145 | write_page_decl () { 146 | # Calculate the required alignment 147 | local align=`perl -l -e "print log(${PAGE_SIZE})/log(2)"` 148 | asm << EOF 149 | # GENERATED CODE - DO NOT EDIT" 150 | # This file was generated by $PROGNAME on `date` 151 | 152 | # Write out the trampoline table, aligned to the page boundary 153 | .text 154 | .align ${align} 155 | .globl _${PAGE_NAME} 156 | _${PAGE_NAME}: 157 | EOF 158 | } 159 | 160 | main () { 161 | echo '' > "${SRC_OUTPUT}" 162 | echo '' > "${SRC_C_OUTPUT}" 163 | echo '' > "${HEADER_OUTPUT}" 164 | 165 | # Write out the trampoline header file 166 | header "extern void *${PAGE_NAME};" 167 | header "extern struct pl_trampoline_table_config ${PAGE_NAME}_config;" 168 | 169 | # Don't generate tables for an unsupported arch 170 | check_architecture "${CURRENT_ARCH}" 171 | if [ "$?" != "1" ]; then 172 | return 173 | fi 174 | 175 | # Determine the trampoline prefix size 176 | trampoline_prefix 177 | local prefix_size=$(compute_asm_size) 178 | asm_discard 179 | 180 | # Compute the size of the remaining code page. 181 | local page_avail=`expr $PAGE_SIZE - $prefix_size` 182 | 183 | # Determine the trampoline size 184 | trampoline 185 | local tramp_size=$(compute_asm_size) 186 | asm_discard 187 | if [ "${tramp_size}" = 0 ]; then 188 | echo "Error occured calculating trampoline size; received size of 0" 189 | exit 1 190 | fi 191 | 192 | # Compute the number of of available trampolines. 193 | local trampoline_count=`expr $page_avail / $tramp_size` 194 | echo "Prefix size: ${prefix_size}" 195 | echo "Trampoline size: ${tramp_size}" 196 | echo "Trampolines per page: ${trampoline_count}" 197 | 198 | # Write out the page declaration 199 | write_page_decl 200 | asm_flush 201 | 202 | # Write out the prefix 203 | trampoline_prefix 204 | asm_flush 205 | 206 | # Write out the trampolines 207 | trampoline 208 | local i=0 209 | while [ $i -lt ${trampoline_count} ]; do 210 | asm_write 211 | local i=`expr $i + 1` 212 | done 213 | asm_discard 214 | 215 | # Write out the table configuration 216 | local config_src=`cat << EOF 217 | #define PL_BLOCKIMP_PRIVATE 1 218 | #include 219 | 220 | extern void *${PAGE_NAME}; 221 | pl_trampoline_table_config ${PAGE_NAME}_config = { 222 | .trampoline_size = ${tramp_size}, 223 | .page_offset = ${prefix_size}, 224 | .trampoline_count = ${trampoline_count}, 225 | .template_page = &${PAGE_NAME} 226 | }; 227 | EOF` 228 | src "${config_src}" 229 | } 230 | 231 | main -------------------------------------------------------------------------------- /PLPatchMaster/SymbolBinder.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Landon Fuller 3 | * 4 | * Copyright (c) 2015 Landon Fuller . 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | 29 | #pragma once 30 | 31 | #include "PMLog.h" 32 | 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | #include 44 | #include 45 | 46 | #include "SymbolName.hpp" 47 | 48 | namespace patchmaster { 49 | 50 | /* Architecture-specific Mach-O types and constants */ 51 | #ifdef __LP64__ 52 | typedef struct mach_header_64 pl_mach_header_t; 53 | typedef struct segment_command_64 pl_segment_command_t; 54 | typedef struct section_64 pl_section_t; 55 | typedef struct nlist_64 pl_nlist_t; 56 | static constexpr uint32_t PL_LC_SEGMENT = LC_SEGMENT_64; 57 | #else 58 | typedef struct mach_header pl_mach_header_t; 59 | typedef struct segment_command pl_segment_command_t; 60 | typedef struct section pl_section_t; 61 | typedef struct nlist pl_nlist_t; 62 | static constexpr uint32_t PL_LC_SEGMENT = LC_SEGMENT; 63 | #endif 64 | 65 | 66 | uint64_t read_uleb128 (const void *location, std::size_t *size); 67 | int64_t read_sleb128 (const void *location, std::size_t *size); 68 | 69 | /* Forward declaration */ 70 | class LocalImage; 71 | 72 | /** 73 | * A simple byte-based opcode stream reader. 74 | * 75 | * This was adapted from our DWARF opcode evaluation code in PLCrashReporter. 76 | */ 77 | class bind_opstream { 78 | public: 79 | class symbol_proc; 80 | 81 | private: 82 | /** Current position within the op stream */ 83 | const uint8_t *_p; 84 | 85 | /** Starting address. */ 86 | const uint8_t *_instr; 87 | 88 | /** Ending address. */ 89 | const uint8_t *_instr_max; 90 | 91 | /** Current immediate value */ 92 | uint8_t _immd = 0; 93 | 94 | /** 95 | * If true, this is a lazy opcode section; BIND_OPCODE_DONE is automatically skipped at the end of 96 | * each entry (the lazy section is written to terminate evaluation after each entry, as each symbol within 97 | * the lazy section is by dyld on-demand, and is supposed to terminate after resolving one symbol). 98 | */ 99 | bool _isLazy; 100 | 101 | /** 102 | * Opcode evaluation state. 103 | */ 104 | struct evaluation_state { 105 | /* dylib path from which the symbol will be resolved, or an empty string if unspecified or flat binding. */ 106 | const char *sym_image = ""; 107 | 108 | /* bind type (one of BIND_TYPE_POINTER, BIND_TYPE_TEXT_ABSOLUTE32, or BIND_TYPE_TEXT_PCREL32) */ 109 | uint8_t bind_type = BIND_TYPE_POINTER; 110 | 111 | /* symbol name */ 112 | const char *sym_name = ""; 113 | 114 | /* symbol flags (one of BIND_SYMBOL_FLAGS_WEAK_IMPORT, BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) */ 115 | uint8_t sym_flags = 0; 116 | 117 | /* A value to be added to the resolved symbol's address before binding. */ 118 | int64_t addend = 0; 119 | 120 | /* The actual in-memory bind target address. */ 121 | uintptr_t bind_address = 0; 122 | 123 | /** 124 | * Return symbol_proc representation of the current evaluation state. 125 | */ 126 | symbol_proc symbol_proc () { 127 | return symbol_proc::symbol_proc( 128 | SymbolName(sym_image, sym_name), 129 | bind_type, 130 | sym_flags, 131 | addend, 132 | bind_address 133 | ); 134 | } 135 | }; 136 | 137 | /** The current evaluation state. */ 138 | evaluation_state _eval_state; 139 | 140 | public: 141 | bind_opstream (const uint8_t *opcodes, const size_t opcodes_len, bool isLazy) : _p(opcodes), _instr(_p), _instr_max(_p + opcodes_len), _isLazy(isLazy) {} 142 | 143 | bind_opstream (const bind_opstream &other) : _p(other._p), _instr(other._instr), _instr_max(other._instr_max), _isLazy(other._isLazy), _eval_state(other._eval_state) {} 144 | 145 | /** 146 | * The parsed bind procedure for a single symbol. 147 | */ 148 | class symbol_proc { 149 | public: 150 | /** 151 | * Construct a new symbol procedure record. 152 | * 153 | * @param name The two-level symbol name bound by this procedure. 154 | * @param type The bind type for this symbol. 155 | * @param flags The bind flags for this symbol. 156 | * @param addend A value to be added to the resolved symbol's address before binding. 157 | * @param bind_address The actual in-memory bind target address. 158 | */ 159 | symbol_proc (const SymbolName &name, uint8_t type, uint8_t flags, int64_t addend, uintptr_t bind_address) : 160 | _name(name), _type(type), _flags(flags), _addend(addend), _bind_address(bind_address) {} 161 | 162 | symbol_proc (SymbolName &&name, uint8_t type, uint8_t flags, int64_t addend, uintptr_t bind_address) : 163 | _name(std::move(name)), _type(type), _flags(flags), _addend(addend), _bind_address(bind_address) {} 164 | 165 | /** The two-level symbol name bound by this procedure. */ 166 | const SymbolName &name () const { return _name; } 167 | 168 | /* The bind type for this symbol (one of BIND_TYPE_POINTER, BIND_TYPE_TEXT_ABSOLUTE32, or BIND_TYPE_TEXT_PCREL32) */ 169 | uint8_t type () const { return _type; } 170 | 171 | /* The bind flags for this symbol (one of BIND_SYMBOL_FLAGS_WEAK_IMPORT, BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) */ 172 | uint8_t flags () const { return _flags; } 173 | 174 | /* A value to be added to the resolved symbol's address before binding. */ 175 | int64_t addend () const { return _addend; } 176 | 177 | /* The actual in-memory bind target address. */ 178 | uintptr_t bind_address () const { return _bind_address; } 179 | 180 | private: 181 | /** The two-level symbol name bound by this procedure. */ 182 | SymbolName _name; 183 | 184 | /* The bind type for this symbol (one of BIND_TYPE_POINTER, BIND_TYPE_TEXT_ABSOLUTE32, or BIND_TYPE_TEXT_PCREL32) */ 185 | uint8_t _type; 186 | 187 | /* The bind flags for this symbol (one of BIND_SYMBOL_FLAGS_WEAK_IMPORT, BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) */ 188 | uint8_t _flags = 0; 189 | 190 | /* A value to be added to the resolved symbol's address before binding. */ 191 | int64_t _addend = 0; 192 | 193 | /* The actual in-memory bind target address. */ 194 | uintptr_t _bind_address = 0; 195 | }; 196 | 197 | void evaluate (const LocalImage &image, const std::function &bind); 198 | uint8_t step (const LocalImage &image, const std::function &bind); 199 | 200 | /** Read a ULEB128 value and advance the stream */ 201 | inline uint64_t uleb128 () { 202 | size_t len; 203 | uint64_t result = read_uleb128(_p, &len); 204 | 205 | _p += len; 206 | assert(_p <= _instr_max); 207 | return result; 208 | } 209 | 210 | /** Read a SLEB128 value and advance the stream */ 211 | inline int64_t sleb128 () { 212 | size_t len; 213 | int64_t result = read_sleb128(_p, &len); 214 | 215 | _p += len; 216 | assert(_p <= _instr_max); 217 | return result; 218 | } 219 | 220 | /** Skip @a offset bytes. */ 221 | inline void skip (size_t offset) { 222 | _p += offset; 223 | assert(_p <= _instr_max); 224 | } 225 | 226 | /** Read a single opcode from the stream. */ 227 | inline uint8_t opcode () { 228 | assert(_p < _instr_max); 229 | uint8_t value = (*_p) & BIND_OPCODE_MASK; 230 | _immd = (*_p) & BIND_IMMEDIATE_MASK; 231 | _p++; 232 | 233 | /* Skip BIND_OPCODE_DONE if it occurs within a lazy binding opcode stream */ 234 | if (_isLazy && *_p == BIND_OPCODE_DONE && !isEmpty()) 235 | skip(1); 236 | 237 | return value; 238 | }; 239 | 240 | /** Return the current stream position. */ 241 | inline const uint8_t *position () { return _p; }; 242 | 243 | /** Return true if there are no additional opcodes to be read. */ 244 | inline bool isEmpty () { return _p >= _instr_max; } 245 | 246 | /** Return true if this is a lazy opcode stream. */ 247 | inline bool isLazy () { return _isLazy; } 248 | 249 | /** Read a NUL-terminated C string from the stream, advancing the current position past the string. */ 250 | inline const char *cstring () { 251 | const char *result = (const char *) _p; 252 | skip(strlen(result) + 1); 253 | return result; 254 | } 255 | 256 | /** Return the immediate value from the last opcode */ 257 | inline uint8_t immd () { return _immd; } 258 | 259 | /** Return the signed representation of immd */ 260 | inline int8_t signed_immd () { 261 | /* All other constants are negative */ 262 | if (immd() == 0) 263 | return 0; 264 | 265 | /* Sign-extend the immediate value */ 266 | return (~BIND_IMMEDIATE_MASK) | (immd() & BIND_IMMEDIATE_MASK); 267 | } 268 | 269 | }; 270 | 271 | /** 272 | * An in-memory Mach-O image. 273 | */ 274 | class LocalImage { 275 | private: 276 | friend class bind_opstream; 277 | 278 | /** 279 | * Construct a new local image. 280 | */ 281 | LocalImage ( 282 | const std::string &path, 283 | const pl_mach_header_t *header, 284 | const intptr_t vmaddr_slide, 285 | std::shared_ptr> &libraries, 286 | std::shared_ptr> &segments, 287 | std::shared_ptr> &bindings 288 | ) : _header(header), _vmaddr_slide(vmaddr_slide), _libraries(libraries), _segments(segments), _bindOpcodes(bindings), _path(path) {} 289 | 290 | public: 291 | static const std::string &MainExecutablePath (); 292 | static LocalImage Analyze (const std::string &path, const pl_mach_header_t *header); 293 | void rebind_symbols (const std::function &bind); 294 | 295 | /** 296 | * Return a borrowed reference to the image's path. 297 | */ 298 | const std::string &path () const { return _path; } 299 | 300 | /** 301 | * Return the image's vm_slide. 302 | */ 303 | intptr_t vmaddr_slide () const { return _vmaddr_slide; } 304 | 305 | /** 306 | * Return the image's symbol binding opcode streams. 307 | */ 308 | std::shared_ptr> bindOpcodes () const { return _bindOpcodes; } 309 | 310 | /** 311 | * Return the image's defined segments. 312 | */ 313 | std::shared_ptr> segments () const { return _segments; } 314 | 315 | private: 316 | /** Mach-O image header */ 317 | const pl_mach_header_t *_header; 318 | 319 | /** Offset applied when the image was loaded; required to compute in-memory addresses from on-disk VM addresses.. */ 320 | const intptr_t _vmaddr_slide; 321 | 322 | /** Linked libraries, indexed by reference order. */ 323 | std::shared_ptr> _libraries; 324 | 325 | /** Segment commands, indexed by declaration order. */ 326 | std::shared_ptr> _segments; 327 | 328 | /** All symbol binding opcodes. */ 329 | std::shared_ptr> _bindOpcodes; 330 | 331 | /** Image path */ 332 | const std::string _path; 333 | }; 334 | 335 | } /* namespace patchmaster */ -------------------------------------------------------------------------------- /PLPatchMaster/SymbolBinder.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Landon Fuller 3 | * 4 | * Copyright (c) 2015 Landon Fuller . 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | 29 | #include "SymbolBinder.hpp" 30 | #include 31 | 32 | namespace patchmaster { 33 | 34 | /** 35 | * Read a ULEB128 value from @a address. 36 | * 37 | * @param location The location from which the value should be read. 38 | * @param size On return, will be set to the total size of the decoded LEB128 value in bytes. 39 | * 40 | * This implementation was extracted from the PLCrashReporter DWARF code. 41 | */ 42 | uint64_t read_uleb128 (const void *location, std::size_t *size) { 43 | unsigned int shift = 0; 44 | size_t position = 0; 45 | 46 | uint64_t result = 0; 47 | for (const uint8_t *p = (const uint8_t *) location ;; p++) { 48 | /* LEB128 uses 7 bits for the number, the final bit to signal completion */ 49 | uint8_t byte = *p; 50 | result |= ((uint64_t) (byte & 0x7f)) << shift; 51 | shift += 7; 52 | 53 | /* This is used to track length, so we must set it before 54 | * potentially terminating the loop below */ 55 | position++; 56 | 57 | /* Check for terminating bit */ 58 | if ((byte & 0x80) == 0) 59 | break; 60 | 61 | /* Check for a ULEB128 larger than 64-bits */ 62 | if (shift >= 64) { 63 | PMFatal("Invalid DYLD info: ULEB128 is larger than the maximum supported size of 64 bits!"); 64 | } 65 | } 66 | 67 | *size = position; 68 | return result; 69 | } 70 | 71 | /** 72 | * Read a SLEB128 value from @a location within @a mobj. 73 | * 74 | * @param location The location from which the value should be read. 75 | * @param size On return, will be set to the total size of the decoded LEB128 value in bytes. 76 | * 77 | * This implementation was extracted from the PLCrashReporter DWARF code. 78 | */ 79 | int64_t read_sleb128 (const void *location, std::size_t *size) { 80 | unsigned int shift = 0; 81 | size_t position = 0; 82 | int64_t result = 0; 83 | 84 | const uint8_t *p; 85 | for (p = (const uint8_t *) location ;; p++) { 86 | /* LEB128 uses 7 bits for the number, the final bit to signal completion */ 87 | uint8_t byte = *p; 88 | result |= ((uint64_t) (byte & 0x7f)) << shift; 89 | shift += 7; 90 | 91 | /* This is used to track length, so we must set it before 92 | * potentially terminating the loop below */ 93 | position++; 94 | 95 | /* Check for terminating bit */ 96 | if ((byte & 0x80) == 0) 97 | break; 98 | 99 | /* Check for a SLEB128 larger than 64-bits */ 100 | if (shift >= 64) { 101 | PMFatal("Invalid DYLD info: SLEB128 is larger than the maximum supported size of 64 bits!"); 102 | } 103 | } 104 | 105 | /* Sign bit is 2nd high order bit */ 106 | if (shift < 64 && (*p & 0x40)) 107 | result |= -(1ULL << shift); 108 | 109 | *size = position; 110 | return result; 111 | } 112 | 113 | /** 114 | * Step the opcode stream, evaluating and returning the next opcode. 115 | * 116 | * Upon evaluating a complete symbol binding procedure, it will be dispatched to the provided bind function. 117 | * 118 | * @param image The local image to be used as the procedure's execution environment. 119 | * @param bind Function to call upon successfully evaluating a full bind procedure for a symbol. 120 | */ 121 | uint8_t bind_opstream::step (const LocalImage &image, const std::function &bind) { 122 | /* Given an index into our reference libraries, update the `sym_image` state */ 123 | auto set_current_image = [&](uint64_t image_idx) { 124 | if (image_idx > image._libraries->size()) { 125 | PMFatal("dyld bind opcode in '%s' references invalid image index %" PRIu64, image._path.c_str(), image_idx); 126 | return; 127 | } 128 | 129 | /* `0` is a special index referencing the current image */ 130 | if (image_idx == 0) { 131 | _eval_state.sym_image = image._path.c_str(); 132 | } else { 133 | if (image_idx > SIZE_T_MAX) { 134 | PMFatal("dyld bind opcode in '%s' references image index %" PRIu64 " > SIZE_T_MAX" PRIu64, image._path.c_str(), image_idx); 135 | return; 136 | } 137 | _eval_state.sym_image = image._libraries->at(static_cast(image_idx) - 1).c_str(); 138 | } 139 | }; 140 | 141 | uint8_t op = opcode(); 142 | switch (op) { 143 | case BIND_OPCODE_DONE: 144 | break; 145 | 146 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: { 147 | set_current_image(immd()); 148 | break; 149 | } 150 | 151 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: { 152 | set_current_image(uleb128()); 153 | break; 154 | } 155 | 156 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: 157 | switch (signed_immd()) { 158 | /* Enable flat resolution */ 159 | case BIND_SPECIAL_DYLIB_FLAT_LOOKUP: 160 | _eval_state.sym_image = ""; 161 | break; 162 | 163 | /* Fetch the path of the main executable */ 164 | case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE: 165 | _eval_state.sym_image = LocalImage::MainExecutablePath().c_str(); 166 | break; 167 | 168 | /* Use our own path */ 169 | case BIND_SPECIAL_DYLIB_SELF: 170 | _eval_state.sym_image = image._path.c_str(); 171 | break; 172 | } 173 | 174 | break; 175 | 176 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 177 | /* Flags are supplied as an immediate value. */ 178 | _eval_state.sym_flags = immd(); 179 | 180 | /* Symbol name is defined inline. */ 181 | _eval_state.sym_name = cstring(); 182 | break; 183 | 184 | case BIND_OPCODE_SET_TYPE_IMM: 185 | _eval_state.bind_type = immd(); 186 | break; 187 | 188 | case BIND_OPCODE_SET_ADDEND_SLEB: 189 | _eval_state.addend = sleb128(); 190 | break; 191 | 192 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: { 193 | uint8_t segment_idx = immd(); 194 | if (segment_idx >= image._segments->size()) 195 | PMFatal("dyld BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB in '%s' references invalid segment index %" PRIu8, image._path.c_str(), segment_idx); 196 | 197 | /* Compute the in-memory address from the segment reference */ 198 | const pl_segment_command_t *segment = image._segments->at(segment_idx); 199 | _eval_state.bind_address = segment->vmaddr + image._vmaddr_slide; 200 | _eval_state.bind_address += uleb128(); 201 | break; 202 | } 203 | 204 | case BIND_OPCODE_ADD_ADDR_ULEB: 205 | _eval_state.bind_address += uleb128(); 206 | break; 207 | 208 | case BIND_OPCODE_DO_BIND: 209 | /* Perform the bind */ 210 | bind(_eval_state.symbol_proc()); 211 | 212 | /* This implicitly advances the current bind address by the pointer width */ 213 | _eval_state.bind_address += sizeof(uintptr_t); 214 | break; 215 | 216 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: 217 | /* Perform the bind */ 218 | bind(_eval_state.symbol_proc()); 219 | 220 | /* Advance the bind address */ 221 | _eval_state.bind_address += uleb128() + sizeof(uintptr_t); 222 | break; 223 | 224 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: 225 | /* Perform the bind */ 226 | bind(_eval_state.symbol_proc()); 227 | 228 | /* Immediate offset scaled by the native pointer width */ 229 | _eval_state.bind_address += immd() * sizeof(uintptr_t) + sizeof(uintptr_t); 230 | break; 231 | 232 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: { 233 | /* Fetch the number of addresses at which this symbol should be bound */ 234 | uint64_t count = uleb128(); 235 | 236 | /* Fetch the number of bytes to skip between each binding */ 237 | uint64_t skip = uleb128(); 238 | 239 | for (uint64_t i = 0; i < count; i++) { 240 | /* Perform the bind */ 241 | bind(_eval_state.symbol_proc()); 242 | 243 | /* Advance by the requested skip */ 244 | _eval_state.bind_address += skip + sizeof(uintptr_t); 245 | } 246 | break; 247 | } 248 | 249 | default: 250 | PMFatal("Unhandled opcode: %hhx", op); 251 | break; 252 | } 253 | 254 | return op; 255 | } 256 | 257 | /** 258 | * Evaluate the opcode stream, passing all resolved bindings to @a bind. 259 | * 260 | * @param image The local image to be used as the procedure's execution environment. 261 | * @param bind The function to be called with resolved symbol bindings. 262 | */ 263 | void bind_opstream::evaluate (const LocalImage &image, const std::function &bind) { 264 | while (!isEmpty() && step(image, bind) != BIND_OPCODE_DONE); 265 | } 266 | 267 | /** 268 | * Return the linker-provided path to the main executable. 269 | */ 270 | const std::string &LocalImage::MainExecutablePath () { 271 | static std::string path; 272 | 273 | /* Fetch the path only once */ 274 | std::once_flag once; 275 | std::call_once(once, []{ 276 | char *buffer = nullptr; 277 | uint32_t buffer_len = 0; 278 | while (_NSGetExecutablePath(buffer, &buffer_len) == -1) { 279 | free(buffer); 280 | buffer = (char *) malloc(buffer_len); 281 | } 282 | path = buffer; 283 | free(buffer); 284 | }); 285 | 286 | return path; 287 | } 288 | 289 | /** 290 | * Analyze an in-memory Mach-O image. 291 | * 292 | * @param path The image path. 293 | * @param header The image header. 294 | */ 295 | LocalImage LocalImage::Analyze (const std::string &path, const pl_mach_header_t *header) { 296 | using namespace std; 297 | 298 | /* Image slide */ 299 | intptr_t vm_slide = 0; 300 | 301 | /* Collect the segment and library lists, saving the __LINKEDIT info and vm_slide */ 302 | auto segments = std::make_shared>(); 303 | auto libraries = std::make_shared>(); 304 | pl_segment_command_t *linkedit = nullptr; 305 | 306 | const uint8_t *cmd_ptr = (const uint8_t *) header + sizeof(*header); 307 | for (uint32_t cmd_idx = 0; cmd_idx < header->ncmds; cmd_idx++) { 308 | auto cmd = (const struct load_command *) cmd_ptr; 309 | cmd_ptr += cmd->cmdsize; 310 | 311 | switch (cmd->cmd) { 312 | case PL_LC_SEGMENT: { 313 | auto segment = (pl_segment_command_t *) cmd; 314 | 315 | /* Use the actual load address of the __TEXT segment to calculate the dyld slide */ 316 | if (strcmp(segment->segname, SEG_TEXT) == 0) { 317 | uintptr_t load_addr = (uintptr_t) header; 318 | if (segment->vmaddr < load_addr) { 319 | vm_slide = load_addr - segment->vmaddr; 320 | } else if (segment->vmaddr > load_addr) { 321 | vm_slide = -((intptr_t) (segment->vmaddr - load_addr)); 322 | } else { 323 | vm_slide = 0; 324 | } 325 | } else if (strcmp(segment->segname, SEG_LINKEDIT) == 0) { 326 | linkedit = segment; 327 | } 328 | 329 | /* For the purposes of indexing segments, dyld ignores zero-length segments */ 330 | if (segment->vmsize > 0) 331 | segments->push_back(segment); 332 | break; 333 | } 334 | 335 | case LC_LOAD_DYLIB: 336 | case LC_LOAD_WEAK_DYLIB: 337 | case LC_LOAD_UPWARD_DYLIB: 338 | case LC_REEXPORT_DYLIB: 339 | { 340 | auto dylib_cmd = (struct dylib_command *) cmd; 341 | 342 | /* Fetch the library path */ 343 | const char *name = (const char *) (((const char *) cmd) + dylib_cmd->dylib.name.offset); 344 | libraries->push_back(name); 345 | } 346 | } 347 | } 348 | 349 | /* Save references to all dyld bind opcode streams */ 350 | auto bindOpcodes = std::make_shared>(); 351 | 352 | cmd_ptr = (const uint8_t *) header + sizeof(*header); 353 | for (uint32_t cmd_idx = 0; cmd_idx < header->ncmds; cmd_idx++) { 354 | auto cmd = (const struct load_command *) cmd_ptr; 355 | cmd_ptr += cmd->cmdsize; 356 | 357 | switch (cmd->cmd) { 358 | case LC_DYLD_INFO: 359 | case LC_DYLD_INFO_ONLY: if (linkedit != nullptr) { 360 | auto info = (const dyld_info_command *) cmd; 361 | uintptr_t linkedit_base = (linkedit->vmaddr + vm_slide) - linkedit->fileoff; 362 | 363 | if (info->bind_size != 0) 364 | bindOpcodes->push_back(bind_opstream((const uint8_t *) (linkedit_base + info->bind_off), (size_t) info->bind_size, false)); 365 | 366 | if (info->weak_bind_size != 0) 367 | bindOpcodes->push_back(bind_opstream((const uint8_t *) (linkedit_base + info->weak_bind_off), (size_t) info->weak_bind_size, false)); 368 | 369 | if (info->lazy_bind_size != 0) 370 | bindOpcodes->push_back(bind_opstream((const uint8_t *) (linkedit_base + info->lazy_bind_off), (size_t) info->lazy_bind_size, true)); 371 | } 372 | 373 | default: 374 | break; 375 | } 376 | } 377 | 378 | return LocalImage(path, header, vm_slide, libraries, segments, bindOpcodes); 379 | } 380 | 381 | /** 382 | * Evaluate all available dyld bind opcodes, passing all resolved bindings to @a binder. 383 | * 384 | * @param binder The function to be called with resolved symbol bindings. 385 | * 386 | * @return Returns true on success, or false if the opcode stream references invalid segment or image addresses. 387 | */ 388 | void LocalImage::rebind_symbols (const std::function &bind) { 389 | for (auto &&opcodes : *_bindOpcodes) { 390 | auto ops = opcodes; 391 | ops.evaluate(*this, [&bind](const bind_opstream::symbol_proc &sp) { 392 | // TODO - Can we handle the other types? 393 | if (sp.type() != BIND_TYPE_POINTER) 394 | return; 395 | 396 | /* Hand off to our caller */ 397 | bind(sp); 398 | }); 399 | } 400 | } 401 | 402 | } -------------------------------------------------------------------------------- /PLPatchMaster/PLPatchMasterImpl.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Landon Fuller 3 | * 4 | * Copyright (c) 2013-2015 Plausible Labs Cooperative, Inc. 5 | * All rights reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | 29 | #import "PLPatchMasterImpl.hpp" 30 | #import "PLPatchMaster.h" 31 | 32 | extern "C" { 33 | #define PL_BLOCKIMP_PRIVATE 1 // Required for the PLBlockIMP trampoline API 34 | #import 35 | } 36 | 37 | #import "PLBlockLayout.h" 38 | #import "PMLog.h" 39 | 40 | #import 41 | 42 | #import 43 | 44 | /* Include the generated PLBlockIMP headers */ 45 | extern "C" { 46 | #ifdef __i386__ 47 | #include "blockimp_x86_32.h" 48 | #include "blockimp_x86_32_stret.h" 49 | #elif defined(__x86_64__) 50 | #include "blockimp_x86_64.h" 51 | #include "blockimp_x86_64_stret.h" 52 | #elif defined(__arm64__) 53 | #include "blockimp_arm64.h" 54 | #elif defined(__arm__) 55 | #include "blockimp_arm.h" 56 | #include "blockimp_arm_stret.h" 57 | #else 58 | #error Unsupported Architecture 59 | #endif 60 | } 61 | 62 | /* The ARM64 ABI does not require (or support) the _stret objc_msgSend variant */ 63 | #ifdef __arm64__ 64 | #define STRET_TABLE_REQUIRED 0 65 | #define STRET_TABLE_CONFIG pl_blockimp_patch_table_page_config 66 | #define STRET_TABLE blockimp_table 67 | #else 68 | #define STRET_TABLE_REQUIRED 1 69 | #define STRET_TABLE_CONFIG pl_blockimp_patch_table_stret_page_config 70 | #define STRET_TABLE blockimp_table_stret 71 | #endif 72 | 73 | /** Notification sent (synchronously) when an image is added. */ 74 | static NSString *PLPatchMasterImageDidLoadNotification = @"PLPatchMasterImageDidLoadNotification"; 75 | 76 | /** Notification user-info key containing the mach header pointer for a newly added image */ 77 | static NSString *PLPatchMasterMachHeaderKey = @"PLPatchMasterMachHeaderKey"; 78 | 79 | static void perform_dyld_rebinding (const PatchTable &patches, const char *image_name, const struct mach_header *mh); 80 | 81 | /* Global lock for our mutable trampoline state. Must be held when accessing the trampoline tables. */ 82 | static pthread_mutex_t blockimp_lock = PTHREAD_MUTEX_INITIALIZER; 83 | 84 | /* Trampoline tables for objc_msgSend() dispatch. */ 85 | static pl_trampoline_table *blockimp_table = NULL; 86 | 87 | #if STRET_TABLE_REQUIRED 88 | /* Trampoline tables for objc_msgSend_stret() dispatch. */ 89 | static pl_trampoline_table *blockimp_table_stret = NULL; 90 | #endif /* STRET_TABLE_REQUIRED */ 91 | 92 | /** 93 | * Create a new PLPatchIMP block IMP trampoline. 94 | */ 95 | static IMP patch_imp_implementationWithBlock (id block, SEL selector, IMP origIMP) { 96 | /* Allocate the appropriate trampoline type. */ 97 | pl_trampoline *tramp; 98 | struct Block_layout *bl = (__bridge struct Block_layout *) block; 99 | if (bl->flags & BLOCK_USE_STRET) { 100 | tramp = pl_trampoline_alloc(&STRET_TABLE_CONFIG, &blockimp_lock, &STRET_TABLE); 101 | } else { 102 | tramp = pl_trampoline_alloc(&pl_blockimp_patch_table_page_config, &blockimp_lock, &blockimp_table); 103 | } 104 | 105 | /* Configure the trampoline */ 106 | void **config = (void **) pl_trampoline_data_ptr((void *) tramp->trampoline); 107 | config[0] = Block_copy((__bridge void *)block); 108 | config[1] = tramp; 109 | config[2] = (void *) origIMP; 110 | config[3] = selector; 111 | 112 | /* Return the function pointer. */ 113 | return (IMP) tramp->trampoline; 114 | } 115 | 116 | #if UNUSED 117 | 118 | /** 119 | * Return the backing block for an IMP trampoline. 120 | */ 121 | static void *patch_imp_getBlock (IMP anImp) { 122 | /* Fetch the config data and return the block reference. */ 123 | void **config = pl_trampoline_data_ptr(anImp); 124 | return config[0]; 125 | } 126 | 127 | #endif 128 | 129 | /** 130 | * Deallocate the IMP trampoline. 131 | */ 132 | static BOOL patch_imp_removeBlock (IMP anImp) { 133 | /* Fetch the config data */ 134 | void **config = (void **) pl_trampoline_data_ptr((void *) anImp); 135 | auto bl = (struct Block_layout *) config[0]; 136 | auto tramp = (pl_trampoline *) config[1]; 137 | 138 | /* Drop the trampoline allocation */ 139 | if (bl->flags & BLOCK_USE_STRET) { 140 | pl_trampoline_free(&blockimp_lock, &STRET_TABLE, tramp); 141 | } else { 142 | pl_trampoline_free(&blockimp_lock, &blockimp_table, tramp); 143 | } 144 | 145 | /* Release the block */ 146 | Block_release(config[0]); 147 | 148 | // TODO - what does this return value mean? 149 | return YES; 150 | } 151 | 152 | 153 | /** 154 | * @internal 155 | * Concrete internal implementation of PLPatchMaster. This is implemented seperately to allow us to hide 156 | * the PLPatchMaster instance variables when targeting Objective-C runtimes (i.e. Mac i386). 157 | */ 158 | @implementation PLPatchMasterImpl 159 | 160 | /* Handle dyld image load notifications. These *should* be dispatched after the Objective-C callbacks have been 161 | * dispatched, but there's no gaurantee. It's possible, though unlikely, that this could break in a future release of Mac OS X. */ 162 | static void dyld_image_add_cb (const struct mach_header *mh, intptr_t vmaddr_slide) { 163 | auto *userInfo = [NSMutableDictionary dictionary]; 164 | [userInfo setObject: [NSValue valueWithPointer: mh] forKey: PLPatchMasterMachHeaderKey]; 165 | 166 | [[NSNotificationCenter defaultCenter] postNotificationName: PLPatchMasterImageDidLoadNotification object: nil userInfo: userInfo]; 167 | } 168 | 169 | + (void) initialize { 170 | if (([self class] != [PLPatchMasterImpl class])) 171 | return; 172 | 173 | /* Register the shared dyld image add function */ 174 | _dyld_register_func_for_add_image(dyld_image_add_cb); 175 | } 176 | 177 | - (instancetype) init { 178 | if ((self = [super init]) == nil) 179 | return nil; 180 | 181 | /* Default state */ 182 | _classPatches = [[NSMutableDictionary dictionary] retain]; 183 | _instancePatches = [[NSMutableDictionary dictionary] retain]; 184 | _restoreBlocks = [[NSMutableArray array] retain]; 185 | _pendingPatches = [[NSMutableArray array] retain]; 186 | _lock = OS_SPINLOCK_INIT; 187 | 188 | /* Watch for image loads */ 189 | [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handleImageLoad:) name: PLPatchMasterImageDidLoadNotification object: nil]; 190 | 191 | return self; 192 | } 193 | 194 | - (void) dealloc { 195 | [[NSNotificationCenter defaultCenter] removeObserver: self]; 196 | [_classPatches release]; 197 | [_instancePatches release]; 198 | [_restoreBlocks release]; 199 | [_pendingPatches release]; 200 | 201 | [super dealloc]; 202 | } 203 | 204 | // PLPatchMasterImageDidLoadNotification notification handler 205 | - (void) handleImageLoad: (NSNotification *) notification { 206 | /* Apply all dyld symbol rebindings */ 207 | { 208 | auto mh = (const struct mach_header *) [[[notification userInfo] objectForKey: PLPatchMasterMachHeaderKey] pointerValue]; 209 | const char *name = nullptr; 210 | for (uint32_t i = 0; i < _dyld_image_count(); i++) { 211 | if (_dyld_get_image_header(i) != mh) 212 | continue; 213 | name = _dyld_get_image_name(i); 214 | } 215 | 216 | /* Apply any symbol rebindings immediately */ 217 | if (name == nullptr) { 218 | PMLog("Failed to lookup Mach-O image name; skipping patching"); 219 | } else { 220 | OSSpinLockLock(&_lock); 221 | perform_dyld_rebinding(_symbolPatches, name, mh); 222 | OSSpinLockUnlock(&_lock); 223 | } 224 | } 225 | 226 | /* Apply all Objective-C patches */ 227 | NSArray *blocks; 228 | OSSpinLockLock(&_lock); { 229 | blocks = [[_pendingPatches copy] autorelease]; 230 | } OSSpinLockUnlock(&_lock); 231 | 232 | for (BOOL (^patcher)(void) in blocks) { 233 | if (patcher()) { 234 | OSSpinLockLock(&_lock); { 235 | [_pendingPatches removeObject: patcher]; 236 | } OSSpinLockUnlock(&_lock); 237 | } 238 | } 239 | } 240 | 241 | /** 242 | * Patch the class method @a selector of @a className, where @a className may not yet have been loaded, 243 | * or @a selector may not yet have been registered by a category. 244 | * 245 | * This may be used to register patches that will be automatically applied when the bundle or framework 246 | * to which they apply is loaded. 247 | * 248 | * @param className The name of the class to patch. The class may not yet have been loaded. 249 | * @param selector The selector to patch. 250 | * @param replacementBlock The new implementation for @a selector. The first parameter must be a pointer to PLPatchIMP; the 251 | * remainder of the parameters must match the original method. 252 | */ 253 | - (void) patchFutureClassWithName: (NSString *) className selector: (SEL) selector replacementBlock: (id) replacementBlock { 254 | /* Create a patch block */ 255 | BOOL (^patcher)(void) = ^{ 256 | Class cls = NSClassFromString(className); 257 | if (!cls) 258 | return NO; 259 | 260 | if (![cls respondsToSelector: selector]) 261 | return NO; 262 | 263 | /* Class and selector are registered! Patch away! */ 264 | return [self patchClass: cls selector: selector replacementBlock: replacementBlock]; 265 | }; 266 | 267 | /* Register the patch */ 268 | OSSpinLockLock(&_lock); { 269 | [_pendingPatches addObject: patcher]; 270 | } OSSpinLockUnlock(&_lock); 271 | 272 | /* Try immediately -- the patch may already have been viable, or the required image may have been concurrently loaded */ 273 | if (patcher()) { 274 | OSSpinLockLock(&_lock); { 275 | [_pendingPatches removeObject: patcher]; 276 | } OSSpinLockUnlock(&_lock); 277 | } 278 | } 279 | 280 | /** 281 | * Patch the instance method @a selector of @a className, where @a className may not yet have been loaded, 282 | * or @a selector may not yet have been registered by a category. 283 | * 284 | * This may be used to register patches that will be automatically applied when the bundle or framework 285 | * to which they apply is loaded. 286 | * 287 | * @param className The name of the class to patch. The class may not yet have been loaded. 288 | * @param selector The selector to patch. 289 | * @param replacementBlock The new implementation for @a selector. The first parameter must be a pointer to PLPatchIMP; the 290 | * remainder of the parameters must match the original method. 291 | */ 292 | - (void) patchInstancesWithFutureClassName: (NSString *) className selector: (SEL) selector replacementBlock: (id) replacementBlock { 293 | /* Create a patch block */ 294 | BOOL (^patcher)(void) = ^{ 295 | Class cls = NSClassFromString(className); 296 | if (!cls) 297 | return NO; 298 | 299 | if (![cls instancesRespondToSelector: selector]) 300 | return NO; 301 | 302 | /* Class and selector are registered! Patch away! */ 303 | return [self patchInstancesWithClass: cls selector: selector replacementBlock: replacementBlock]; 304 | }; 305 | 306 | /* Register the patch */ 307 | OSSpinLockLock(&_lock); { 308 | [_pendingPatches addObject: patcher]; 309 | } OSSpinLockUnlock(&_lock); 310 | 311 | /* Try immediately -- the patch may already have been viable, or the required image may have been concurrently loaded */ 312 | if (patcher()) { 313 | OSSpinLockLock(&_lock); { 314 | [_pendingPatches removeObject: patcher]; 315 | } OSSpinLockUnlock(&_lock); 316 | } 317 | } 318 | 319 | /** 320 | * Patch the class method @a selector of @a cls. 321 | * 322 | * @param cls The class to patch. 323 | * @param selector The selector to patch. 324 | * @param replacementBlock The new implementation for @a selector. The first parameter must be a pointer to PLPatchIMP; the 325 | * remainder of the parameters must match the original method. 326 | * 327 | * @return Returns YES on success, or NO if @a selector is not a defined @a cls method. 328 | */ 329 | - (BOOL) patchClass: (Class) cls selector: (SEL) selector replacementBlock: (id) replacementBlock { 330 | Method m = class_getClassMethod(cls, selector); 331 | if (m == NULL) 332 | return NO; 333 | 334 | /* Insert the new implementation */ 335 | IMP oldIMP = method_getImplementation(m); 336 | IMP newIMP = patch_imp_implementationWithBlock(replacementBlock, selector, oldIMP); 337 | 338 | if (!class_addMethod(object_getClass(cls), selector, newIMP, method_getTypeEncoding(m))) { 339 | /* Method already exists in subclass, we just need to swap the IMP */ 340 | method_setImplementation(m, newIMP); 341 | } 342 | 343 | OSSpinLockLock(&_lock); { 344 | /* If the method has already been patched once, we won't need to restore the IMP */ 345 | BOOL restoreIMP = YES; 346 | if (_classPatches[cls][NSStringFromSelector(selector)] != nil) 347 | restoreIMP = NO; 348 | 349 | /* Otherwise, record the patch and save a restore block */ 350 | if (_classPatches[cls] == nil) 351 | _classPatches[(id)cls] = [NSMutableSet setWithObject: NSStringFromSelector(selector)]; 352 | else 353 | [_classPatches[(id)cls] addObject: NSStringFromSelector(selector)]; 354 | 355 | [_restoreBlocks addObject: [[^{ 356 | if (restoreIMP) { 357 | Method m = class_getClassMethod(cls, selector); 358 | method_setImplementation(m, oldIMP); 359 | } 360 | patch_imp_removeBlock(newIMP); 361 | } copy] autorelease]]; 362 | } OSSpinLockUnlock(&_lock); 363 | 364 | return YES; 365 | } 366 | 367 | /** 368 | * Patch the instance method @a selector of @a cls. 369 | * 370 | * @param cls The class to patch. 371 | * @param selector The selector to patch. 372 | * @param replacementBlock The new implementation for @a selector. The first parameter must be a pointer to PLPatchIMP; the 373 | * remainder of the parameters must match the original method. 374 | * 375 | * @return Returns YES on success, or NO if @a selector is not a defined @a cls instance method. 376 | */ 377 | - (BOOL) patchInstancesWithClass: (Class) cls selector: (SEL) selector replacementBlock: (id) replacementBlock { 378 | @autoreleasepool { 379 | Method m = class_getInstanceMethod(cls, selector); 380 | if (m == NULL) 381 | return NO; 382 | 383 | /* Insert the new implementation */ 384 | IMP oldIMP = method_getImplementation(m); 385 | IMP newIMP = patch_imp_implementationWithBlock(replacementBlock, selector, oldIMP); 386 | 387 | if (!class_addMethod(cls, selector, newIMP, method_getTypeEncoding(m))) { 388 | /* Method already exists in subclass, we just need to swap the IMP */ 389 | method_setImplementation(m, newIMP); 390 | } 391 | 392 | OSSpinLockLock(&_lock); { 393 | /* If the method has already been patched once, we won't need to restore the IMP */ 394 | BOOL restoreIMP = YES; 395 | NSMutableSet *knownSels = _instancePatches[cls]; 396 | if ([knownSels containsObject: NSStringFromSelector(selector)]) 397 | restoreIMP = NO; 398 | 399 | /* Otherwise, record the patch and save a restore block */ 400 | if (_instancePatches[cls] == nil) 401 | _instancePatches[(id)cls] = [NSMutableSet setWithObject: NSStringFromSelector(selector)]; 402 | else 403 | [_instancePatches[(id)cls] addObject: NSStringFromSelector(selector)]; 404 | 405 | [_restoreBlocks addObject: [[^{ 406 | if (restoreIMP) { 407 | Method m = class_getInstanceMethod(cls, selector); 408 | method_setImplementation(m, oldIMP); 409 | } 410 | patch_imp_removeBlock(newIMP); 411 | } copy] autorelease]]; 412 | } OSSpinLockUnlock(&_lock); 413 | } 414 | 415 | return YES; 416 | } 417 | 418 | /** 419 | * @internal 420 | * 421 | * Perform dyld-compatible symbol rebinding of the given image. 422 | * 423 | * @param patches The table of patches; if locking is required, locks must be held by the caller. 424 | * @param image_name The name of the image being rebound. 425 | * @param mh The in-memory base address of the target image. 426 | */ 427 | static void perform_dyld_rebinding (const PatchTable &patches, const char *image_name, const struct mach_header *mh) { 428 | /* Analyze the image */ 429 | auto image = LocalImage::Analyze(image_name, (const pl_mach_header_t *) mh); 430 | 431 | /* Rebind all symbols */ 432 | image.rebind_symbols([&patches](const bind_opstream::symbol_proc &sp) { 433 | // TODO: We need to evaluate when/how addend is used. 434 | if (sp.addend() != 0) { 435 | // PMDebug("Skipping unsupported symbol binding for %s:%s with non-zero addend %" PRId64, name.image().c_str(), name.symbol().c_str(), addend); 436 | return; 437 | } 438 | 439 | /* Check whether there are /any/ patches for this symbol */ 440 | if (patches.count(sp.name().symbol()) == 0) 441 | return; 442 | 443 | /* Fetch the patches and apply /all/ patches the match; this ensures that patches added later take priority. */ 444 | for (auto &&patch : patches.at(sp.name().symbol())) { 445 | /* Skip non-matching patches */ 446 | if (!std::get<0>(patch).match(sp.name())) 447 | continue; 448 | 449 | /* Apply matching patches */ 450 | auto patchValue = std::get<1>(patch); 451 | uintptr_t *target = (uintptr_t *) sp.bind_address(); 452 | if (*target != patchValue) { 453 | *target = patchValue; 454 | } 455 | } 456 | 457 | }); 458 | 459 | } 460 | 461 | /** 462 | * Perform dyld-compatible symbol rebinding of all references to @a symbol defined by @a library across all current 463 | * and future loaded images. 464 | * 465 | * @param symbol The name of the symbol to patch. 466 | * @param library The install name (e.g. '/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation') of 467 | * the library responsible for exporting the original symbol. 468 | * @param replacementAddress The new address to which the symbol will be bound. 469 | */ 470 | - (void) rebindSymbol: (NSString *) symbol fromImage: (NSString *) library replacementAddress: (uintptr_t) replacementAddress { 471 | using namespace std; 472 | std::map>> foo; 473 | 474 | auto symbolName = SymbolName(library.UTF8String, symbol.UTF8String); 475 | auto patchEntry = make_tuple(symbolName, replacementAddress); 476 | 477 | /* Add to the standard patch table */ 478 | OSSpinLockLock(&_lock); 479 | if (_symbolPatches.count(symbolName.symbol()) == 0) { 480 | _symbolPatches.emplace(make_pair(symbolName.symbol(), vector> { patchEntry })); 481 | } else { 482 | _symbolPatches.at(symbolName.symbol()).push_back(patchEntry); 483 | } 484 | 485 | /* Mock up a patch table and use it to apply the patch to all existing images */ 486 | auto patchTable = PatchTable(); 487 | patchTable.emplace(make_pair(symbolName.symbol(), vector> { patchEntry })); 488 | 489 | for (uint32_t i = 0; i < _dyld_image_count(); i++) { 490 | const char *name = _dyld_get_image_name(i); 491 | perform_dyld_rebinding(patchTable, name, _dyld_get_image_header(i)); 492 | } 493 | OSSpinLockUnlock(&_lock); 494 | } 495 | 496 | /** 497 | * Perform dyld-compatible symbol rebinding of all references to @a symbol defined by *any* library across all current 498 | * and future loaded images. 499 | * 500 | * This is essentially equivalent to single-level namespace symbol binding. 501 | * 502 | * @param symbol The name of the symbol to patch. 503 | * @param replacementAddress The new address to which 504 | */ 505 | - (void) rebindSymbol: (NSString *) symbol replacementAddress: (uintptr_t) replacementAddress { 506 | [self rebindSymbol: symbol fromImage: @"" replacementAddress: replacementAddress]; 507 | } 508 | 509 | 510 | @end --------------------------------------------------------------------------------