iOS逆向 代碼注入+Hook

寫在前面

本文涉及內容無風險,但某信有檢測BundId機制,建議不要大號登陸bash

本文是創建在應用重簽名的基礎上微信

iOS逆向 應用重簽名+微信重簽名實戰app

iOS逆向 Shell腳本+腳本重簽名框架

工具:yololib+class_dump 密碼:8ujj函數

1、初次注入

代碼注入有兩種方案:經過FrameWork和dylib工具

1.腳本重簽名

照着iOS逆向 Shell腳本+腳本重簽名重簽名post

2.FrameWork注入

2.1 新建FrameWork

在Xcode中File->Target新增一個Framework 學習

2.2 FrameWork中新建一個類
2.3 添加一個load方法

僅僅這樣還不夠,DYLD會動態加載項目中的Frameworks,但不會加載當前FrameWorkui

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則從新運行就行了)

2、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

各位看官可能以爲太簡單了,接下來就回到重簽名項目開始重頭戲

3、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
複製代碼

4、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 {
    /** * 一、給哪一個類添加方法 * 二、方法編號 * 三、方法實現(地址) * 四、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
複製代碼

5、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中的代碼塊
複製代碼

寫在結尾

習武是爲了強身健體,學習逆向是爲了防禦

相關文章
相關標籤/搜索