寫在前面
本文涉及內容無風險,但某信有檢測BundId機制,建議不要大號登入
本文是建立在應用重簽名的基礎上
工具:yololib+class_dump 密碼:8ujj
一、初次注入
程式碼注入有兩種方案:通過FrameWork和dylib
1.指令碼重簽名
2.FrameWork注入
2.1 新建FrameWork
在Xcode中File->Target
新增一個Framework
2.2 FrameWork中新建一個類
2.3 新增一個load
方法
僅僅這樣還不夠,DYLD會動態載入專案中的Frameworks,但不會載入當前FrameWork
2.4 執行編譯一下
保證FrameWork放到FrameWorks
目錄下
2.5 yololib注入動態庫
建議將
yololib
複製貼上到/usr/local/bin目錄
下,可以隨時隨地呼叫
將app.sh
的最後一句程式碼啟用(注意修改FrameWork名稱)
yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/XXXX.framework/XXXX"
複製程式碼
2.6 執行
不出意外會列印 ❎❎❎❎❎❎❎❎❎❎
3.dylib注入
其實就是換了個Target
3.1 新建Library
3.2 修改dylib的BaseSDK
3.3 修改dylib的簽名
修改成iPhone Developer
3.4 新增依賴
3.5 執行編譯
只有加進來了才算成功了一半
3.6 修改指令碼
yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/libFXHook.dylib"
複製程式碼
3.7 執行
列印 ❎❎❎❎❎❎❎❎❎❎ (有時候會報錯“image notound”,如果FrameWorks包含了dylib則重新執行就好了)
二、Method Swizzling初用
1.定義
在OC中,SEL和IMP之間的關係,就好像一本書的“目錄”。
SEL是方法編號,就像“標題”一樣。
IMP是方法實現的真實地址,就像“頁碼”一樣。
他們是一一對應的關係。
Runtime提供了交換兩個SEL和IMP對應關係的函式method_exchangeImplementations(<#Method _Nonnull m1#>, <#Method _Nonnull m2#>)
,通過這個函式交換兩個SEL和IMP對應關係的技術,我們稱之為Method Swizzle(方法欺騙)
我更願意把SEL和IMP的關係理解成書的封面和書,原先一本《三國》和《水滸》,在經過方法交換之後翻開《三國》的封面卻是《水滸》的內容
2.程式碼演示
NSURL *url = [NSURL URLWithString:[@"www.Felix.com/好好學習" stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];
NSLog(@"%@",url);
複製程式碼
比如上述程式碼看起來很繁瑣,這時候就可以用Method Swizzling來實現
2.1 新建NSURL的分類
2.2 方法交換
#import "NSURL+FXUrl.h"
#import <objc/runtime.h>
@implementation NSURL (FXUrl)
+ (void)load {
// 獲取原來的方法
Method URLWithString = class_getClassMethod(self, @selector(URLWithString:));
// 獲取自定義方法
Method FXURLWithString = class_getClassMethod(self, @selector(FX_URLWithString:));
// 交換方法
method_exchangeImplementations(URLWithString, FXURLWithString);
}
+ (instancetype)FX_URLWithString:(NSString *)string {
NSURL *url = [NSURL FX_URLWithString:string];
if (!url) {
string = [string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
}
return [NSURL FX_URLWithString:string];
}
@end
複製程式碼
2.3 疑問點
①為什麼要用分類
去做方法交換呢?
下文中將會提及
②自定義FX_URLWithString
中是不是遞迴了?
答:load方法執行順序較早,呼叫FX_URLWithString
時已經進行了方法交換,想呼叫FX_URLWithString
就應該呼叫URLWithString
各位看官可能覺得太簡單了,接下來就回到重簽名專案開始重頭戲
三、Hook微信——破壞微信註冊
目標:點選“註冊”按鈕使之無效
1.獲取到物件名稱和方法名稱
之前文章中有講到過,選中控制元件就能通過地址列印對應的資訊(有可能直接顯示了物件名稱和方法名稱)2.利用class-dump匯出MachO的標頭檔案
MachO檔案在編譯出來的ipa包中
3.搜尋標頭檔案檢視方法宣告
這裡用的是sublime工具,先全域性找類(找不到就找父類)再找方法
4.交換方法
在第一節注入FrameWork的程式碼中繼續
#import "InjectCode.h"
#import <objc/runtime.h>
@implementation InjectCode
+ (void)load {
Method oldMethod = class_getInstanceMethod(objc_getClass("WCAccountLoginControlLogic"), @selector(onFirstViewRegister));
Method newMethod = class_getInstanceMethod(self, @selector(FX_onFirstViewRegister));
method_exchangeImplementations(oldMethod, newMethod);
}
- (void)FX_onFirstViewRegister {
NSLog(@"想註冊嗎?想註冊先打錢!");
}
@end
複製程式碼
四、Hook微信——竊取登入密碼
目標:點選“登入”按鈕獲取到
賬號
和密碼
並繼續登入
1.分析
列印地址去標頭檔案列表查詢宣告方法
發現這是一個不帶引數的方法,那麼賬號
和密碼
去那裡獲取呢?
我們可以去Viewcontroller的變數中找找線索
發現了兩個可疑的例項變數_textFieldUserNameItem
和_textFieldUserPwdItem
檢視WCAccountTextFieldItem
沒發現什麼有實際意義的內容,那再找找父類吧
WCUITextField *m_textField
這個例項變數看起來有點用
怎麼判斷是否是我們要找的賬號
和密碼
呢?
輸入賬號和密碼再ViewDebug除錯一下
如下圖所示,我們找到了寫Hook程式碼的方向
(lldb) po 0x133800600
<WCAccountMainLoginViewController: 0x133800600>
(lldb) po [(WCAccountMainLoginViewController *)0x133800600 valueForKey:@"_textFieldUserNameItem"]
<WCAccountTextFieldItem: 0x28231f180>
(lldb) po [(WCAccountTextFieldItem *)0x28231f180 valueForKey:@"m_textField"]
<WCUITextField: 0x13090d600; baseClass = UITextField; frame = (20 0; 345 44); text = 'Felix'; opaque = NO; autoresize = W+H; tintColor = UIExtendedSRGBColorSpace 0.00784314 0.733333 0 1; gestureRecognizers = <NSArray: 0x280891650>; layer = <CALayer: 0x280612560>>
複製程式碼
2.開始Hook
#import "InjectCode.h"
#import <objc/runtime.h>
#import "UIKit/UIKit.h"
//@interface WCAccountTextFieldItem: NSObject
//
//@end
@implementation InjectCode
+ (void)load {
Method oldMethod = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
Method newMethod = class_getInstanceMethod(self, @selector(FX_onNext));
method_exchangeImplementations(oldMethod, newMethod);
}
- (void)FX_onNext {
// /// 宣告WCAccountTextFieldItem類,為了不報錯
// WCAccountTextFieldItem *account = [self valueForKey:@"_textFieldUserNameItem"];
// /// 匯入UIKit框架
// UITextField *accountTF = [account valueForKey:@"m_textField"];
UITextField *accountTF = [[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"];
UITextField *passwordTF = [[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"];
NSLog(@"賬號:“%@”\n密碼:“%@”", accountTF.text, passwordTF.text);
[self FX_onNext];
}
@end
複製程式碼
正當我們滿心歡喜等待神奇的一刻時,熟悉的味道來了
崩潰原因:WCAccountMainLoginViewController
找不到FX_onNext
的方法編號,即原工程中WCAccountMainLoginViewController
沒有FX_onNext
宣告
OC方法呼叫有兩個隱藏引數:self(方法呼叫者)、cmd(方法編號),FrameWork中把onNext
的imp替換成了FX_onNext
,頁面呼叫登入方法來到我們自定義的方法實現;然後給VC
傳送FX_onNext
訊息,必然是unrecognized selector sent to instance
此時此刻用分類Hook的好處就體現的淋漓盡致,直接給分類加個方法就完事了
3.解決崩潰完成Hook
3.1 class_addMethod方法
利用class_addMethod方法讓原始方法可以被呼叫(麻煩不推薦)
#import "InjectCode.h"
#import <objc/runtime.h>
#import "UIKit/UIKit.h"
@implementation InjectCode
+ (void)load {
/**
* 1、給哪個類新增方法
* 2、方法編號
* 3、方法實現(地址)
* 4、v代表Void @代表id型別 :代表@selecter型別(可以在幫助文件檢視這個方法)
*/
BOOL didAddMethod = class_addMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(FX_onNext), FX_onNext, "v@:");
if (didAddMethod) {
NSLog(@"新增方法成功");
Method oldMethod = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
Method newMethod = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(FX_onNext));
method_exchangeImplementations(oldMethod, newMethod);
}
}
//方法實現IMP
void FX_onNext(id self, SEL _cmd) {
UITextField *accountTF = [[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"];
UITextField *passwordTF = [[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"];
NSLog(@"賬號:“%@” 密碼:“%@”", accountTF.text, passwordTF.text);
//使用原來邏輯
[self performSelector:@selector(FX_onNext)];
}
@end
複製程式碼
3.2 class_replaceMethod方法
儲存原始方法,利用replaceMethod方法將原始方法的IMP覆蓋
#import "InjectCode.h"
#import <objc/runtime.h>
#import "UIKit/UIKit.h"
@implementation InjectCode
+ (void)load {
onNext = method_getImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)));
class_replaceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext), FX_onNext, "v@:");
}
IMP (*onNext)(id self,SEL _cmd);
void FX_onNext(id self, SEL _cmd) {
UITextField *accountTF = [[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"];
UITextField *passwordTF = [[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"];
NSLog(@"賬號:“%@” 密碼:“%@”", accountTF.text, passwordTF.text);
//使用原來邏輯
onNext(self,_cmd);
}
複製程式碼
3.3 method_setImplementation方法
儲存原始方法,利用setImplementation方法將原始方法的IMP重寫
#import "InjectCode.h"
#import <objc/runtime.h>
#import "UIKit/UIKit.h"
@implementation InjectCode
+ (void)load {
onNext = method_getImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)));
method_setImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)), FX_onNext);
}
IMP (*onNext)(id self,SEL _cmd);
//方法實現IMP
void FX_onNext(id self, SEL _cmd) {
UITextField *accountTF = [[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"];
UITextField *passwordTF = [[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"];
NSLog(@"賬號:“%@” 密碼:“%@”", accountTF.text, passwordTF.text);
//使用原來邏輯
onNext(self,_cmd);
}
@end
複製程式碼
五、Runtime-API
// 1.objc_xxx 系列函式
// 函式名稱 函式作用
objc_getClass 獲取Class物件
objc_getMetaClass 獲取MetaClass物件
objc_allocateClassPair 分配空間,建立類(僅在 建立之後,註冊之前 能夠新增成員變數)
objc_registerClassPair 註冊一個類(註冊後方可使用該類建立物件)
objc_disposeClassPair 登出某個類
objc_allocateProtocol 開闢空間建立協議
objc_registerProtocol 註冊一個協議
objc_constructInstance 構造一個例項物件(ARC下無效)
objc_destructInstance 析構一個例項物件(ARC下無效)
objc_setAssociatedObject 為例項物件關聯物件
objc_getAssociatedObje*ct 獲取例項物件的關聯物件
objc_removeAssociatedObjects 清空例項物件的所有關聯物件
objc_系列函式關注於巨集觀使用,如類與協議的空間分配,註冊,登出等操作
// 2.class_xxx 系列函式
函式名稱 函式作用
class_addIvar 為類新增例項變數
class_addProperty 為類新增屬性
class_addMethod 為類新增方法
class_addProtocol 為類遵循協議
class_replaceMethod 替換類某方法的實現
class_getName 獲取類名
class_isMetaClass 判斷是否為元類
objc_getProtocol 獲取某個協議
objc_copyProtocolList 拷貝在執行時中註冊過的協議列表
class_getSuperclass 獲取某類的父類
class_setSuperclass 設定某類的父類
class_getProperty 獲取某類的屬性
class_getInstanceVariable 獲取例項變數
class_getClassVariable 獲取類變數
class_getInstanceMethod 獲取例項方法
class_getClassMethod 獲取類方法
class_getMethodImplementation 獲取方法的實現
class_getInstanceSize 獲取類的例項的大小
class_respondsToSelector 判斷類是否實現某方法
class_conformsToProtocol 判斷類是否遵循某協議
class_createInstance 建立類的例項
class_copyIvarList 拷貝類的例項變數列表
class_copyMethodList 拷貝類的方法列表
class_copyProtocolList 拷貝類遵循的協議列表
class_copyPropertyList 拷貝類的屬性列表
class_系列函式關注於類的內部,如例項變數,屬性,方法,協議等相關問題
// 3.object_xxx 系列函式
函式名稱 函式作用
object_copy 物件copy(ARC無效)
object_dispose 物件釋放(ARC無效)
object_getClassName 獲取物件的類名
object_getClass 獲取物件的Class
object_setClass 設定物件的Class
object_getIvar 獲取物件中例項變數的值
object_setIvar 設定物件中例項變數的值
object_getInstanceVariable 獲取物件中例項變數的值 (ARC中無效,使用object_getIvar)
object_setInstanceVariable 設定物件中例項變數的值 (ARC中無效,使用object_setIvar)
objcet_系列函式關注於物件的角度,如例項變數
// 4.method_xxx 系列函式
函式名稱 函式作用
method_getName 獲取方法名
method_getImplementation 獲取方法的實現
method_getTypeEncoding 獲取方法的型別編碼
method_getNumberOfArguments 獲取方法的引數個數
method_copyReturnType 拷貝方法的返回型別
method_getReturnType 獲取方法的返回型別
method_copyArgumentType 拷貝方法的引數型別
method_getArgumentType 獲取方法的引數型別
method_getDescription 獲取方法的描述
method_setImplementation 設定方法的實現
method_exchangeImplementations 替換方法的實現
method_系列函式關注於方法內部,如果方法的引數及返回值型別和方法的實現
// 5.property_xxx 系列函式
函式名稱 函式作用
property_getName 獲取屬性名
property_getAttributes 獲取屬性的特性列表
property_copyAttributeList 拷貝屬性的特性列表
property_copyAttributeValue 拷貝屬性中某特性的值
property_系類函式關注與屬性*內部,如屬性的特性等
// 6.protocol_xxx 系列函式
函式名稱 函式作用
protocol_conformsToProtocol 判斷一個協議是否遵循另一個協議
protocol_isEqual 判斷兩個協議是否一致
protocol_getName 獲取協議名稱
protocol_copyPropertyList 拷貝協議的屬性列表
protocol_copyProtocolList 拷貝某協議所遵循的協議列表
protocol_copyMethodDescriptionList 拷貝協議的方法列表
protocol_addProtocol 為一個協議遵循另一協議
protocol_addProperty 為協議新增屬性
protocol_getProperty 獲取協議中的某個屬性
protocol_addMethodDescription 為協議新增方法描述
protocol_getMethodDescription 獲取協議中某方法的描述
// 7.ivar_xxx 系列函式
函式名稱 函式作用
ivar_getName 獲取Ivar名稱
ivar_getTypeEncoding 獲取型別編碼
ivar_getOffset 獲取偏移量
// 8.sel_xxx 系列函式
函式名稱 函式作用
sel_getName 獲取名稱
sel_getUid 註冊方法
sel_registerName 註冊方法
sel_isEqual 判斷方法是否相等
// 9.imp_xxx 系列函式
函式名稱 函式作用
imp_implementationWithBlock 通過程式碼塊建立IMP
imp_getBlock 獲取函式指標中的程式碼塊
imp_removeBlock 移除IMP中的程式碼塊
複製程式碼
寫在結尾
習武是為了強身健體,學習逆向是為了防護