SoFunction
Updated on 2025-04-12

Detailed introduction to IOS App codeless hacking method hook

iOS App codeless hacking method hook

Continue the Objective-C runtime research

Recently, the company's project is conducting user behavior analysis

Therefore, the App side needs to send a message to the statistics system during certain pages and interoperability.

In dozens of Controller projects, adding codes one by one is completely impossible, and it is also difficult to maintain

But what needs to be dealt with here is the Controller, and the above requirements can be achieved in the following ways

1. Utilize object inheritance in Objective-C

Inheritance is very commonly used in object-oriented development. For example, the project we are doing now will have a BaseViewController.

All newly created ViewControllers inherit BaseViewController. By adding some public methods\properties to BaseViewController, they can be called by their subclasses.

This is a major way to unify all view controller styles in our project

2. Use Category and Runtime to implement method hook

One advantage of hook solution is that it can avoid code intrusion and achieve wider universality. Through swizzling, we can combine the original method with the method we have added.

That is, there is no need to add code to the original project, and it can achieve global coverage.

Comparison of the two solutions:

Implementation by inheriting the parent class is more accurate compared to hooks, because the pages that need to be counted are inherited from the controller of this parent class, while other such as UINavigationController, the UIAlertController included in the system will not be mistakenly included in the statistics data.

As mentioned above, the hook solution is through hook UIViewController viewdidload/viewdidappear and other methods. In fact, each controller will be called, and the controller that should not appear will also appear (such as the UINavigationController and UIAlertController mentioned above). However, a better feature of the hook solution is codeless intrusion, which completes the work without modifying the project code.

Considering that the behavioral analysis statistics system may be used in other projects of the company, a hook scheme is used here. Then there will inevitably be cases where the uncountable statistics are not counted, and the analysis will be made later.

Since you use the hook solution, you need to use runtime's swizzling

First, create a new category of UIViewController

Implement swizzling code

+ (void)load{
  [super load];
  
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    // If you want to open the controller statistics, open the following line of code    __gbh_tracer_swizzleMethod([self class], @selector(viewDidAppear:), @selector(__gbh_tracer_viewDidAppear:));
  });
}

Well, when you see this, you will find that the C method is called here, but how is this C method implemented? See below

void __gbh_tracer_swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector){
  Method originalMethod = class_getInstanceMethod(class, originalSelector);
  Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
  
  BOOL didAddMethod =
  class_addMethod(class,
          originalSelector,
          method_getImplementation(swizzledMethod),
          method_getTypeEncoding(swizzledMethod));
  
  if (didAddMethod) {
    class_replaceMethod(class,
              swizzledSelector,
              method_getImplementation(originalMethod),
              method_getTypeEncoding(originalMethod));
  } else {
    method_exchangeImplementations(originalMethod, swizzledMethod);
  }
}

This is a standard swizzling writing method. Of course, there is also an open source library for swizzling on github, which is easy to use. I won't say much here

Looking back at the first piece of code, the red viewDidAppear is the method that I will be hooked soon, and __gbh_tracer_viewDidAppear is the method I need to implement

- (void)__gbh_tracer_viewDidAppear:(BOOL)animated{
  [self __gbh_tracer_viewDidAppear:animated]; // Since the method has been swapped, the method called here is actually viewDidAppear:  
 //Set the Controller that does not allow sending data  NSArray *filter = @[@"UINavigationController",@"UITabBarController"];
  NSString *className = NSStringFromClass();
  if ([filter containsObject:className]) return ; //If the controller is in the list that does not allow the sending of logs, it cannot continue to move down  
  if ([ isKindOfClass:[NSString class]] &&  > 0){ //Only those with titles meet my requirements    // Send log here  }

}

Well, I just mentioned that there are some controllers that do not send data. Here are two judgments: one is added to the blacklist, and the other is to determine whether the title attribute of the controller is empty

The above judgment can basically meet the needs of my behavioral analysis and statistics system. If you need any other judgment, you can continue to add it.

In this way, I just need to add this Category to the project, and this viewDidAppear will be hooked and you can do whatever you want..

In addition, the requirement also mentioned that an init message needs to be sent when the application is started

hook? Yes, but I prefer to use category+NSNotification because the system already has UIApplicationDidFinishLaunchingNotification

This notification can be used directly

@implementation UIApplication (GBHTracer)
+ (void)load{
  [super load];
  
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{ //It's only done once    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(__gbh_tracer_applicationDidFinishLaunching:) name:UIApplicationDidFinishLaunchingNotification object:nil];
  });
}

+ (void)__gbh_tracer_applicationDidFinishLaunching:(NSNotification *)noti{
  //Do whatever you want when the application starts!}

@end

Well... Our behavioral analysis statistics system can achieve statistical results without importing a header file without calling any method.

However, when the statistics of what operation is responding, the judges still need to call the corresponding method in the response

Thank you for reading, I hope it can help you. Thank you for your support for this site!