前言
本篇文章基於前兩篇基礎之上的 . 還沒了解的同學歡迎閱讀 :
這兩篇文章中花了很多篇幅來講解 簽名
、重籤
、程式碼注入
等等 . 那麼重簽了 wx
的應用包 , 我們到底能不能拿來除錯 , 能不能看到原始碼 , 或者說 , 我們重簽名了到底有什麼用呢 ?
本篇文章我們一起來探索一下 .
"wx" 應用 '原始碼' 提取
準備工具
class-dump 提取碼 : kjjs
class-dump
這個工具可以將 Mach-O
中的類的描述 copy
出來 . 可以理解成把標頭檔案提取出來 , ( 但也不僅僅是標頭檔案 ) .
提取
開啟我們下載的越獄微信 ipa
. 轉 zip
解壓 , Payload
- WeChat
顯示包內容 , 找到 WeChat
的 Mach-O
原始檔 . 複製出來到 class-dump
同路徑下 .
cd
到這個目錄下 , 執行 :
./class-dump -H WeChat -o ./headers/
複製程式碼
執行完畢 :
其實其原理就是 根據 Mcah-O
中類的描述 , 屬性 , 方法 . 進行整理 , 然後生成 , 寫入 .
我們看到了一萬多個標頭檔案 . 這裡推薦一個方便檢視與搜尋的工具 .
直接把 headers
資料夾拖入 Sublime
.
你就可以隨意瀏覽了 . 後期會再考慮是否攝入彙編程式碼部分 .
程式碼修改 ( 破壞 / 竊取 ... )
需求 1 : 破壞註冊功能
這個需求比較簡單 , 實現思路就是程式碼注入的方式 , Hook
註冊按鈕的方法 . 修改為自己的方法即可 , 就不演示了 .
我們來演示個有點意思的.
需求 2 : 獲取使用者登入密碼 但保持其登入功能
我們來一步步玩一下 .
1 重簽名工程
準備好重籤成功的工程 , 沒有做程式碼注入的 , 就寫一個 framework
, 然後 shell
指令碼里 yololib
做一下. cmd + r
, run
起來.
記得檢查一下 程式碼有沒有注入成功.
2 找到登入按鈕方法
-
來到如下頁面 .
-
View Debug
左邊選擇視窗 , 選中登入按鈕 , 注意不要選中 上面覆蓋的 imageview
了 , 綠的那個. 右邊看 Target
和 Action
.
注意 :
筆者這裡是 Xcode 11 , 因此
Target
和Action
都是地址 , 老版本的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
來到我們的方法實現 , 是沒問題的 . 但當我們拿到了密碼想讓其訪問原方法 , 這個時候呼叫的是給 VC
發 my_onNext
的訊息 , 那肯定是找不到的 , 而如果是我們正向開發使用分類就沒這個問題了 , 這也是我們為什麼經常使用分類來做 hook
的主要原因 , 面試再碰到不要再回答 什麼汙染 什麼效率了...
解決辦法也很簡單 , 這裡我都給大家敲了一遍 貼出來了
-
- 給原本類新增一個方法 .
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
複製程式碼
-
- 使用替換
#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), new_onNext, "v@:");
}
// imp 指標 --》 8位元組。
IMP (*old_onNext)(id self,SEL _cmd);
//方法實現IMP
void new_onNext(id self,SEL _cmd){
//拿出使用者的密碼
UITextField * pwd = [[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
NSLog(@"竊取到使用者的密碼是%@",pwd.text);
//登入
old_onNext(self,_cmd);
}
複製程式碼
-
- 使用
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,SEL _cmd);
//方法實現IMP
void new_onNext(id self,SEL _cmd){
//拿出使用者的密碼
UITextField * pwd = [[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
NSLog(@"竊取到使用者的密碼是%@",pwd.text);
//登入
old_onNext(self,_cmd);
}
複製程式碼
其實 二和三的原理就是僅僅把 微信原方法 onNext
的 imp
儲存一下 , 然後換成我們自己的 , 在呼叫我們自己的方法之後再直接呼叫一下儲存的 imp
. 是不是超級簡單呢 ?
為什麼要講這麼多種方法呢 . 一是方便大家理解 , 另外後面我們會介紹一個專門來做 Hook
的工具 , 這個工具大部分都是直接使用的 getIMP
和 setIMP
. 大家敬請期待吧 ?.
執行
- 控制檯拿到密碼.
- 頁面上正常呼叫微信登入
這裡簡單模擬了一個需求 , 來實現了一下 , 主要是將這種方式介紹給大家 , 能實現什麼 , 大家可以自己去玩一玩 , 例如可否繞過某些視訊網站開通 vip
呢 ? 或者其他想法 .
當然 , 還是那句話 : 玩逆向 只是為了防護 .