preface

Last time we finished the macOS version of wechat little Assistant, now we finally have (Xian) time (Huang) to say iOS reverse. This paper mainly realizes the automatic adding of friends on wechat (that is, the automatic verification of new friend application), so as to be familiar with the process of iOS reverse analysis. The summary may be a little rough, welcome to discuss if there is something you don’t understand.

Github address: iOS version oF wechat small assistant (anti-withdrawal, modify wechat sports, group management, friend request management)

tool

For details on how to use the following tools, see the iOS App Reverse Engineering Edition 2, Part 2 Tools section.

The macbook software

  • theos

    Make Tweak tools

  • hopper disassembler

    Used for static analysis

  • usbmuxd

    Port forwarding allows us to connect the phone through USB for SSH, LLDB debugging, etc. Mainly use files in the python-client directory

  • class-dump

A tool that dumps class information about a target object.

  • lldb

Calibrate the artifact. Use it. The default built-in in the/Applications/Xcode. App/Contents/Developer/usr/bin/LLDB.

Jailbreaking iPhone Apps

The following software can be downloaded in Cydia (except debugServer)

  • OpenSSH

Remote SSH service can be implemented on the jailbroken mobile phone. Through SSH, the terminal can be connected to the iPhone for control.

** Most iOS tools require ** in the SSH environmentCopy the code
  • Cycript

Scripting language to hook running processes and inject code in real time.

  • ondeviceconsole

Log used to view mobile phones in Terminal

  • debugserver

LLDB debugging tool for connecting mobile phones. App debugging on the phone with Xcode is generated in the iPhone directory /Developer /usr/bin/.

The debugServer needs to be processed first. Unable to debug other apps due to lack of task_for_PID permission. Copy the DEBUgServer over SSH

SCP root @ iOSIP: / Developer/usr/bin / / debugserver / / iOSIP debugserver ~ for the IP address of the phoneCopy the code

Download LDID and ENT and proceed

ldid - Sent.xml debugserver
Copy the code

Copy the file to the mobile phone using SSH.

Analysis of the

To implement automatic validation of a friend request, you need to get a method for getting a friend request and a method for passing a friend request. Hook a method to get a friend request. When a friend request is received, the method to add a friend is executed. The main logic is in the “New Friends” screen.

A method to locate friend requests

Analysis of the UI

And we know, according to the MVC model, the general method implementation is in the ViewController, so the way to get a friend request is to get the controller of the current interface. This can be achieved through UI analysis.

Let’s go to the new friends screen.

Use USBMUxD for port forwarding (if the mobile phone is not stuck, you can skip this step and directly use SSH for wifi remote connection)

python tcprelay.py -t 22:2222
Copy the code

Then use SSH to connect to the mobile phone

SSH root@localhost -p 2222 // SSH [email protected] If the connection is wifi, check the wifi address of the current mobile phone.Copy the code

View the wechat process information

 ps -e |grep WeChat
Copy the code

Cycript injection

Cycript -p WeChat // or the current WeChat process number, as shown belowCopy the code

After starting Cycript, use the following command to view the current UI layout

UIApp.keyWindow.recursiveDescription().toString()
Copy the code

Because we know that the current view has a tableView, we find the object of the tableView. You can see from the figure above that the address of this object is 0x18C4be00. Use nextResponder() to look up the current ViewController based on the responder.

SayHelloViewController

The Log analysis

Use class-dump to dump wechat class information.

class-dump -S -s-h demo.app -o ~/Document/headers/ // dump the headers file of wechat app to the ~/Document/headers/ directoryCopy the code

Then use theos’s Logify tool, which injects NSLog to print the method’s input and output parameters. Hook all methods of a class, add log to them, and export xM files.

logify.pl  ~/Document/headers/SayHelloViewController.h > ~/Desktop/Tweak.xm
Copy the code

** Note: ** Generally this Tweak. Xm still cannot be executed and needs to be modified:

  • Remove the.cxx_destruct method
  • Change HBLogDebug to NSLog
  • Get rid of all the delegates
  • Change all parameter object types to ID
  • Remove all weak

Then use theOS configuration files (specifically check iOS reverse – wechat helloWorld), and then make package install to the phone.

Restart wechat to enter the new friends screen.

Print the phone’s log in SSH using onDeviceconsole.

Then use another wechat to add their friends. Method to trigger a friend request. You can see the following log

-[SayHelloViewController OnSayHelloDataChange] is called when a friend is added.

A dynamic analysis

Since we already know that OnSayHelloDataChange will be called when there is a friend request, we can handle it in the current method. However, there is a drawback that when there is a friend request, wechat will not call this method when there is no new friend interface. So instead of working in a lower level class (let’s say a message management class), how do we find the message management class? As a general rule, there must be a method in the message management class that triggers OnSayHelloDataChange. In this case, use the LLDB + Hopper artifact to find the corresponding message management class and its handling method.

LLDB for mobile terminal debugging, hopper for static analysis, analysisOnSayHelloDataChangeMethod information to find a breakthrough.

Use Hopper to open wechat binaries. Search the SayHelloViewController OnSayHelloDataChange method. You can see the offset address 0x14a4824 of the current method in wechat.

Start the DEBUgServer for LLDB debugging

First, open wechat and use the USBMUxD conversion port

python tcprelay.py -t 1234:1234
Copy the code

Then SSH the mobile phone to start the debugServer.

debugserver *:1234 -a "WeChat"
Copy the code

Use the new Terminal window to open LLDB, connect port 1234, and view the current wechat process information (generally in the first line of all processes). It will be stuck for a while.

/ / open LLDB/Applications/Xcode. The app/Contents/Developer/usr/bin/LLDB / / connection debugging (LLDB) process the connect Connect ://localhost:1234 // Print all processes (LLDB) image list -o-f
Copy the code

Find wechat process memory base address 0x000b2000 on current phone (this value is not fixed)

[SayHelloViewController OnSayHelloDataChange] = sayHelloDatachange = sayHelloDatachange namely

Memory address = process memory base address + method offset addressCopy the code

Use BR to interrupt point view

 br s -a "0x000b2000 + 0x14a4824"
Copy the code

The breakpoint will be triggered when you type C to continue and add friends using another wechat account.

Use BT to view the call stack information, that is, which methods call the current method, and find the upstream of the method. (Async calls cannot be viewed)

The first represents the current method, and you can see that the process called a total of three methods before calling this method. The offsets of these three methods in wechat are calculated respectively.

Check these three addresses in Hopper (press g to enter the address) and find the corresponding method as

[SayHelloViewController OnSayHelloDataChange] [SayHelloDataLogic onFriendAssistAddMsg:] [FriendAsistSessionMgr OnAddMsgForSpecialSession:MsgList:] [CMessageMgr MainThreadNotifyToExt:]Copy the code

You can guess from the above method names

[FriendAsistSessionMgr OnAddMsgForSpecialSession:MsgList:]
Copy the code

MsgList: is a function used to receive messages adding friends, where the following argument may be an array of messages, to prove that we can use this method to check the breakpoint. Use the register read command to read the register address and print the object using Po.

We see that the R3 register is really an array, and we also get the message object as CMessageWrap which proves that we’re right.

Ps: To explain why WE want to look at R3, because in ARMV7, a method call, the general register is stored like this: the first four parameters are placed in r0~ R3, and the rest are stored on the stack. To view the stack, use x/10 $sp to view the addresses of objects in the first 10 stacks.

However, the FriendAsistSessionMgr class might do some initialization in the new friends interface and put it in the SayHelloViewController, and we want to hook the message array object no matter which controller it is in. So we look up, [CMessageMgr MainThreadNotifyToExt:], but it doesn’t have the information we need. Based on the class name, CMessageMgr is used to manage messages. It is possible that a fetch of the message array was performed asynchronously.

Therefore, repeat the above steps and use Logify to perform Log analysis on CMessageMgr. Finally locked the CMessageMgr MessageReturn: MessageInfo: Event:

Locate the method through friend request

A dynamic analysis

Now that you’ve found a way to receive a friend request, it’s time to find a way to pass it. We know that the friend request method is triggered when you click accept on the new friend screen. (Log analysis is possible, but there is another faster method.)

Cycript positioning

First print out all the UI levels via Cycript. Find the object that receives the button (a trick, we know that the current button is under a cell, so locate this).

#0x186922f0.hidden = 1
Copy the code

Finding the button missing proves we were right. You need to find the button click event.

UIButton inherits UIControl. In Cycript, you can view allTargets and events of UIControl using allTargets and allControlEvents. Reuse actionsForTarget: forControlEvent: to find a way to trigger.

See that the triggered method is [ContactsItemView onRightBtnAction]

Static analysis

Now that we have the method name, what do we think of its implementation? Then came hopper. Use Hopper to open wechat binary files and convert assembly and pseudocode. ~~ because the assembly read more obscure, so or pseudo-code conversion, so the efficiency is faster. ~~ Click the button to convert

You get pseudocode

r10 = self;
r5 = r10 + *0x33befe8;
r4 = objc_loadWeakRetained(r5);
r8 = @selector(onContactsItemViewRightButtonClick:);
r11 = [r4 respondsToSelector:r8];
Copy the code

Can be concluded that the r11 = [r5 onContactsItemViewRightButtonClick: BTN], agent and r5 our judgement for the self, This we also can be in before the class – the head of the dump file search onContactsItemViewRightButtonClick inside, will find in the ContactsItemViewDelegate. Namely [ContactsItemView onRightBtnAction] internal call [the self. The delegate onContactsItemViewRightButtonClick:]. And the ContactsItemView’s delegate is SayHelloViewController.

With a hopper onContactsItemViewRightButtonClick positioning.

I don’t know where to start when I see this. At this point it’s just a matter of conjecture. In the figure above, two if judgments are made, the first is

r10 = @selector(class);
r2 = loc_1c099bc(@class(CPushContact), r10);
r1 = @selector(isKindOfClass:);
r5 = loc_1c099bc(r4, r1, r2);
loc_1c099d4(r4);
if((r5 & 0xff) ! = 0x0) {Copy the code

If ([r4 isKindOfClass: [CPushContact class]]); And what is R4? It must be CPushContact, otherwise the following code will not be executed. Based on dynamic analysis, we can look at the LLDB interrupt point and look at the object type of the R3 register and see that this object is CPushContact. So r4 is the CPushContact object, which is literally the contact object.

If ((loc_1c099BC (r6, @selector(m_bCut user)) & 0xff)! = 0x0) && ((loc_1C099BC (r6, @selector(isMMContact)) &0xff) == 0x0))), see MMUIAlertView. If it is a suspicious user or the current friend is already a friend of your own, then it will pop up. And the other part is verifyContactWithOpCode: opcode:, speculated that the department is divided into the method of add buddy. You can do Log analysis or LLDB break points, and you’ll see that both enter this method. The arguments are CPushContact and 3. Then continue to analyze verifyContactWithOpCode: opcode: method. The main parts are shown below.

Through analysis, we can see that the CContactVerifyLogic object is constructed by confirming the friend request. Construct a CVerifyContactWrap object, and set the related properties, such as m_nsUsrName m_uiScene m_nsTicket. Then added to the array of object by CContactVerifyLogic startWithVerifyContactWrap: opCode: parentView: fromChatRoom: method to send. The code is as follows:

CContactVerifyLogic *verifyLogic = [[CContactVerifyLogic alloc] init];
CVerifyContactWrap *wrap = [[CVerifyContactWrap alloc] init];
[wrap setM_nsUsrName:contact.m_nsEncodeUserName];
[wrap setM_uiScene:contact.m_uiFriendScene];
[wrap setM_nsTicket:contact.m_nsTicket];
[wrap setM_nsChatRoomUserName:contact.m_nsChatRoomUserName];
wrap.m_oVerifyContact = contact;

AutoSetRemarkMgr *mgr = [[MMServiceCenter defaultCenter] getService:[AutoSetRemarkMgr class]];
id attr = [mgr GetStrangerAttribute:contact AttributeName:1001];

if([attr boolValue]) {
    [wrap setM_uiWCFlag:(wrap.m_uiWCFlag | 1)];
}
[verifyLogic startWithVerifyContactWrap:[NSArray arrayWithObject:wrap] opCode:3 parentView:[UIView new] fromChatRoom:NO];
Copy the code

Now we have a method to get a friend request and a method to add a friend. Another problem is that the object to add a friend is CPushContact, while the object to get the friend request is CMessageWrap. The conversion is required, and the conversion method is in the SayHelloViewController, which can be obtained by repeating the analysis above.


Write a Tweak

Through the above analysis, combine the code

%hook CMessageMgr
- (void)MessageReturn:(unsigned int)arg1 MessageInfo:(NSDictionary *)info Event:(unsigned int)arg3 {
    %orig;
    if(arg1 == 332) {NSString *keyStr = [info objectForKey:@"5"];
        if ([keyStr isEqualToString:@"fmessage"]) {
            NSArray *wrapArray = [info objectForKey:@"27"];
            [self addAutoVerifyWithArray:wrapArray];
        }
    }
}

%new
- (void)addAutoVerifyWithArray:(NSArray *)ary {
    [ary enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            CPushContact *contact = [%c(SayHelloDataLogic) getContactFrom:obj];
            if(! [contact isMyContact] && [contact.m_nsDes isEqualToString:autoVerifyKeyword]) { CContactVerifyLogic *verifyLogic = [[%c(CContactVerifyLogic) alloc] init]; CVerifyContactWrap *wrap = [[%c(CVerifyContactWrap) alloc] init]; [wrapsetM_nsUsrName:contact.m_nsEncodeUserName];
                [wrap setM_uiScene:contact.m_uiFriendScene];
                [wrap setM_nsTicket:contact.m_nsTicket];
                [wrap setM_nsChatRoomUserName:contact.m_nsChatRoomUserName];
                wrap.m_oVerifyContact = contact;

                AutoSetRemarkMgr *mgr = [[%c(MMServiceCenter) defaultCenter] getService:%c(AutoSetRemarkMgr)];
                id attr = [mgr GetStrangerAttribute:contact AttributeName:1001];

                if([attr boolValue]) {
                    [wrap setM_uiWCFlag:(wrap.m_uiWCFlag | 1)]; } [verifyLogic startWithVerifyContactWrap:[NSArray arrayWithObject:wrap] opCode:3 parentView:[UIView new] fromChatRoom:NO]; }}]; }Copy the code

conclusion

This paper analyzes the content of iOS application reverse engineering version 2 for myself. Because the whole reverse process is a bit cumbersome, sometimes it is not only successful to analyze once, but also needs to repeatedly conduct UI analysis, Log analysis and LLDB analysis.

reference

IOS App Reverse Engineering 2nd edition Mobile App hacking and Reverse Cracking techniques -iOS chapter