本文涉及內容無風險,但某信有檢測BundId機制,建議不要大號登陸bash
本文是創建在應用重簽名的基礎上微信
工具:yololib+class_dump 密碼:8ujj函數
代碼注入有兩種方案:經過FrameWork和dylib工具
照着iOS逆向 Shell腳本+腳本重簽名重簽名post
在Xcode中File->Target
新增一個Framework 學習
load
方法僅僅這樣還不夠,DYLD會動態加載項目中的Frameworks,但不會加載當前FrameWorkui
保證FrameWork放到FrameWorks
目錄下 編碼
建議將
yololib
複製粘貼到/usr/local/bin目錄
下,能夠隨時隨地調用
將app.sh
的最後一句代碼啓用(注意修改FrameWork名稱)
yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/XXXX.framework/XXXX"
複製代碼
不出意外會打印 ❎❎❎❎❎❎❎❎❎❎
其實就是換了個Target
BaseSDK
簽名
修改爲iPhone Developer
只有加進來了纔算成功了一半
yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/libFXHook.dylib"
複製代碼
打印 ❎❎❎❎❎❎❎❎❎❎ (有時候會報錯「image notound」,若是FrameWorks包含了dylib則從新運行就行了)
在OC中,SEL和IMP之間的關係,就好像一本書的「目錄」。 SEL是方法編號,就像「標題」同樣。 IMP是方法實現的真實地址,就像「頁碼」同樣。 他們是一一對應的關係。 Runtime提供了交換兩個SEL和IMP對應關係的函數method_exchangeImplementations(<#Method _Nonnull m1#>, <#Method _Nonnull m2#>)
,經過這個函數交換兩個SEL和IMP對應關係的技術,咱們稱之爲Method Swizzle(方法欺騙)
我更願意把SEL和IMP的關係理解成書的封面和書,原先一本《三國》和《水滸》,在通過方法交換以後翻開《三國》的封面倒是《水滸》的內容
NSURL *url = [NSURL URLWithString:[@"www.Felix.com/好好學習" stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];
NSLog(@"%@",url);
複製代碼
好比上述代碼看起來很繁瑣,這時候就能夠用Method Swizzling來實現
#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
複製代碼
①爲何要用分類
去作方法交換呢?
下文中將會說起
②自定義FX_URLWithString
中是否是遞歸了?
答:load方法執行順序較早,調用FX_URLWithString
時已經進行了方法交換,想調用FX_URLWithString
就應該調用URLWithString
各位看官可能以爲太簡單了,接下來就回到重簽名項目開始重頭戲
目標:點擊「註冊」按鈕使之無效
MachO文件在編譯出來的ipa包中
這裏用的是sublime工具,先全局找類(找不到就找父類)再找方法
在第一節注入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
複製代碼
目標:點擊「登陸」按鈕獲取到
帳號
和密碼
並繼續登陸
打印地址去頭文件列表查找聲明方法
發現這是一個不帶參數的方法,那麼帳號
和密碼
去那裏獲取呢?
咱們能夠去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>>
複製代碼
#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的好處就體現的淋漓盡致,直接給分類加個方法就完事了
利用class_addMethod方法讓原始方法能夠被調用(麻煩不推薦)
#import "InjectCode.h"
#import <objc/runtime.h>
#import "UIKit/UIKit.h"
@implementation InjectCode
+ (void)load {
/** * 一、給哪一個類添加方法 * 二、方法編號 * 三、方法實現(地址) * 四、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
複製代碼
保存原始方法,利用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);
}
複製代碼
保存原始方法,利用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
複製代碼
// 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中的代碼塊
複製代碼
習武是爲了強身健體,學習逆向是爲了防禦