preface

How do we dynamically analyze and debug other people’s applications and then inject scripts for our own purposes? We have all heard of Hook, so what is its principle and what kinds of Hook technology are there? The so-called knife does not mistakenly cut wood, the following several problems listed Hook principle and dynamic debugging, as a note for reference and learning only.

1.0 Hook principle

I have a HOOK. In iOS reverse is the technique of changing the flow of a program. Hook can let other people’s programs to execute their own code, we focus on understanding its principle, so that malicious code can be effectively protected. The following several hook technology analysis.

1.1 MethodSwizzle

This is the more familiar Hook method, callOC system apis, using OC’sRuntimeProperties, dynamic changeSEL (method number) and IMP(method implementation), to achieve the purpose of OC method call process change. It is mainly used forOC method. In OC, the relationship between SEL and IMP is like that of a book"Directory". SEL is method number"Title"The same. IMP is the real address of the method implementation, like the “page number.” They are one-to-one correspondence, as shown in the figure below, we achieve the purpose of hook by changing this correspondence.

Use case

The following takes hook wechat login event as an example. When the login button is clicked, the input password is obtained without affecting the original login logic.

  • referenceReverse with shell smashingArticle, create a demo project, put the smashed wechat IPA package into the APP folder,Pre-run projectInstall the demo on your phone
  • withappSign.shThe scriptRe-sign wechatAnd install it on your phone. Note that the appsign.sh script is not deletedmachoCannot be signed inWatch, the pluginFolders, the latest version of wechat 8.0.16 also need to be deletedcom.apple.WatchPlaceholderFolder, as follows

  • XcodeRun re-signed wechat,lldbAdditional debugging wechat login interface

  • Click the login button to see the login message sentidforWCAccountMainLoginViewController.SELforonNextWe also need to know which UITextField the password control is,Class-dumpWechat header file, static analysisWCAccountMainLoginViewController.hThe header file looks like this

No UI component associated with the password was found, but there is a property named_mainPageViewOpen the fileWCAccountLoginMainPageView.hFiles looking for passwords related to the UI

Seems to have found a very password-related property_passwordTextItem, open the fileWCRedesignTextItem.h

fileWCRedesignTextItem.hThere is an attribute in_textFiledOpen,WCUITextField.hfile

WCUITextField inherits from UITextField, so far we have located the password component, and the keyValue relationship is: WCAccountMainLoginViewController – > _mainPageView – > _passwordTextItem – > _textField.

  • onNextMethod found, password component also found, you can hook code, a new demowgy.framework, write hook code as follows

I got the password, but[self newnext]callThe original logic collapsedThe breakpoint is at line 23, print the id and sel at this point, id isWCAccountMainLoginViewController, selnewNext, butWCAccountMainLoginViewControllerAnd in theThere is no newNext method, so you need to add this method to the class and change it to the following

+ (void)load{
 Method oldmethod=class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
const char * typeencode=  method_getTypeEncoding(oldmethod);

 class_addMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(newNext),method_getImplementation(class_getInstanceMethod(self, @selector(newNext))), typeencode);
 
 method_exchangeImplementations(oldmethod, class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(newNext))); } - (void)newNext{
    UITextField* password=[[[self valueForKey:@"_mainPageView"] valueForKey:@"_passwordTextItem"] valueForKey:@"_textField"];
    NSLog(@"% @",password.text);
    [self newNext];

}
Copy the code

Method_exchangeImplementations although methods can be exchanged, but if you don’t pay attention to calling the original method, you can use class_replaceMethod instead

+ (void)load{
    Method oldmethod=class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
   const char * typeencode=  method_getTypeEncoding(oldmethod);

    oldimp= class_replaceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext), newNext, typeencode);

}

IMP (*oldimp)(id self,SEL _cmd);

void newNext(id self,SEL _cmd){
    UITextField* password=[[[self valueForKey:@"_mainPageView"] valueForKey:@"_passwordTextItem"] valueForKey:@"_textField"];
    NSLog(@"% @",password.text);
    oldimp(self,_cmd);
}
Copy the code

IMP= (*oldimp)(id self,SEL _cmd); oldimp =(oldimp)(id self,SEL _cmd) Class_replaceMethod is a bit simpler, but many third-party hook frameworks use the setImp and getImp apis to implement hooks, such as the Monkey framework. I also feel that setImp and getImp are more logical

+ (void)load{
oldimp=method_getImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)));
method_setImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)), newNext);
}
IMP (*oldimp)(id self,SEL _cmd);

void newNext(id self,SEL _cmd){
    UITextField* password=[[[self valueForKey:@"_mainPageView"] valueForKey:@"_passwordTextItem"] valueForKey:@"_textField"];
    NSLog(@"% @",password.text);
    oldimp(self,_cmd);
}
Copy the code
  • So once you’ve written the logical code, this is the last thing you need to doFrameWorkInjected into theMacho LoadCommandsIn the paragraph, this involvesdyldLoading principle, useyololibTool changeMachO.yololibDownload it and copy it into the project directoryappSign.shAdd the following script at the bottom and re-run the APP to inject successfully
yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/wgyFramework.framework/wgyFramework"
Copy the code

MethodSwizzle is mainly used forHook OCMethod, often using the following API

  • method_exchangeImplementations: Exchange two API, use should be careful, to avoid the problem of calling the original method crash
  • class_replaceMethod: Replace the IMP of the original method, saveAddress of the primitive function pointer.
  • method_getImplementation,method_setImplementationSetImp sets the address of the pointer to the new function.It is recommended to use.

1.2 FishHook

FaceBook provides a tool, using MachO file loading principle, by dynamically changing the pointer address of lazy and non-lazy two tables to achieve the purpose of Hook C function. Note that the C function here refers to the system’s own C functions, such as MethodSwizzle function, NSLog function and other system shared cache C functions, can not Hook their own C functions. Let’s see how it works, and then how it works, okay

Use case

So let’s take the Hook SYSTEM API “method_exchangeImplementations” C function, and we can sort of feel the inverse protection, so we’ll replace the method_exchangeImplementations system function with something we defined ourselves, So that’s invalidated when someone uses the system method method_exchangeImplementations to attack your APP. Complete the following steps

  1. newfishhookdemoProject, the main interface defines a UIButton, click the button to trigger the eventonNext, pop-up prompt “Welcome you”, the purpose is to demonstrate protection.
  2. Created in the Demo projectprotected.frameworkFor protection,Pay attention toFenced code generally uses the Framework becauseThe FrameWork loads before the MachO main processAnd theSelf-written frameworks load before others inject frameworksSo we need to execute our protection code as much as possible first
  3. downloadFishHook source fileCopy,Fishhook. C and fishhook. HFile in protected. Framework and create a new protected fileSaveProtect. H and saveProtect. M
  4. SaveProtect protection code is written as follows, noteannotationThe use of functions in
@interface saveProtect : NSObject
// The original function address is exposed for its own use
CF_EXPORT void (*old_exchange)(Method m1,Method m2);
@end
@implementation saveProtect
+ (void)load{
    struct rebinding rebind;
    rebind.name="method_exchangeImplementations";
    rebind.replacement=new_exchangeImp;
    rebind.replaced=(void*)&old_exchange;

    struct rebinding rebs[]={rebind};
    rebind_symbols(rebs, 1);
}
// The original function pointer can be placed in a header file to expose its own project use
void (*old_exchange)(Method m1,Method m2);
/ / the new function
void new_exchangeImp(Method m1,Method m2){
    NSLog(@"Hook detected");
}
@end
Copy the code
  1. packagingfishhookdemo.ipa
  2. Create a new demo project to crack fishhookDemo. ipa, writeMethodSwizzleCode HookonNextmethods
+ (void)load{
    class_addMethod(objc_getClass("ViewController"), @selector(newNext), newNext, "v@:");
    method_exchangeImplementations(class_getInstanceMethod(objc_getClass("ViewController"), @selector(onNext)), class_getInstanceMethod(objc_getClass("ViewController"), @selector(newNext)));
}

void newNext(id self,SEL _cmd){
    NSLog(@"Hook to the");
}
Copy the code

So if method_exchangeImplementations Hook succeeds, hitting the Button should print “Hook is up”, and the fact is that there is still a “welcome” prompt that says method_exchangeImplementations is invalid, Successful defense.

FishHook principle

Ios has a special location for the system dynamic library, known as the dynamic shared cache, and FishHook uses PIC technology to dynamically change the value of pointer addresses when rebinding.

  • Due to theExternal functionThe memory location cannot be determined at compile time.
  • Apple USESPICTechnology (location-independent code). inmachofileDataCreate two tables,Lazy loading and non-lazy loading tables.Holds a pointer to an external function
  • For the first timecallLazy loading function, will go toThe pile of codeExecution, the first execution will be executedbinderfunctionThe binding
  • FishHookusingStringtable -> Symbols > Indirect SYbols -> Lazy load symbol tableThe corresponding relationship betweenHeavy bindingModifies the value of a pointer

1.3 InlineHook

The so-called inlineHook is to directly modify the header code of the target function to make it jump to our self-defined function to execute our code, so as to achieve the purpose of Hook. This Hook technology is generally used in static language hooks, such as custom C function, or C function of swift language. This fixes FishHook’s inability to Hook C functions. Dobby’s best framework is at home, so it’s as easy as using FishHook.

Compile the Dobby

  1. git clone https://github.com/jmpews/Dobby.git --depth=1“, download the Dobby source locally, depth specifiedThe depth of cloningIf the value is 1, only the latest COMMIT is cloned
  2. Due to theDobbyIt’s cross-platform, so you need toCompile into Xcode project. Run the following command to create onebuild_for_ios_arm64Folder put compiledThe Xcode project
cd Dobby && mkdir build_for_ios_arm64 && cd build_for_ios_arm64

cmake .. -G Xcode \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_TOOLCHAIN_FILE=cmake/ios.toolchain.cmake \
-DPLATFORM=OS64 \
-DARCHS=arm64 \
-DENABLE_BITCODE=0 \
-DENABLE_ARC=0 \
-DENABLE_VISIBILITY=1 \
-DDEPLOYMENT_TARGET=9.3 \
-DCMAKE_SYSTEM_PROCESSOR=aarch64 \
-DDynamicBinaryInstrument=ON -DNearBranchTrampoline=ON \
-DPlugin.FindSymbol=ON -DPlugin.HideLibrary=ON -DPlugin.ObjectiveC=ON
Copy the code
  1. After compilingbuild_for_ios_arm64The Xcode project in the folder is as follows. Then compile the Xcode project to generateDobbyX.framework

Use case

  1. newdemoengineering
  2. copyDobbyX.frameworkThere are two possible errors when running the Demo project
    • Bitcode error: Either set bitcode support when compiling dobbyx.framework, or bitcode=NO in demo
    • dyld: Library not loaded: @ rpath/DobbyX framework/DobbyX Reason: image not found error: framework for the first time into the project, Xcode will not help us to copy into the Macho, so you need toCopy it manually, as shown below

3. Add test functions on the main interfacesum().Dobbyhooksum()The function is replaced byNew_sum (), tap the screen to invokesum()Function, code as follows

@implementation ViewController
int sum(int a,int b){
    return a+b;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    // Argument 1: address of the function that needs hook
    // Parameter 2: address of the new function
    // Parameter 3: preserves the address of the pointer to the original function
    DobbyHook(sum, new_sum, (void*)&originsum);

}
// Save the original function pointer address for subsequent calls
int (*originsum)(int a,int b);
int new_sum(int a,int b){
    NSLog(@"Original result: % I",originsum(a,b));
    returna*b; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@Result: % I,sum(10.20));
}
Copy the code

Click on the screen console and the output is as follows:

We’ve obviously hooked the sum() function and executed our own. C function assembly, look at how Dobby, broken lives the sum () function, hook before and after the hook assembly below

After hook, only the first three lines are different, and there is no stretch stack space, jump into BR x17, look at X17

Dobby inserted the new function, new_sum(), directly in the header of the original function sum(), so inlineHook modified the target function header code directly. New_sum (), x8, x8, x8, x8

To follow upblr x8Look at the assembly

If the original function is called, stretch the stack space and br x17 executes back to the original function. Inlinehook can hook custom C functions, so can hook system functions? Try Hook NSLog() as shown below

Dobby was mighty,External system functions can also Hook“At that point, it seemed possible to get Dobby done instead of FishHook.

The principle of summary

  • inlineHookI’m going to modify it directlyHeader code for the target function.
  • If the original function is called in the new function, the stack space is stretched and the original function is called otherwise the original function is not executed.
  • inlinehookIs in theDynamic head insertionThe new function,__textSegments are read-only.

Address substitution function

After all, the release package is unsigned. We usually get the offset address of the function by analyzing Macho, and then get the first address of Macho ASLR. The offset address +ASLR is the real address of the function. Sum () = LLDB () = LLDB () = LLDB () = LLDB () = LLDB () = LLDB ()

As shown in the figure, the offset of the sum() function is:0x104119e18-0x1041114000=0x5e18.machoSee the0x5e18The offsetcontrastlldbSum assembler and Macho assembler, this should besum()Function, we know that the offset of sum() is0x5e18Do the whole thing, package the demo project, create a new sample project and re-sign it as abovedemoProject, as shown below

Analysis:

  • Sum () = pageZero; sum() = pageZero; sum() = pageZero; sum() = pageZero

  • After dragging dobbyx. framework into the main project, remember to link dobbyx. framework in wgyFramework, otherwise the dynamic library will not be found by compilation

  • It also needs to be therewgyFrameworkSet in theFramework search pathReference path, otherwise import header file can not find the header file error. (Library Search PathsIs that this is.aThe path)

1.4 Monkey

Logos Tweak, CaptainHook Tweak, command-line Tool, Monke4App is a powerful Tool for jailbreak and non-jailbreak developers that integrates four modules, Logos Tweak, CaptainHook Tweak, command-line Tool, Monke4App, and more. Monkey Hook: getImp and setImp: Monkey Hook: getImp and setImp: Monkey hook: getImp and setImp: setImp: Monkey hook: getImp and setImp: setImp

  • Install thoes
//1. Install the latest theOS recursively
sudo git clone --recursive https://github.com/theos/theos.git /opt/theos
//2. Install lDID signature tool, jailbreak plug-in signature tool, coDesign is official signature tool,
brew install ldid
Copy the code
  • Xcode installs the plug-in
sudo /bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/AloneMonkey/MonkeyDev/master/bin/md-install)
Copy the code
  • After the installation is successful,/optYou’ll find it in your directoryTheos, MonkeyDev, frida-ios-dumpFolder, and the Xcode new project will appearMonkeyAppoptions

  • remindIf any error occurs during the installation, refer to the following two articles
    • www.jianshu.com/p/208906289…
    • www.jianshu.com/p/767304005…

– error # libstdc++ not found

  • Modify the/opt/MonkeyDev/Tools/pack.shFile, add deletecom.apple.WatchPlaceholderOtherwise, the latest wechat cannot be re-signed

Use case

Or in theHook wechat loginFor example, if you click the login button, you can obtain the entered password without affecting the original login logic.XcodeSelect a new Demo projectMonkeyApp,Smash the shell of wechat. IpaPackage copy intoTargetAppfolder

inLogosFolder to prepare Hook code as follows, run the program can be very simple HookonNextMethods.

Logos grammar

The simple example above is a Hook code written through the Logos syntax, so it is important to understand the syntax of Logos. According to the Logos syntax on the website, there are three sections. Memorizing them is useless.

Conclusion:

  • %hook,%end: Hooks a class
  • %group,%end: grouping, each group needs to be constructed by the %ctor() function, initialized by %init(group name)
  • %logOutput method details (caller, method name, method parameters)
  • %origCalling the original method, you can pass parameters and receive return values
  • %cSimilar to getClass, get a class object
  • %newAdd a method
  • It is often used in practical applicationsMSHookIvarTo obtainAttributes of a class, such as MSHookIvar<UITableView*>(self, “_tableView”), to get the _tableView property of the self class
  • It is often used in actual developmentIn response to the chainFind specific classes, such as[tableview.nextResponder.nextResponder isKindofClass:%c(WeiXinViewController)], the tableView response chain is used to determine whether the TableView belongs to the class WeiXinViewController

2.0 Dynamic Debugging

Reverse is no source code, so can not set breakpoints in the source code, followed by debugging others on the shelf of the application is off the symbol, can not directly give a symbol under the breakpoint, only dynamic debugging to find memory address, and then give a specific memory address under the breakpoint. So it is necessary to master the method of dynamic debugging, and then with static analysis can be very easy to play reverse. Let’s take a look at the dynamic debugging tools LLDB, Cycript and Reveal.

2.1 LLDB

Dynamic debugging tools built into Xcode by default. The standard LLDB provides an extensive set of commands designed to be compatible with older versions of GDB commands. In addition to using standard configurations, you can easily customize the LLDB to suit your needs.

Common instructions are as follows:

Normal breakpoints

  • To set breakpoints:breakpoint set -n xxxIf it is a method of a class, use double quotation marks, such as “-[viewController Touchbegin :]”.breakpoint set -r xxx, block all places that contain “XXX”.Breakpoint - a addressTo set a breakpoint on the address. (B-n XXX, short for XXX)
  • View all breakpoints:breakpoint list
  • Delete breakpoint:breakpoint delete
  • Disable/enable breakpoints:breakpoint disable/breakpoint enable
  • To continue:c(the continue abbreviated)
  • To execute a single function as a whole in one step:n(next abbreviation)
  • Run in a single step, if it encounters a subfunction, it will enter:s

Function call stack type

  • View the function call stack:bt
  • Select to enter the concrete stack:frame select 12, and enter the stack number 12, but enter the stack, the data will not change
  • Walk through the current method and return to the upper layerThe call stack frame:finish
  • View the id and method parameters of the current function stack:frame variableIt’s easy to use.
  • Function call stack rollback:up/down
  • Rollback function stack:thread returnUp /down, which affects the result of execution

Memory breakpoint

  • Listen for changes in memory values and enter breakpoints when changes occur:watchpoint set variable p1->nameOr,Watchpoint set expression Memory address
  • Memory breakpoint removal:whatch delete, Disable, etc., similar to BreakPoint
  • Global breakpoint listening:Target stop-hook add -o "frame variable"“Stop-hook” is used to display the frame variable whenever a breakpoint occurs

other

  • Execution code:pPo stands for calling the discraption method of an object.
  • View instructions:help breakpoint
  • View the mirror list:image list
  • Register read and write:register read/write x0
  • Read memory value:Memory read 或者 x

Just memorizing is useless, must refer to the practice, more instructions please refer to the LLDB official website. Using these instructions can be tedious and often requires several instructions to locate your view, so we need to use tools that encapsulate the LLDB API.

chisel

LLDB API wrapped by Facebook, using tools called by Python. First download and install Chisel, refer to this website for specific installation steps, configure. Lldbinit and then you can use it.

Common convenience instruction

  • pviews: Prints the view level, pview-u, prints the upper view level
  • pvc: Displays the current controller
  • Pactions Indicates the address of the pointer: Prints the page on which the button is located and its click event method
  • Presponder pointer address: Gets the response chain for the button
  • Pclass pointer address: Prints the controller inheritance relationship
  • Pmethods pointer address: Prints all methods
  • Pinternals Pointer address: Prints all member attributes
  • Fvc-v pointer address: Locate the controller to which it belongs
  • Fv UI name: Displays how many such views the current controller has, for example, fV WCUITextFiled
  • Flicker pointer address: Locate the control will flicker, this is very useful oh
  • Vs pointer address: Enters a specific control and can be debugged. Q exits debugging. This is handy for finding components.
    • (w)Move to superView
    • (s)Move to first subview
    • (a)Move to previous Sibling
    • (d)Move to next Sibling
    • (p)Print the Hierarchy

Here is an example of locating the login button in the wechat login interface. First, pViews prints all the views, randomly find a component address, flicker locates the component, and when it is located, it will blink. Then vs component address goes into the component for debugging, using w, S, A, D, P instructions to locate the login button, as shown in the picture below.

DerekSelander

This LLDB tool is more convenient to use in conjunction with Chisel, so it is common to install both tools together. First download DerekSelander to the local directory, then unzip to the specified directory, such as /opt, edit.lldbinit to specify dslldy.py, as shown in the following image.

Common convenience instruction

  • search UIView: Global search view
  • Methods pointer address: Print all methods (and method addresses, breakpoints can be set to address)
  • sbt: Prints the call stack. Note that the stack isRestore partial method symbolsThe bt command prints without symbols.

See DerekSelander for more instructions, here with chisel to see what methods are available on the home screen.

2.2 cycript

Cycript is a scripting language developed by Saurik, the founder of Cydia. Cycript mixes OC with the interpreter for JavaScript syntax, which means that we can use OC or JavaScript, or both, in a single command. It can hook up to running processes and modify a lot of things at run time. Unlike LLDB, cycript does not block processes, so it is convenient to debug applications dynamically, which is also a tool often used in reverse, whereas LLDB add-on processes are blocked. The installation steps are as follows

  • Website: www.cycript.org/
  • Once downloaded, use the executable file Cycript
  • For convenience, we can put it/ opt/cycript_0. 9.594(the opt directory is optional), and also configure the environment variables in ~/.bash_profile (the path of the execution file).

Special reminder: MonkeyDev/bin contains the executable file, so it is highly recommended to install and configure the environment variables as follows: This allows the console to directly use tools in MonkeyDev/bin such as class-dump, dump.py, and cycript.

Non-jailbreak debugging

The Monkey project automatically packages the Cycript static library into your APP, so you can only dynamically debug this APP instead of other apps. The default Monkey port number for cycript injected into your phone is 6666. The Cycript tool on your Mac uses this port number to connect to cycript in your APP.

The cycript command in the Mac connection APP is as follows:

cycript -r 192.1681.98.:6666
Copy the code
  • here192.168.2.2Cell phone isipThe address,6666Is the cycript port number in the APP on your phonecycriptCommand encapsulation asA shell scriptHowever, it is not recommended here, after all, once the wireless network changes the IP will change, so it is easier to package USB connection.
  • inJailbreak and shell smashingWe wrote one in the articleusbConnect.shScript, encapsulatedSSH via USBLink cell phones. Here again, two shell scripts are encapsulated, onecyusbConnect.shDo USB port mapping, onecyLogin.shMake a cycript connection to the APP. Sh and cyusbconnect. sh are two port mapping scripts combined together. If you know, please leave a message to me.

Common commands

  • It is strongly recommended thatUIApp, UIApp. KeyWindow. RootViewControllerAnd so on commonly used command encapsulationCustom cyThe cy file is then imported into the phone for use. It is very convenient to customize cy files in both jailbroken and non-jailbroken environments, such as the following customizationGY.cy.
//IIFE anonymous function self-executing expression
(function(exports){
 APPID = [NSBundle mainBundle].bundleIdentifier,
 APPPATH = [NSBundle mainBundle].bundlePath,
 // If there is a change, use function to define it.
 GYRootVc = function(){
 return UIApp.keyWindow.rootViewController;
 };
 GYKeyWindow = function(){
 return UIApp.keyWindow;
 };

GYGetFrontVcFromRootVc = function(rootVC){
 var currentVC;
 if([rootVC presentedViewController]){
 rootVC = [rootVC presentedViewController];
 }
 if([rootVC isKindOfClass:[UITabBarController class]]){
   currentVC =   GYGetFrontVcFromRootVc(rootVC.selectedViewController);
 }else if([rootVC isKindOfClass:[UINavigationController class]]){
  currentVC = GYGetFrontVcFromRootVc(rootVC.visibleViewController);
 }else{
  currentVC = rootVC;
 }
  return currentVC;
 };
 // The controller currently being displayed
 GYFrontVc = function(){
  return GYGetFrontVcFromRootVc(GYRootVc());
 };
 GYVcViews=function(vc){
       if(! [vc isKindOfClass:[UIViewControllerclass]]) throw new Error(invalidParamStr);
       return vc.view.recursiveDescription().toString(); 
 }
// The UI level currently being displayed
 GYFrontViews = function(){
 var currentVC=GYGetFrontVcFromRootVc(GYRootVc());
         return GYVcViews(currentVC);

 };
 // Gets the method names of all TouchUpInside events bound to the button
 GYTouchUpEvent = function(btn) { 
        var events = [];
        var allTargets = btn.allTargets.allObjects();
        var count = allTargets.count;
        for (var i = count - 1; i >= 0; i--) { 
                if(btn ! = allTargets[i]) {vare = [btn actionsForTarget:allTargets[i] forControlEvent:UIControlEventTouchUpInside]; events.push(e); }}return events;
 };

 // Print the view hierarchy recursively
GYSubviews = function(view) { 
      if(! [view isKindOfClass:[UIViewclass]]) throw new Error(invalidParamStr);
     return view.recursiveDescription().toString(); 
};
var _GYClass = function(className) {
        if(! className)throw new Error(missingParamStr);
        if (MJIsString(className)) {
                return NSClassFromString(className);
        } 
        if(! className)throw new Error(invalidParamStr);
        // Object or class
        return className.class();

};
// Prints all subclasses
GYSubclasses = function(className, reg) {
        className = GYClass(className);
        return [c for each (c in ObjectiveC.classes) 
        if(c ! = className && class_getSuperclass(c) && [c isSubclassOfClass:className] && (! reg || reg.test(c))) ]; };var _GYGetMethods = function(className, reg, clazz) {
        className = GYClass(className);
        var count = new new Type('I');
        var classObj = clazz ? className.constructor : className;
        var methodList = class_copyMethodList(classObj, count);
        var methodsArray = [];
        var methodNamesArray = [];
        for(var i = 0; i < *count; i++) {
                var method = methodList[i];
                var selector = method_getName(method);
                var name = sel_getName(selector);
                if(reg && ! reg.test(name))continue;
                methodsArray.push({
                        selector : selector, 
                        type : method_getTypeEncoding(method)
                });
                methodNamesArray.push(name);
        }
        free(methodList);
        return [methodsArray, methodNamesArray];

};
var _GYMethods = function(className, reg, clazz) {
        return GYGetMethods(className, reg, clazz)[0];
};

var _GYMethodNames = function(className, reg, clazz) {
     return GYGetMethods(className, reg, clazz)[1];
};
// Prints all object methods
GYInstanceMethods = function(className, reg) {
    return _GYMethods(className, reg);
};

// Prints all object method names
GYInstanceMethodNames = function(className, reg) {
    return _GYMethodNames(className, reg);
};

// Prints all class methods
GYClassMethods = function(className, reg) {
    return _GYMethods(className, reg, true);
};

// Prints all class method names
GYClassMethodNames = function(className, reg) {
    return _GYMethodNames(className, reg, true);
};

// Prints all member variables
GYIvars = function(obj, reg){
    if(! obj)throw new Error(missingParamStr);
    var x = {};
    for(var i in *obj) {
        try {
            var value = (*obj)[i];
            if(reg && ! reg.test(i) && ! reg.test(value))continue;
            x[i] = value;
        } catch(e){}
    }
    return x;
};

// Prints all member variable names
GYIvarNames = function(obj, reg) {
    if(! obj)throw new Error(missingParamStr);
    var array = [];
    for(var name in *obj) {
        if(reg && ! reg.test(name))continue;
        array.push(name);

    }
    returnarray; }; }) (exports);
Copy the code
  • XcodeOpen the Monkey project and importGY.cyThe file looks like this, Xcode reruns the project,GY.cyIt’s packaged into your APP, and when YOU use your custom cy file,Need @ the import

  • MonkeyThe project will download it for us automaticallymd.cyandMS.cyFile and package into APP, mD.cy provides some convenient instructions such asPviews (), PVCS (), RP (), pactions()Ms. Cy provides dynamic code insertion. Note these two CY filesDon't need @ importThe import.

However, these two files can not be downloaded due to network problems, so it is necessary to change the git source address and replace these two URLS withRaw.fastgit.org/AloneMonkey… 和 Raw.fastgit.org/AloneMonkey…

The demo is as follows:

Prison break debugging

The cycript plugin can be downloaded from the jailbreak store Cydia. Once the plugin is downloaded, SSH can connect to the phone to attach the Cycript process.

The cycript plugin folder is shown in the picture above. Although you can attach apps on your phone, some shortcut commands such as APPID and PVCS () are not available, so we need to import our custom Gy. cy and mD. cy downloaded from Git. SCP copy the cy file to /usr/lib/cycript0.9/com/, create a gy folder in com, as shown below

When used@importImport it and you can happily use the shortcut command, the additional genuine wechat demo is as follows

Analysis:Cycript -p Process ID/appIDAppend process. It is possible to import custom Gy. cy, but it is not possible to import MD. cy. I am not sure about this, and some friends who know this can leave me a message.

2.3 pass Reveal

Reveal is a tool that allows you to dynamically debug your APP UI. Compared to xcode’s built-in View Debugger, it does not block the APP process and can dynamically debug the UI. Here’s how to use Reveal in a jailbreak environment.

  • Jailbreak cell phone inCydia storeIn the installationReveal Loader
  • SSH Connection to a Mobile PhoneCD Library directorymkdir RHRevealLoaderFolder.
  • MacComputer installationPass Reveal toolsHere is a recommendationCracking tool download platform
  • Ma computerCopy RevealServerExecutable program into the mobile phoneRHRevealLoader folder. Open the Reveal program,Help –> Show Reveal Library in Finder –> iOS Library –> RevealServer.framework–>RevealServer. SCP copied RevealServer into the phone and renamed it tolibReveal.dylib

  • Restart the phone
  • Mobile phone intoSettings -->Reveal--> Open the APP you want to debug
  • If you want to use the Reveal tool again, you should be able to debug the UI dynamically.