11-代碼注入

前言

上篇文章10-應用重簽名搀罢,我們利用CodeSign終端指令Shell腳本2種方式肪凛,成功實現(xiàn)了對微信app的重簽名太示,已經(jīng)能夠查看微信的登錄注冊頁面UI層級镀钓,接下來病毡,我們想做些自己的事情,例如注入自己的代碼中狂,修改微信注冊登錄的流程凫碌。

一、代碼注入

一般修改原始的程序胃榕,是利用代碼注入的方式盛险,注入代碼就會選擇利用Framework 或者Dylib等第三方庫的方式注入。

1.1 FrameWork注入

我們知道勋又,重簽名app后自己的殼工程的代碼就被替換掉了(替換的是MachO二級制執(zhí)行文件)苦掘,那么原有的工程代碼并不會執(zhí)行。iOS系統(tǒng)是通過dyld(iOS系統(tǒng)提供的動態(tài)鏈接器)加載MachO可執(zhí)行文件赐写,而加載的過程鸟蜡,首先就是讀取MachO的Load Commands(其中包括_PAGEZERO膜赃、_TEXT挺邀、_DATA、_LINKEDIT等)??

App的執(zhí)行過程跳座,除了執(zhí)行自己的工程代碼外端铛,還依賴一些系統(tǒng)基礎(chǔ)庫(例如UIKit,F(xiàn)oudation等)和第三方的庫(.framwork.a庫等)疲眷,而這些庫最終也是一個MachO可執(zhí)行文件,分析MachO文件會發(fā)現(xiàn)Frameworks中的庫在Load Commands中以LC_LOAD_DYLIB存在禾蚕,路徑是Frameworks下對應庫??

只要Load Command中有對應庫的LC_LOAD_DYLIBdyld就會去對應路徑加載庫狂丝。

知道了這點换淆,那么代碼注入的過程就很明確了??

  1. 確認目標App的MachO執(zhí)行文件的Load Commands中有對應的LC_LOAD_DYLIB
  2. 將自己的代碼寫入動態(tài)庫中

1.1.1 注入步驟

  1. 給自己的殼工程添加一個target ?? LGHook Framework動態(tài)庫

??注意:這里的殼工程,用的是上篇文章10-應用重簽名中的shell腳本重簽名的模式几颜。

  1. LGHook 添加LGInject類倍试,并且重寫+load方法

如果LGHook被加載進內(nèi)存,則會打印log蛋哭。

  1. run县习,查看Produces.app

動態(tài)庫LGHook已經(jīng)在目標AppFramewroks中了,接著我們查看MachO??

  1. 查看MachO文件

Load Commands中并沒有LGHookLC_LOAD_DYLIB,所以run起來躁愿,控制臺中并沒有打印日志叛本。

1.1.2 yololib手動注入

這個時候就需要使用yololib工具修改MachO文件,將LGHook真正加入到目標App的Load Commands中??

  1. 拷貝yololib和目標App可執(zhí)行文件同一目錄??(可單獨新建一個文件夾)

然后執(zhí)行命令??

./yololib 目標可執(zhí)行文件 要添加的Framework路徑名稱

例如??

./yololib WeChat Frameworks/LGHook.framework/LGHook

再查看可執(zhí)行文件Load Commands??

此時就有LGHook了彤钟。

  1. 拿取原始.ipa包来候,解壓,在Payload文件夾中找到.app逸雹,右鍵顯示包內(nèi)容吠勘,替換成上一步生成的二進制MachO文件,再回到Payload所在目錄峡眶,輸入以下指令生成.ipa
zip -ry xx.ipa Payload/

??注意:這一步一定要用原始.ipa包剧防,替換原始包里的二進制MachO文件。

  • 原始包微信8.0.2.ipa辫樱,將后綴改為.zip峭拘,解壓??
  • 右鍵顯示包內(nèi)容??
  • 替換上一步的WeChat Mach-O??
  • 返回到Payload所在目錄,輸入指令生成.ipa包??
  1. 將上一步生成的.ipa包狮暑,放入APP文件夾中??

cmd+shift+k先清空XCode項目的緩存鸡挠,再來run??

解決??

修改LGHook.framework所支持的最低版本。

再次run??

大功告成搬男!??????????????????

小結(jié)

以上步驟大致為??

  1. 新建殼工程WeChat椒功,配置重簽名腳本
  2. 通過Xcode新建LGHook.framwork(注意改支持的版本),真機運行阴汇,將庫安裝進入APP包
  3. 通過yololib冰悠,對WeChatMachO注入LGHook.framwork庫路徑。命令 ?? $./yololib MachO文件路徑 庫路徑
  4. 使用原始微信.ipa包褐奴,將第3步生成的MachO按脚,替換原始包里面的MachO,再次打包生成.ipa
  5. 殼工程的APP文件夾中敦冬,替換第4步生成的.ipa包辅搬,直接運行即可。

所有的Framwork加載都是由DYLD加載進入內(nèi)存被執(zhí)行的脖旱。注入成功的庫路徑會寫入到MachO文件的LC_LOAD_DYLIB中堪遂。

1.2 Dylib注入

除了Framwork注入外,還能用Dylib注入萌庆。原理和Framwork相同溶褪,過程和Framwork差不多。

  1. 創(chuàng)建dylib庫踊兜。這里選擇了macOS竿滨,為了是庫為動態(tài)庫??

修改Base SDKiOS??

Code Signing Identity修改為iOS Developer??

build Phases中添加Copy Files佳恬,增加libLGDylibHook.dylib??

  1. LGDylibHook.m添加Hook代碼??
+ (void)load {
    NSLog(@"LGDylibHook Success ????????????????");
}

此時run,仍然只是Frameworks有l(wèi)ibLGDylibHook.dylib庫于游,MachOLoad Commands仍然沒有LC_LOAD_DYLIB毁葱。

1.2.1 yololib自動注入

  1. 直接在appResign.sh腳本中添加yololib修改MachO腳本的指令??
#yololib修改MachO文件
#./yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/LGHook.framework/LGHook"
./yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/libLGDylibHook.dylib"

yololib庫放在工程目錄中??

  1. 觀察libLGDylibHook.dylib是否在Frameworks中??

MachOLoad Commands??

  1. 運行

??注意:需要先編譯libLGDylibHook.dylib再編譯殼工程

小結(jié)

注入步驟

  1. 通過Xcode新建dylib庫(注意:dylib屬于MacOS贰剥,所以需要修改屬性
    • Base SDKiOS
    • Code Signing Identity修改為iOS Developer
  2. 添加Target依賴倾剿,讓Xcode將自定義Dylib文件打包進入APP
  3. 利用yololib進行注入

二、示例演示

接下來蚌成,我們實戰(zhàn)一下前痘,針對微信的注冊登錄頁面,代碼HOOK注冊和登錄的流程担忧。

2.1 注冊分析

首先是注冊芹缔,我們查看注冊按鈕的UI層級??

上圖,打印地址可知瓶盛,發(fā)現(xiàn)注冊按鈕的TargetWCAccountLoginControlLogic最欠,actiononFirstViewRegister

然后我們直接hook這個這個方法就可以攔截了注冊事件??

#import "LGDylibHook.h"
#import <objc/runtime.h>

@implementation LGDylibHook

+ (void)load {
    NSLog(@"LGDylib  Hook success");
    //攔截微信注冊事件
    Method oldMethod = class_getInstanceMethod(objc_getClass("WCAccountLoginControlLogic"), @selector(onFirstViewRegister));
    Method newMethod = class_getInstanceMethod(self, @selector(hook_onFirstViewRegister));
    method_exchangeImplementations(oldMethod, newMethod);
}

- (void)hook_onFirstViewRegister {
    NSLog(@"WeChat click login");
}

@end

運行看看??

成功攔截惩猫!接下來芝硬,我們想在攔截到事件后,先執(zhí)行自己的代碼轧房,然后恢復原有的流程拌阴,怎么做到呢?相信大家都能想到 ?? Method Swizzle奶镶。

Method Swizzle

在OC中迟赃,SELIMP 之間的關(guān)系,就好像一本書的目錄实辑。SEL 是方法編號捺氢,就像標題一樣藻丢。IMP是方法實現(xiàn)的真實地址剪撬,就像頁碼一樣。他們是一一對應的關(guān)系悠反。
Runtime提供了交換兩個SELIMP對應關(guān)系的函數(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ù)交換兩個SELIMP對應關(guān)系的技術(shù)稱之為Method Swizzle(方法欺騙)

2.2 登錄調(diào)試分析

接下來我們調(diào)試下【登錄】功能??

上圖可知斋否,view debug分析可以得到TargetWCAccountMainLoginViewController梨水,actiononNext。同理茵臭,【登錄】事件可以攔截拿到疫诽,那么pwd怎么獲取呢?

view debug可以看到pwd控件是WCUITextField并且能看到對應的text(即密碼)。假如我們不借助view debug奇徒,怎么去獲取密碼呢雏亚?

2.2.1動態(tài)分析

第一種方式就是動態(tài)分析 ?? 可以通過響應鏈一步步分析控件層級響應關(guān)系??

結(jié)合presponderpviews分析。不過這種方式一般比較麻煩摩钙。

2.2.2靜態(tài)分析

第二種就是靜態(tài)分析了 ?? 使用class-dump工具導出頭文件(??swift文件不行)罢低。

./class-dump -H WeChat -o ./Headers

執(zhí)行完成后,所有頭文件都在Headers文件夾中??

文件過多22335個胖笛,不推薦Xcode打開网持。

  • 搜索WCAccountMainLoginViewController,找到onNext??

onNext方法既沒有參數(shù)也沒有返回值长踊,再看看有沒其它的和密碼相關(guān)的??

我們發(fā)現(xiàn)了_textFieldUserPwdItem不就是密碼的輸入框嗎功舀。

  1. 搜索WCAccountTextFieldItem

沒有找到WCUITextField相關(guān)的內(nèi)容,接著找父類WCBaseTextFieldItem

找到了WCUITextField類型的m_textField成員變量身弊。這個不就是輸入賬號密碼的控件日杈。

  1. 調(diào)試驗證

接下來我們來驗證下,是否是輸入密碼的控件佑刷??? KVC控制臺打印查看??

相關(guān)指令??

(lldb) po [(WCAccountMainLoginViewController *)0x117813000 valueForKey:@"_textFieldUserPwdItem"]
<WCAccountTextFieldItem: 0x2830f9ec0>

(lldb) po [(WCAccountTextFieldItem *)0x2830f9ec0 valueForKey:@"m_textField"]
<WCUITextField: 0x1120a4400; baseClass = UITextField; frame = (20 0; 345 44); text = 'qwer1234'; opaque = NO; autoresize = W+H; tintColor = UIExtendedSRGBColorSpace 0.027451 0.756863 0.376471 1; gestureRecognizers = <NSArray: 0x2818beca0>; layer = <CALayer: 0x281441dc0>>

(lldb) po [(WCUITextField *)0x1120a4400 text]
qwer1234

果然找到了對應的和想要的密碼莉擒。????????????????

2.3 登錄代碼注入

??注意:別用自己的常用賬號去嘗試,有可能被封號瘫絮。

最后涨冀,也是重點,注入自己的代碼??

+ (void)load {
    NSLog(@"LGDylib Hook success");
    //攔截微信登錄事件
    Method oldMethod = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
    Method newMethod = class_getInstanceMethod(self, @selector(hook_onNext));
    method_exchangeImplementations(oldMethod, newMethod);
}

- (void)hook_onNext {
    NSLog(@"WeChat click login");
    //獲取密碼
    UITextField *textField = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSString *pwd = [textField text];
    NSLog(@"password: %@",pwd);
}

接下來麦萤,在這個過程中我們應該調(diào)用回原來的方法鹿鳖,讓登錄進行下去??

- (void)hook_onNext {
    NSLog(@"WeChat click login");
    //獲取密碼
    UITextField *textField = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSString *pwd = [textField text];
    NSLog(@"password: %@",pwd);
    [self hook_onNext];
}

運行??

報錯:找不到方法!WCAccountMainLoginViewController中不存在hook_onNext方法壮莹。因為??

一般情況下我們進行方法交換分類中進行翅帜,現(xiàn)在不是在分類中.

那有沒有解決方案呢?當然有命满,且有3種??

class_addMethod方式

利用AddMethod方式涝滴,讓原始方法可以被調(diào)用,不至于因為找不到SEL而崩潰??

//1.class_addMethod 方式
+ (void)load {
    //攔截微信登錄事件
    Class cls = objc_getClass("WCAccountMainLoginViewController");
    Method onNext = class_getInstanceMethod(cls, @selector(onNext));
    //給WC添加新方法
    class_addMethod(cls, @selector(new_onNext), new_onNext, "v@:");
    //交換
    method_exchangeImplementations(onNext, class_getInstanceMethod(cls, @selector(new_onNext)));
}

//相當于imp
void new_onNext(id self, SEL _cmd) {
    //獲取密碼
    UITextField *textField = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSString *pwd = [textField text];
    NSLog(@"password: %@",pwd);
    [self performSelector:@selector(new_onNext)];
}
class_replaceMethod方式

利用class_replaceMethod胶台,直接給原始的方法替換IMP??

//2.class_replaceMethod 方式
+ (void)load {
    //攔截微信登錄事件
    Class cls = objc_getClass("WCAccountMainLoginViewController");
    //替換
    origin_onNext = class_replaceMethod(cls, @selector(onNext), new_onNext, "v@:");
}

//原始imp
IMP (*origin_onNext)(id self, SEL _cmd);

//相當于imp
void new_onNext(id self, SEL _cmd) {
    //獲取密碼
    UITextField *textField = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSString *pwd = [textField text];
    NSLog(@"password: %@",pwd);
    origin_onNext(self,_cmd);
}
method_setImplementation方式

利用method_setImplementation歼疮,直接重新賦值原始的IMP??

//3.method_setImplementation 方式
+ (void)load {
    //攔截微信登錄事件
    Class cls = objc_getClass("WCAccountMainLoginViewController");
    //獲取method
    Method onNext = class_getInstanceMethod(cls,@selector(onNext));
    //get
    origin_onNext = method_getImplementation(onNext);
    //set
    method_setImplementation(onNext, new_onNext);
}

//原始imp
IMP (*origin_onNext)(id self, SEL _cmd);

//相當于imp
void new_onNext(id self, SEL _cmd) {
    //獲取密碼
    UITextField *textField = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSString *pwd = [textField text];
    NSLog(@"password: %@",pwd);
    origin_onNext(self,_cmd);
}

以上3種方式都可以成功回到原來的登錄流程。大家可以自行驗證诈唬。

Demo(包含微信.ipa原始包)

XFResignHook

??注意:微信ipa包體過大韩脏,無法上傳gitHub,如果有需要铸磅,請留言發(fā)郵箱或私信我赡矢。

總結(jié)

  • 代碼注入:Framework(推薦)/ dylib
    • 創(chuàng)建Framework/dylib
    • yololib修改Macho Load Commands
      • ./yololib 要修改的可執(zhí)行文件 要添加的Framework/dylib路徑&名稱
  • 調(diào)試分析
    • 動態(tài)調(diào)試:view debug調(diào)試控件層級結(jié)合presponderpviews
    • 靜態(tài)分析:通過class-dump導出頭文件分析代碼邏輯
  • 代碼Hook
    • + load方法中hook對應類的對應方法
    • hook方法中調(diào)用回原來的方法
      1. class_addMethod
      2. class_replaceMethod
      3. method_setImplementation

相關(guān)鏈接

yololib
class-dump官網(wǎng)
class-dump github
class-dump 兼容Swift版本

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末杭朱,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子吹散,更是在濱河造成了極大的恐慌痕檬,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件送浊,死亡現(xiàn)場離奇詭異梦谜,居然都是意外死亡,警方通過查閱死者的電腦和手機袭景,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門唁桩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人耸棒,你說我怎么就攤上這事荒澡。” “怎么了与殃?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵单山,是天一觀的道長。 經(jīng)常有香客問我幅疼,道長米奸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任爽篷,我火速辦了婚禮悴晰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逐工。我一直安慰自己铡溪,他們只是感情好,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布泪喊。 她就那樣靜靜地躺著棕硫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪袒啼。 梳的紋絲不亂的頭發(fā)上哈扮,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天,我揣著相機與錄音瘤泪,去河邊找鬼灶泵。 笑死,一個胖子當著我的面吹牛对途,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播髓棋,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼实檀,長吁一口氣:“原來是場噩夢啊……” “哼惶洲!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起膳犹,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤恬吕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后须床,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铐料,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年豺旬,在試婚紗的時候發(fā)現(xiàn)自己被綠了钠惩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡族阅,死狀恐怖篓跛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情坦刀,我是刑警寧澤愧沟,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站鲤遥,受9級特大地震影響沐寺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜盖奈,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一芽丹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧卜朗,春花似錦拔第、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至逛万,卻和暖如春泳猬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宇植。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工得封, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人指郁。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓忙上,卻偏偏與公主長得像,于是被迫代替她去往敵國和親闲坎。 傳聞我的和親對象是個殘疾皇子疫粥,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容