OCRunner QQ群: 860147790
Use binary patch files. Increase security, reduce patch size, optimize startup time, and can be optimized in the PatchGenerator stage.
Custom Arm64 ABI (You can also choose to use libffi)
Complete Objective-C syntax support, but does not support pre-compilation and partial syntax.
OCRunnerDemo can be used as a reference for the entire process.
You can't run it successlly with downloading zip file. You must using the below shell commands to tour OCRunnerDemo.
git clone --recursive https://github.com/SilverFruity/OCRunner.git
pod 'OCRunner' #Support all architectures, including libffi.a
# or
pod 'OCRunnerArm64' #Only supports arm64 and arm64e, does not include libffi.a
Unzip PatchGenerato.zip, then save PatchGenerator to /usr/local/bin/ or the project directory.
Run Script
of PatchGeneratorProject Setting -> Build Phases -> click +
in the upper left corner -> New Run Script Phase
[Path to PatchGenerator file] -files [Objective-C source files or diretory] -refs [Objective-C header files or diretory] -output [Path to save the patch]
for example: Run Script
in OCRunnerDemo
$SRCROOT/OCRunnerDemo/PatchGenerator -files $SRCROOT/OCRunnerDemo/ViewController1 -refs $SRCROOT/OCRunnerDemo/Scripts.bundle -output $SRCROOT/OCRunnerDemo/binarypatch
Add the generated patch file as a resource file to the project.
Appdelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
#if DEBUG
NSString *patchFilePath = [[NSBundle mainBundle] pathForResource:@"PatchFileName" ofType:nil];
#else
// download from server
#endif
[ORInterpreter excuteBinaryPatchFile:patchFilePath];
return YES;
}
Run Scrip
to regenerate the patch file.You can run the following code by modifying ViewController1 in OCRunnerDemo.
// A new type called dispatch_once_t will be added
typedef NSInteger dispatch_once_t;
// link NSLog
void NSLog(NSString *format, ...);
typedef enum: NSUInteger{
UIControlEventTouchDown = 1 << 0,
UIControlEventTouchDownRepeat = 1 << 1,
UIControlEventTouchDragInside = 1 << 2,
UIControlEventTouchDragOutside = 1 << 3,
UIControlEventTouchDragEnter = 1 << 4
}UIControlEvents;
int main(){
UIControlEvents events = UIControlEventTouchDown | UIControlEventTouchDownRepeat;
if (events & UIControlEventTouchDown){
NSLog(@"UIControlEventTouchDown");
}
NSLog(@"enum test: %lu",events);
return events;
}
main();
Tips:
It is recommended to create a new file to place the above code, similar to the UIKitRefrence and GCDRefrence files in OCRunnerDemo, and then add the patch generation in the form of -links.
//you only need to add the C function declaration in Script.
//link NSLog
void NSLog(NSString *format, ...);
//then you can use it in Scrtips.
NSLog(@"test for link function %@", @"xixi");
You can run the code by changing the content of ViewController1 in OCRunnerDemo.
When you add this code in scripts. OCRunner will use ORSearchedFunction
to search the pointer of function name. It's core is SymbolSearch
(edit from fishhook).
If the searched result of function name is NULL,OCRunner will notice you in console like this:
|----------------------------------------------|
|❕you need add ⬇️ code in the application file |
|----------------------------------------------|
[ORSystemFunctionTable reg:@"dispatch_source_set_timer" pointer:&dispatch_source_set_timer];
If you want to fix a method, you can reimplement the method without implementing other methods.
@interface ORTestClassProperty:NSObject
@property (nonatomic,copy)NSString *strTypeProperty;
@property (nonatomic,weak)id weakObjectProperty;
@end
@implementation ORTestClassProperty
- (void)otherMethod{
self.strTypeProperty = @"Mango";
}
- (NSString *)testObjectPropertyTest{
[self ORGtestObjectPropertyTest] // Add'ORG' before the method name to call the original method
[self otherMethod];
return self.strTypeProperty;
}
@end
__weak id object = [NSObject new];
// Minimal block
void (^a)(void) = ^{
int b = 0;
};
a();
Its essence is Use system built-in C functions. It is added through the GCDRefrences file in OCRunnerDemo. The GCD related function declaration and typedef are all included in it.
For Example:
// link dispatch_sync
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
void main(){
dispatch_queue_t queue = dispatch_queue_create("com.plliang19.mango",DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
completion(@"success");
});
}
main();
// Inline function: just add a global function in the patch, such as `CGRectMake` in UIKitRefrences
CGRect CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
{
CGRect rect;
rect.origin.x = x; rect.origin.y = y;
rect.size.width = width; rect.size.height = height;
return rect;
}
// Pre-compiled function: you need to add the following code in the App
[[MFScopeChain top] setValue:[MFValue valueWithBlock:^void(dispatch_once_t *onceTokenPtr,
dispatch_block_t _Nullable handler){
dispatch_once(onceTokenPtr,handler);
}] withIndentifier:@"dispatch_once"];
Device: iPhone SE2 , iOS 14.2, Xcode 12.1.
Take the classic Fibonacci sequence function as an example, find the test result of the value of the 25th term
Execution time, the average time is 0.169s
Memory usage has been stable at around 12MB
Execution time, the average time is 1.05s
Memory usage, peak value is about 60MB
Execution time, the average time is 2.38s
The memory usage continues to rise, reaching about 350MB at the highest point.
etc.