|
|
Line 1: |
Line 1: |
| {{Infobox Package
| | The NSAutoreleasePool class is a thin wrapper around the '''NSPushAutoreleasePool''' and '''NSPopAutoreleasePool''' functions. |
| |developer=saurik
| |
| |version=0.9.5001
| |
| |package=mobilesubstrate
| |
| }}
| |
| <small>'''Languages: English • [[MobileSubstrate/fr|français]] • [[MobileSubstrate/th|ไทย]]'''</small>
| |
| | |
| '''Cydia Substrate''' (formerly called '''MobileSubstrate''') is the ''de facto'' framework that allows 3rd-party developers to provide run-time patches (“Cydia Substrate extensions”) to system functions, similar to [http://web.archive.org/web/20021207193945/http://www.unsanity.com/haxies/ape/ Application Enhancer] on the OS X.
| |
| | |
| saurik has written a [http://www.cydiasubstrate.com/id/264d6581-a762-4343-9605-729ef12ff0af/ whole website of documentation for Substrate].
| |
| | |
| Cydia Substrate consists of 3 major components: MobileHooker, MobileLoader and safe mode.
| |
| | |
| == MobileHooker ==
| |
| | |
| MobileHooker is used to replace system functions. This process is known as hooking. There are 2 APIs that one would use:
| |
| | |
| <source lang="c">
| |
| IMP MSHookMessage(Class class, SEL selector, IMP replacement, const char* prefix); // prefix should be NULL.
| |
| void MSHookMessageEx(Class class, SEL selector, IMP replacement, IMP *result);
| |
| void MSHookFunction(void* function, void* replacement, void** p_original);
| |
| </source>
| |
| | |
| MSHookMessage() will replace the implementation of the Objective-C message <tt>-[</tt>''class'' ''selector''<tt>]</tt> by ''replacement'', and return the original implementation. To hook a class method, provide the meta class retrieved from objc_getMetaClass in the MSHookeMessage(Ex) call and see example note below. This dynamic replacement is in fact a feature of Objective-C, and can be done using [http://developer.apple.com/mac/library/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html#//apple_ref/c/func/method_setImplementation method_setImplementation].
| |
| MSHookMessage() is not thread-safe and has been deprecated in favor of MSHookMessageEx()
| |
| | |
| MSHookFunction() is like MSHookMessage() but is for C/C++ functions. The replacement is done at assembly level. Conceptually, MSHookFunction() will write instructions that jumps to the replacement function, and allocate some bytes on a custom memory location, which has the original cut-out instructions and a jump to the rest of the hooked function. Since on iOS by default a memory page cannot be simultaneously writable and executable, a kernel patch must be applied for MSHookFunction() to work. (Any public jailbreak should have one.)
| |
| | |
| As of the latest version of MobileSubstrate, MSHookMessage() also requires a kernel patch for supercall closures to hook all methods properly.
| |
| | |
| It is also possible to get references to classes' ivars via [[Hooking Instance Variables | MSHookIvar]] when not possible via instance methods.
| |
| | |
| === Example code ===
| |
| | |
| Using MSHookFunction:
| |
| | |
| <source lang="c">
| |
| MSHook(void, CFShow, CFTypeRef obj) { // our replacement of CFShow().
| |
| printf("Calling original CFShow(%p)...", obj);
| |
| _CFShow(obj); // calls the original CFShow.
| |
| printf(" done.\n");
| |
| }
| |
| ...
| |
| // hook CFShow to our own implementation.
| |
| MSHookFunction(CFShow, MSHake(CFShow));
| |
| // From now on any call to CFShow will pass through our CFShow replacement first.
| |
| ...
| |
| CFShow(CFSTR("test"));
| |
| </source>
| |
| | |
| Using MSHookMessageEx:
| |
|
| |
|
| <source lang="objc"> | | <source lang="objc"> |
| static IMP original_UIView_setFrame_;
| | #ifdef __cplusplus |
| void replaced_UIView_setFrame_(UIView* self, SEL _cmd, CGRect frame) { // Note the implicit self and _cmd parameters are needed explicitly here.
| | extern "C" { |
| CGRect originalFrame = self.frame;
| | #endif |
| NSLog("Changing frame of %p from %@ to %@", self, NSStringFromCGRect(originalFrame), NSStringFromCGRect(frame));
| | void *NSPushAutoreleasePool(NSUInteger capacity); |
| original_UIView_setFrame_(self, _cmd, frame); // Remember to pass self and _cmd.
| | void NSPopAutoreleasePool(void* token); |
| | #ifdef __cplusplus |
| } | | } |
| ...
| | #endif |
| MSHookMessageEx([UIView class], @selector(setFrame:), (IMP)replaced_UIView_setFrame_, (IMP *)&original_UIView_setFrame_);
| |
| ...
| |
| myView.frame = CGRectMake(0, 0, 100, 100);
| |
| </source> | | </source> |
|
| |
|
| Note that if you are hooking a class method, you have to put a meta-class in the ''class'' argument, e.g.
| | Example: |
|
| |
|
| <source lang="objc"> | | <source lang="objc"> |
| MSHookMessageEx(objc_getMetaClass("UIView"), @selector(commitAnimations), replaced_UIView_commitAnimations, (IMP *)&original_UIView_commitAnimations);
| | static void MyMethod() |
| </source>
| |
| | |
| Using MSHookFunction to hook private functions (in this case a C++ Method):
| |
| | |
| <source lang="c">
| |
| #define PRIVATE_FRAMEWORKS "/System/Library/PrivateFrameworks"
| |
| #define WEBKIT PRIVATE_FRAMEWORKS"/WebKit.framework/WebKit"
| |
| #define WEBKIT_FUNC_NAME "__ZN20WebFrameLoaderClient23dispatchWillSendRequestEPN7WebCore14DocumentLoaderEmRNS0_15ResourceRequestERKNS0_16ResourceResponseE"
| |
| #define WEBCORE PRIVATE_FRAMEWORKS"/WebCore.framework/WebCore"
| |
| #define WEBCORE_FUNC_NAME "__ZNK7WebCore15ResourceRequest12nsURLRequestEv"
| |
| | |
| NSURLRequest* (*webcore_func)(void* something);
| |
| | |
| void (*webkit_func)(void* something, void* loader, unsigned long identifier, void* request, const void** response);
| |
| | |
| | |
| MSHook(void, webkit_func, void* something, void* loader, unsigned long identifier, void* request, const void** response) {
| |
|
| |
| NSURLRequest *nsRequest = webcore_func(request);
| |
| //do something
| |
| _webkit_func(something, loader, identifier, request, response);
| |
| }
| |
| | |
| template <typename Type_>
| |
| static void nlset(Type_ &function, struct nlist *nl, size_t index) { | |
| struct nlist &name(nl[index]);
| |
| uintptr_t value(name.n_value);
| |
| if ((name.n_desc & N_ARM_THUMB_DEF) != 0)
| |
| value |= 0x00000001;
| |
| function = reinterpret_cast<Type_>(value);
| |
| }
| |
| | |
| //later, in your %ctor
| |
| | |
| dlopen(WEBKIT, RTLD_LAZY | RTLD_NOLOAD);
| |
| struct nlist nl[2];
| |
| bzero(&nl, sizeof(struct nlist) * 2);
| |
| nl[0].n_un.n_name = (char*)WEBKIT_FUNC_NAME;
| |
| | |
| dlopen(WEBCORE, RTLD_LAZY | RTLD_NOLOAD);
| |
| struct nlist nl2[2];
| |
| bzero(&nl2, sizeof(struct nlist) * 2);
| |
| nl2[0].n_un.n_name = (char*)WEBCORE_FUNC_NAME;
| |
| if(nlist(WEBKIT, nl) < 0 || nl[0].n_type == N_UNDF)
| |
| {
| |
| fprintf(stderr, "\n nlist(%s, %s) failed\n", "WebKit", nl[0].n_un.n_name);
| |
| }
| |
| else if (nlist(WEBCORE, nl2) < 0 || nl2[0].n_type == N_UNDF)
| |
| { | | { |
| fprintf(stderr, "\n nlist(%s, %s) failed\n", "WebCore", nl2[0].n_un.n_name); | | void *pool = NSPushAutoreleasePool(0); |
| }
| | [[[NSObject alloc] init] autorelease]; |
| else
| | NSPopAutoreleasePool(pool); |
| {
| |
| nlset(webcore_func, nl2, 0); | |
| nlset(webkit_func, nl, 0);
| |
| MSHookFunction(webkit_func, MSHake(webkit_func));
| |
| }
| |
| </source>
| |
| | |
| Because we want the pointer to a private symbol we have to use nlist. However you should not be doing it this way, as it is not portable to other platforms and later versions of iOS. [http://www.cydiasubstrate.com/api/c/MSGetImageByName MSGetImageByName] and [http://www.cydiasubstrate.com/api/c/MSFindSymbol/ MSFindSymbol] provide the same functionality and are portable.
| |
| | |
| == MobileLoader ==
| |
| | |
| MobileLoader loads 3rd-party patching code into the running application.
| |
| | |
| MobileLoader will first load itself into the run application using [http://koichitamura.blogspot.com/2008/11/hooking-library-calls-on-mac.html <tt>DYLD_INSERT_LIBRARIES</tt>] environment variable. Then it looks for all dynamic libraries in the directory <tt>/Library/MobileSubstrate/DynamicLibraries/</tt>, and dlopen them. An extension should use constructor code to perform any works, e.g.
| |
| <source lang="objc">
| |
| ...
| |
| // The attribute forces this function to be called on load.
| |
| __attribute__((constructor))
| |
| static void initialize() {
| |
| NSLog(@"MyExt: Loaded");
| |
| MSHookFunction(CFShow, replaced_CFShow, &original_CFShow);
| |
| } | | } |
| </source> | | </source> |
|
| |
|
| === Filters ===
| | The "capacity" argument of NSPushAutoreleasePool only serves as a hint. It is unused in the current implementation. |
| | |
| Developers may add filters to restrict whether the extension should be loaded or not. Filters are implemented as plist that lives beside the dylib. If the dylib is named <tt>foo.dylib</tt>, then the filter should be named <tt>foo.plist</tt>. The filter should be a dictionary with key '''Filter''', which is another dictionaries that can contain these keys:
| |
| * '''CoreFoundationVersion''' (array): The extension is loaded only if the version of [[CoreFoundation.framework]] is above the specified values. Currently, only the first 2 values are checked.
| |
| {{CoreFoundation Version Table|center=1}}
| |
| * '''Bundles''' (array): The extension is loaded only if the bundle-ID of the running application matches the list.
| |
| * '''Classes''' (array): The extension is loaded only if the one of the specified objective-C classes is implemented in the application.
| |
| * '''Executables''' (array): The extension is loaded only if one of the executable names matches the running application. This is required to hook things that have no other identifiable characteristics.
| |
| | |
| For example, to restrict the extension only load in {{applink|SpringBoard}}, the plist would look like
| |
| Filter = {
| |
| Bundles = (com.apple.springboard);
| |
| };
| |
| | |
| You can also use this method to restrict the extension to only load into applications that link to a specific bundle, such as UIKit. For example:
| |
| Filter = {
| |
| Bundles = (com.apple.UIKit);
| |
| };
| |
| | |
| | |
| You can use CoreFoundationVersion key and specify lower- and upper-bounds. When two values are in the array, the first is treated as greater-than-or-equal-to rule, while the second is a less-than rule. The following example shows loading restricted to firmwares from 4.0 to 4.3 only:
| |
| | |
| Filter = {
| |
| CoreFoundationVersion = (550.32, 675.00);
| |
| };
| |
| | |
|
| |
| In general the rule is that if you are using more than one filter (example: Executables and Bundle) all filters have to match. You can change that by using Mode = "Any".
| |
| | |
| Filter = {
| |
| Executables = ("mediaserverd");
| |
| Bundles = ( "com.apple.MobileSMS", "net.whatsapp.WhatsApp" );
| |
| Mode = "Any";
| |
| };
| |
| | |
| As of iOS 9.0, the filter plist ''must'' exist. Dylibs without a corresponding plist will not be loaded. To replicate the previous effect of no filter plist causing the dylib to be loaded into all processes, set your filter to the bundle <code>com.apple.Security</code>.
| |
| | |
| For setuid apps, since almost all environment variables are discarded, the developer of the app must explicitly perform <code>dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY)</code> within <code>main()</code> to let MobileLoader run. This is not recommended, since most extensions do not expect to be running within a root process, and can have unexpected behavior (such as writing a file with permissions that would disallow it from being read by non-root processes). It would be a better choice to design the app to run as mobile, with a helper process performing root operations.
| |
| | |
| In addition, MobileLoader also hooks <tt>nlist()</tt> to improve its performance, and defines several signal handlers for safe mode.
| |
| | |
| == Safe mode ==
| |
| | |
| When a extension crashed the SpringBoard, MobileLoader will catch that and put the device into safe mode. In safe mode all 3rd-party extensions will be disabled.
| |
| | |
| The following signals will invoke safe mode:
| |
| * SIGABRT
| |
| * SIGILL
| |
| * SIGBUS
| |
| * SIGSEGV
| |
| * SIGSYS
| |
| | |
| == External links ==
| |
| | |
| * Safemode source: http://gitweb.saurik.com/safemode-ios.git (git://git.saurik.com/safemode-ios.git)
| |
|
| |
|
| {{Navbox HookingLibs}} | | {{occlass|library=Foundation.framework}} |
| {{Navbox Library}}
| |
| [[Category:Directories in /Library]]
| |