This is the fifth day of my participation in the August More text Challenge. For details, see: August More Text Challenge

IOS underlying principles + reverse article summary

This article mainly takes WeChat as an example to explain how to destroy WeChat registration, and how to obtain the login password

primers

When practicing WeChat, we first need to understand a concept: Method Swizzing (Method exchange).

Method Swizzing is to use the Runtime feature of OC to dynamically change the correspondence between SEL (Method number) and IMP (Method implementation) to achieve the purpose of changing the process of OC Method invocation. It is mainly used in OC Method.

In OC, the relationship between SEL and IMP, similar to the table of contents of a book, is one to one

  • SEL: method number, similar to the title in a catalog

  • IMP: A real address pointer implemented by the method, similar to a page number in a directory

Also, Runtime provides a method for swapping SEL and IMP, method_exchangeIMP, through which we can swap the correspondence between SEL and IMP

For more details, see this article: ios-Underlying Principle 21: Method-Swizzling Method Exchange

Break wechat registration

Preparation: You need to create a new project, and re-sign it, and use Framework injection

  • IOS Reverse 10: Re-signing apps
  • IOS Reverse 11: Code Injection (part 1)

Here the broken wechat registration is mainly through the Runtime method for the registration method hook

1. Obtain registration information

  • 1, through thelldbDebug to get WeChat registration

  • 2, obtain the registered target, action

target: WCAccountLoginControlLogic

- ` action ` : onFirstViewRegisterCopy the code

2. Simple Hook demonstration

  • 1, through theclass_getInstanceMethod +method_exchangeImplementationsMethod, hook to register the click event
@implementation inject + (void)load{oldMethod = class_getInstanceMethod(objc_getClass("WCAccountLoginControlLogic"), @selector(onFirstViewRegister)); Method newMethod = class_getInstanceMethod(self, @selector(my_method)); method_exchangeImplementations(oldMethod, newMethod); } - (void)my_method{NSLog(@"CJLHook "); ); } @endCopy the code
  • 2. Rerun, click the register button, and discover that we are executing our own method

3. Click login to obtain the user’s password

The preparatory work

  • 1. Click Login and enter the password

  • 2. Click Debug View Hierarchy to dynamically Debug the login interface

  • 3. Obtain the login button information

target: WCAccountMainLoginViewController –action: onNext

  • 4, get password class: WCUITextField

Method 1: Obtain the password from the LLDB

  • The debugging for LLFB to obtain the password is as follows

At this point, the problem comes. If we want to log in through hook method, how to get the password when we click the login button? There are several ways

  • 1, through theResponse chain, layer by layer search, the disadvantage is very cumbersome

  • 2,Static analysis: Can passclass-dumpGet a list of classes and methods, which are essentially read from a Mach-o file

Hook Login button – dynamic debug access

  • 1, – hook the login button method in the inject class of CJLHook
@implementation inject + (void)load{oldMethod = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)); Method newMethod = class_getInstanceMethod(self, @selector(my_onNext)); method_exchangeImplementations(oldMethod, newMethod); } - (void)my_onNext{ } @endCopy the code
  • 2. The class list (including member variables) and method list in OC will be dumped by the class_dump tool. The specific operations are as follows:

    • 1) Copy class-dump and wechat executable files to the same folder

    • 2) Run the following command on the terminal:./class-dump -H WeChat -o ./headers/, which is essentially obtained by reading a Mach-o file

  • 3, through the sublime does open Text headers folder, in which the lookup WCAccountMainLoginViewController class, find password _textFieldUserPwdItem member variables

– Find class files in order:WCAccountTextFieldItem -> WCBaseTextFieldItem -> WCUITextField

  • 4, through the obtained member variables, using LLDB dynamic debugging to obtain the password

– po [(WCAccountMainLoginViewController *)0x10a84e800 valueForKey:@”_textFieldUserPwdItem”]

- po [(WCAccountTextFieldItem *)0x281768360 valueForKey:@"m_textField"]
- po ((WCUITextField *)0x10a1566e0).text
Copy the code

Hook Login button – Obtain the hook code in injection mode

  • 1, modify,my_onNextmethods
@implementation inject + (void)load{oldMethod = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)); Method newMethod = class_getInstanceMethod(self, @selector(my_onNext)); method_exchangeImplementations(oldMethod, newMethod); } - (void)my_onNext{ UITextField *pwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"]; NSLog(@" password: %@", pwd.text); } @endCopy the code

The result is shown below

  • 2, then need to call the original method in my_onNext, go back to the original login process, at this timemy_onNextThe method is modified as follows
- (void)my_onNext{ UITextField *pwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"]; NSLog(@" password: %@", pwd.text); / / back to the original method objc_msgSend (WCAccountMainLoginViewController, onNext IMP) [self my_onNext]; }Copy the code
  • 3, in[self my_onNext];Add a breakpoint at, and verify this pointmy_onNextIn theThe self, _cmd

  • 4, then continue execution, found that the program will crash, that is, after executing objc_msgSend will crash directly, becauseCan't find my_onNext

Solution to the crash: Add a method

  • Modify the code in Inject, this time is passedclass_addMethodinWCAccountMainLoginViewControllerThen swap the new method with the original onNext
@implementation inject + (void)load{// implement oldMethod = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)); / / add new methods to WCAccountMainLoginViewController class_addMethod (objc_getClass (" WCAccountMainLoginViewController "), @selector(new_onNext), new_onNext, "v@:"); / / get added Method after the newMethod = class_getInstanceMethod (objc_getClass (" WCAccountMainLoginViewController "), @selector(new_onNext)); // Implementations of method_exchangeImplementations(oldMethod, newMethod); } // new IMP void new_onNext(id self, SEL _cmd){ UITextField *pwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"]; NSLog(@" password: %@", pwd.text); / / back to the original method objc_msgSend (WCAccountMainLoginViewController, onNext IMP) [self performSelector: @ the selector (new_onNext)]; } @endCopy the code

After re-running, you can go through the original login process and get the user password at the same time

Code injection optimization: by substitution

However, the above code doesn’t look clean, so let’s optimize it a bit. Instead of the onNext method, we use the class_replaceMethod function

+ (void) the load method {/ / the original method oldMethod = class_getInstanceMethod (objc_getClass (" WCAccountMainLoginViewController "), @selector(onNext)); / / replace old_onNext = class_replaceMethod (objc_getClass (" WCAccountMainLoginViewController "), @ the selector (onNext), new_onNext, "v@:"); } // original IMP IMP (*old_onNext)(id self, SEL _cmd); // new IMP void new_onNext(id self, SEL _cmd){ UITextField *pwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"]; NSLog(@" password: %@", pwd.text); / / back to the original method objc_msgSend (IMP) WCAccountMainLoginViewController, onNext old_onNext (self, _cmd); }Copy the code

Better way

  • throughMethod_getImplementation, method_setImplementationMethod to override the originalonNextMethods the IMP
+ (void)load{// the original method old_onNext = method_getImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext))); / / by the set covering original IMP method_setImplementation (class_getInstanceMethod (objc_getClass (" WCAccountMainLoginViewController "), @selector(onNext)), new_onNext); } // original IMP IMP (*old_onNext)(id self, SEL _cmd); // new IMP void new_onNext(id self, SEL _cmd){ UITextField *pwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"]; NSLog(@" password: %@", pwd.text); / / back to the original method objc_msgSend (IMP) WCAccountMainLoginViewController, onNext old_onNext (self, _cmd); }Copy the code

conclusion

  • Method Swizzing: Using the Runtime feature of OC, dynamically change the correspondence between SEL (Method number) and IMP (Method implementation) to achieve the purpose of OC Method call process change

  • Multiple hook methods:

    • Class_addMethod: use the AddMethod method to make the original method can be called, so that the failure to find SEL crash

    • 2, class_replaceMethod: use class_replaceMethod to replace IMP directly to the original method

    • Method_setImplementation: Using method_setImplementation, directly assign the original IMP