
在上一篇文章中,在上一篇文章中,我們講了iOS應用重簽名技術,同時我們也把微信的越獄ipa包安裝到了我們的手機上了,那么,這一章,我們就來討論討論代碼注入相關的話題吧。
接下來本文會從以下幾點進行闡述:
- Framework注入
- yololib手動注入
- Dylib注入
- MethodSwizzle
- 破壞微信注冊
準備工作: 本篇文章需要用到以下工具
越獄微信7.0.2 提取碼:hyya
MacchOView
yololib
class-dump
1.Framework注入
一般修改原始的程序,是利用代碼注入的方式,注入代碼就會選擇利用FrameWork或者Dylib等三方庫的方式注入。
什么是Framework這里就不多加敘述,我參考這個網(wǎng)站,非常詳細,看不懂你直接@我。點這里:Framework最強講解
接下來直接演示如何創(chuàng)建一個Framework,并且介紹跟咱們Hook微信有關的基礎原理。
1.1 新建項目,完成重簽名過程
根據(jù)上一篇文章,我們學會了腳本自動代碼重簽名,這也是本次要用到的內容,不理解的小伙伴可以移步看看iOS代碼重簽名相關。
首先,我們要創(chuàng)建一個項目,名稱叫做FrameworkInject,并且使用腳本對項目重簽名。(這里我們還是簽上一篇文章中介紹的微信項目)。
由于上一篇文章我們簡單的介紹了下項目的目錄結構,所以 這里我們使用Xcode快捷鍵 command+b 編譯一下,可以看到項目的目錄下出現(xiàn)了Temp文件夾,依次打開 Temp->Payload->WeChat右鍵顯示包內容,找到 WeChat 的可執(zhí)行文件,使用 MachOView 將WeChat打開,下一步我們簡單分析下 MachO這個可執(zhí)行文件。
1.2 MachO文件簡單分析,注意庫加載邏輯
1.將 WeChat 文件使用 MachOView 打開會得到如下的界面:


注意:微信用的第三方庫有的是在app包里面,有的是在iOS系統(tǒng)里,當程序加載的時候回去 LC_LOAD_DYLIB 這里面查找?guī)斓穆窂?,只有在這里的庫才會被加載,否則不加載。
1.3 注入自己的三方庫。
1.在當前項目中新建framework,打開項目,選擇Targets,選擇下面的添加按鈕。


注意:
在我們用XCode新建CZHook的時候,其實XCode幫我們做了一部操作:創(chuàng)建CZHook時候,同時將CZHook鏈接到我們的項目中(這是后期的XCode新增功能,早年的XCode這一步是需要我們自己做的)
如圖:


但是CZHook在ipa文件中,并不代表著CZHook就可以被我們的可執(zhí)行文件所執(zhí)行,因為CZHook并沒有沒導報入我們的可執(zhí)行文件,只有在這個可行執(zhí)行文件的某一個地方做好標記,告知可執(zhí)行文件,在適當?shù)臅r候需要加載外部的CZHook,才能夠正常運行。
而這個地方所說的可執(zhí)行文件就是MachO文件,我們可以利用工具MachOView來查看MachO中到底有什么內容。結果肯定是找不到的,所以,下一步我們來將CZHook文件標記到我們的 MachO文件中。
5.這里我們就需要用到終端命令行工具 yololib
將下載下來的yololib.zip解壓后得到的yololib放在??目錄/usr?/local?/bin?下,這樣我們在終端中就可以使用yololib命令了

以下命令就是將FYHook注入WeChat的命令
// yololib 「MachO路徑」 「CZHook相對MachO的路徑」
yololib WeChat Frameworks/CZHook.framework/CZHook
1.4 使用yololib手動注入。
這里有個細節(jié)需要注意下,我們使用腳本重新簽名,每次重新簽名的文件都在目錄Temp下,所以我們修改Temp下的文件是沒有用的。
這里我們要修改APP目錄下的ipa才可以。
1.解壓APP目錄下的*.ipa包,依次進入Payload->WeChat顯示包內容。

3.推出到Payload文件目錄下執(zhí)行 zip -ry WeChat.ipa Payload,完成之后將 ipa替換到 APP文件目錄下。
4.在 CZHook 中添加文件繼承與NSObject,并添加以下代碼
+(void)load{
NSLog(@"來了 。。。。 ");
}
如圖:
5.運行項目,當項目輸出 來了 。。。。這句話的時候說明我們手動注入代碼成功。
2.Dylib注入
接下來我們來講一下Dylib注入,Dylib也是常見的注入方法,只是Dylib注入不屬于iOS注入的方法,而是屬于Mac的注入方法,以下是步驟:
1.首先,我們要創(chuàng)建一個項目,名稱叫做DylibInject,并且使用腳本對項目重簽名。
2.創(chuàng)建新的TARGET,這里選擇 macOS 的 Library.
注意:
1. Dylib是給 macOS 使用的庫,所以默認不支持iOS使用,我們需要手動設置兩個地方。
2. 選擇 CZHook -> Build Settings,將Base SDK 改成 iOS。此時我們會發(fā)現(xiàn)這個庫支持運行在手機上了。
3. 修改CZHook的簽名信息,選擇 CZHook -> Build Settings,選擇 ALL 搜索 sign,會發(fā)現(xiàn) Code Signing Identity是使用的Mac develop證書簽名的,我們要把它改成iOS develop證書簽名。如下圖:


3.此時,先選擇CZHook,command+b Build一下,把CZHook庫編譯出來,然后選擇我們的項目工程,再次Build一次,然后查看Products下的*.app,選擇show in finder 查看信息。
4.此時查看 libCZHook.dylib 庫是有的,但是我們要去看安裝包中的Framework中有沒有l(wèi)ibCZHook.dylib這個庫。目前是沒有這個庫的。
5.原因是我們需要自己把libCZHook.dylib這個庫添加到我們的可執(zhí)行文件中,操作方法如下:
- 在TARGETS中選擇我們的工程項目
-
選中Build Phases,點擊加號選擇New Copy Files Phase。添加我們的libCZHook.dylib庫
四 Dylib注入4.png
6.此時 Build一下,此時會發(fā)現(xiàn)libCZHook.dylib出現(xiàn)在我們項目可執(zhí)行文件中了,但是沒有在Frameworks這個文件夾中。
7.然后我們去腳本中注入libCZHook.dylib這個庫。在腳本最后添加
yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/libCZHook.dylib"
8.此時,我們command+r 運行一下,我運行成功,沒有問題。
注意:
- 假如這里報錯,在 dyld 加載的時候報錯,我們要查看報錯的信息,看看是否加載了,或者沒有加載成功,這里可能會遇到這個庫被加載了,但是沒有加載成功(可能是簽名信息有誤,沒有成功)。
- 原因是我們使用腳本對Frameworks中的庫多簽名了一次,這里就沒有必要使用Xcode再次幫我們拷貝一份新的庫了,所以這里我們需要自己手動把這個包放到Framework中去,然后根據(jù)上一篇文章的方法把ipa重新打包一下。
參考代碼02DylibInject
3.MethodSwizzle 簡介
在前面的案例中,我們不停的注入代碼,添加自己的代碼,添加自己的邏輯,此時我們會用到Objective-C中的MethodSwizzle,這里我們簡單介紹一下。
利用OC的Runtime特性,動態(tài)改變SEL(方法編號)和IMP(方法實現(xiàn))的對應關系,達到OC方法調用流程改變的目的。主要用于OC方法。
在OC中,SEL 和 IMP 之間的關系,就好像一本書的“目錄”。
SEL 是方法編號,就像“標題”一樣。IMP是方法實現(xiàn)的真實地址,就像“頁碼”一樣。他們是一一對應的關系。
如圖:

Runtime提供了交換兩個SEL和IMP對應關系的函數(shù).
OBJC_EXPORT void
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
通過這個函數(shù)交換兩個SEL和IMP對應關系的技術,我們稱之為Method Swizzle(方法欺騙)

3.1 Objecive-C 消息案例(簡單解析)
在OC中,函數(shù)調用我們可以理解成消息轉發(fā),就是給某一個對象發(fā)送一個消息,如下圖:
在簡單了解一下后,我們來擼一個真實的案例如下:
1.使用NSUrl發(fā)送請求。這個請求是沒有問題的。
NSURL *url2 = [NSURL URLWithString:@"www.baidu.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url2];
NSLog(@"%@",request);
2.模擬NSUrl有中文的情況。這時url不轉碼的話會為空,此時報錯。
NSURL *url2 = [NSURL URLWithString:@"www.baidu.com/中文"];
NSURLRequest *request = [NSURLRequest requestWithURL:url2];
NSLog(@"%@",request);
3.此時我們怎么處理呢? 因為使用 URLWithString:的地方比較多,找起來很不好處理,這時我們可以用消息轉發(fā)的方式,我們自己給他添加轉碼的邏輯。hook 怎個項目中URLWithString的方法。
4.使用分類處理。
分類代碼如圖:
// 代碼如下
+(void)load{
// 獲取方法
Method urlWithStr = class_getClassMethod(self, @selector(URLWithString:));
Method czUrlWithStr = class_getClassMethod(self, @selector(CZUrlWithString:));
// 交換方法
method_exchangeImplementations(urlWithStr, czUrlWithStr);
}
+(instancetype)CZUrlWithString:(NSString *)str{
// 調用系統(tǒng)原來的方法
NSURL *url = [NSURL CZUrlWithString:str];
// 判斷url是否為空,轉碼
if (url == nil) {
str = [str stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
}
url = [NSURL CZUrlWithString:str];
return url;
}
4.ViewDebug、LLDB、class-dump分析微信登錄頁面
4.1.class-dump介紹和簡單使用
將class-dump拷貝到Mac的目錄/usr?/local?/bin?下,這樣我們在終端中就可以使用class-dump命令了。class-dump,是可以把Objective-C運行時的聲明的信息導出來的工具。其實就是可以導出.h文件。用class-dump可以把未經(jīng)加密的app的頭文件導出來。

1.在左面創(chuàng)建空的文件夾 WeChatHeadrs。并進入到當前的文件夾。
2.進入到 WeChat的可執(zhí)行文件夾中,運行命令將WeChat所有的頭文件導出來。
// class-dump -H 「app的MachO文件」 -o 「輸入的目錄」
class-dump -H WeChat -o /Users/XX/desktop/WeChatHeaders

4.2.通過ViewDebug、LLDB來找到微信的登錄按鈕,并Hook其方法。
4.2.1 嘗試Hook微信的注冊方法。
任務
1.先將微信項目跑起來,通過ViewDebug找到微信的注冊按鈕。
2.向微信的注冊按鈕事件中添加一句話。
操作如下:

2.選中注冊按鈕,這時會發(fā)現(xiàn)注冊按鈕的Clase Name為FixTitleColorButton這個是微信自定義的按鈕。
3.按鈕的Target保存了按鈕添加在哪個視圖,通過底下的po命令輸出,我們會發(fā)現(xiàn)按鈕被添加到了控制器WCAccountLoginControlLogic上,分析Action地址發(fā)現(xiàn)按鈕執(zhí)行的函數(shù)叫做onFirstViewRegester。
4.這時就要用到我們通過class-dump獲取到的頭文件了,搜索 @interface WCAccountLoginControlLogic文件。找到 onFirstViewRegester。如圖:

5.接下來,我們就來Hook onFirstViewRegester這個方法了。
Hook代碼:
+(void)load{
NSLog(@"來了 。。。。 ");
//錯誤寫法,注意 "WCAccountLoginControlLogic" 前面不能加@
// Method oldMethod = class_getInstanceMethod(objc_getClass(@"WCAccountLoginControlLogic"), @selector(onFirstViewRegester));
Method oldMethod = class_getInstanceMethod(objc_getClass("WCAccountLoginControlLogic"), @selector(onFirstViewRegester));
Method newMethod = class_getInstanceMethod(self, @selector(CZHookOnFirstViewRegester));
method_exchangeImplementations(oldMethod, newMethod);
}
-(void)CZHookOnFirstViewRegester{
NSLog(@"檢測到異常,不能注冊!!!!!");
}
參考代碼04FrameworkInject(動態(tài)調試Hook微信注冊按鈕)
4.2.2 嘗試Hook微信的登錄賬號、密碼。
操作步驟類似于上面的4.2.1,結果如下圖:
接下來我們來分析下登錄注冊文本框事件。
由以上內容的出,我們現(xiàn)在所在的類中,具體的文本框代碼不清楚,這時就需要我們去分析頭文件了。
首先,我們來找一下WCAccountMainLoginViewController這個類的頭文件,看看有什么收獲吧。
1.如圖,我們找到了類似賬號、密碼登錄的控件,但是此時并不敢確定,還是要用ViewDug去調試一下,發(fā)現(xiàn)文本框的類型并不是WCAccountTextFieldItem類型

2.我們接著去找WCAccountTextFieldItem的頭問津啊查看情況,會發(fā)現(xiàn)WCAccountTextFieldItem里面的信息更少了,但是我們發(fā)現(xiàn)這個類繼承于WCBaseTextFieldItem,我們去看看吧。

3.在 WCBaseTextFieldItem中我們發(fā)現(xiàn)了WCUITextField類型的變量m_textField,此時和我們要找的控件類型一致了,然而,還是讓我們去驗證一下吧 。。

4.我們通過KVC的方式從WCAccountMainLoginViewController獲取WCAccountTextFieldItem。
po [(WCAccountMainLoginViewController *)0x13606fa00 valueForKey:@"_textFieldUserNameItem"]
5.從WCAccountTextFieldItem中獲取WCUITextField
po [(WCAccountTextFieldItem *)0x281c18d20 valueForKey:@"m_textField"]
最終結果如下:

部分代碼:
#import "CZHookInject.h"
#import <objc/runtime.h>
@implementation CZHookInject
+(void)load{
Method oldMyNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
Method newMyNext = class_getInstanceMethod(self, @selector(hook_myNext));
method_exchangeImplementations(oldMyNext, newMyNext);
}
-(void)hook_myNext{
NSString *userNmaeStr = [[[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"] performSelector:@selector(text)];
NSLog(@"輸入的用戶名是:%@",userNmaeStr);
}
7.當獲取完用戶的密碼的時候,我們需要代碼走之前的邏輯,這時我們需要調用原來的方法,但是怎么調用呢?是調用hook_myNext方法嗎?會發(fā)生什么呢?
參考代碼05FrameworkInject(動態(tài)調試Hook微信賬號密碼)
4.2.3 解決崩潰
崩潰原因介紹:當我們調用hook_myNext的方式時,WCAccountMainLoginViewController的方法列表中沒有hook_myNext方法的實現(xiàn),所以崩潰了,這里不做多解釋。
解決崩潰的方法:
- 通過動態(tài)添加方法解決
- 通過方法替換完成交換
- 通過getIMP和setIMP方式解決
添加方法的方式解決
代碼如下:
/* 添加方法
+(void)load{
Method oldMyNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
// 添加新方法
BOOL didAddMethod = class_addMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(hook_myNext), hook_myNext, @"v@:");
// 獲取新添加的方法
Method newMethod = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(hook_myNext));
method_exchangeImplementations(oldMyNext, newMethod);
}
void hook_myNext(id self,SEL _cmd) {
NSString *userNmaeStr = [[[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"] performSelector:@selector(text)];
NSLog(@"輸入的用戶名是:%@",userNmaeStr);
[self performSelector:hook_myNext];
}
替換方法
1.定義一個函數(shù)指針,保存之前的函數(shù)
2.獲取之前的函數(shù)
3.替換之前的函數(shù)
缺點:
如果你的交換沒有onNext方法,使用 replaceMethod 會默認幫你添加一個空方法
代碼:
IMP (*old_onNext)(id self,SEL _cmd);
+(void)load{
// 1,拿到原始的imp
old_onNext = method_getImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)));
// 替換imp
class_replaceMethod(objc_getClass(@"WCAccountMainLoginViewController"), @selector(onNext), my_next, @"v@:");
}
void my_next(id self,SEL _cmd){
NSString *userNameStr = [[[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"] performSelector:@selector(text)];
NSLog(@"輸入的用戶名是:%@",userNameStr);
// 調用之前的函數(shù)
old_onNext(self,_cmd);
}
通過getIMP和setIMP方式解決
setIMP 和 getIMP 方法
/*
1.拿到舊的方法的IMP
2.保存舊的IMP
3.設置IMP
*/
IMP (*old_onNext)(id self,SEL _cmd);
+(void)load{
// 1.拿到原始的method,以下兩句代碼效果一樣,下面的不會報警告
// Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), sel_registerName("onNext"));
// 2.保存舊的IMP
old_onNext = method_getImplementation(onNext);
// 3.設置IMP
method_setImplementation(onNext, (IMP)my_next);
}
void my_next(id self,SEL _cmd){
NSString *userNameStr = [[[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"] performSelector:@selector(text)];
NSLog(@"輸入的用戶名是:%@",userNameStr);
// 調用之前的函數(shù)
old_onNext(self,_cmd);
}
參考代碼06FrameworkInject(解決hook的崩潰)
警告!
非越獄狀態(tài),玩逆向微信不要真的登錄,有被警告甚至封號風險
參考文章:
作者:一縷清風揚萬里
原文地址:http://www.itdecent.cn/p/31232eef35c5
