iOS逆向 HOOK原理之fishhook

歡迎閱讀iOS逆向系列(按序閱讀食用效果更加)git

寫在前面

以前在iOS逆向 代碼注入+Hook篇章講過Method Swizzling,這是一種基於Runtime能夠動態改變方法實現的HOOK方案,但Method Swizzling有本身的侷限性,本文就將系統的介紹一下何謂HOOKfishhookgithub

1、HOOK概述

1.HOOK定義

HOOK翻譯成中文爲「掛鉤」、「鉤子」,在iOS逆向領域中指的是改變程序運行流程的一種技術,經過HOOK可讓別人的程序執行本身所寫的代碼數組

下列示意圖就是對HOOK功能的形象詮釋:安全

  • 注入惡意代碼讓用戶誤覺得打卡成功,實際並無完成打卡(只修改中間流程)
  • 注入惡意代碼讓用戶誤覺得網絡出問題(開闢新的流程分支)

2.HOOK方式

在iOS中HOOK技術有如下幾種:微信

  • Method Swizzling:利用OC的Runtime特性,動態改變SEL(方法編號)IMP(方法實現)的對應關係,達到OC方法調用流程改變的目的
  • fishhook:這是FaceBook提供的一個動態修改連接machO文件的工具,利用machO文件加載原理,經過修改懶加載和非懶加載兩個表的指針達到C函數HOOK的目的
  • Cydia Substrate:原名爲Mobile Substrate,它的主要做用是針對OC方法、C函數以及函數地址進行HOOK操做,且安卓也能使用

以前已經介紹過Method Swizzling了,OC的Runtime特性讓它有了「黑魔法」之稱,同時也是侷限性所在網絡

三者的區別以下:框架

  • Method Swizzling只適用於動態的OC方法(運行時肯定函數實現地址)
  • fishhook適用於靜態的C方法(編譯時肯定函數實現地址)
  • Cydia Substrate是一種強大的框架,只須要經過Logos語言(相似於正向開發)就能夠進行Hook,適用於OC方法、C函數以及函數地址

2、fishhook

1.fishhook的使用

github上找到fishhook的代碼,將fishhook.cfishhook.h拖入項目中函數

整個fishhook就開放了一個結構體rebinding和兩個函數工具

  • rebind_symbolshook項目中的全部函數名稱
  • rebind_symbols_image只hook某一個資源庫的函數名稱

使用fishhook的步驟:post

  1. 導入頭文件
#import "fishhook.h"
複製代碼
  1. 指定交換函數——rebinding結構體對象
struct rebinding nslog;
nslog.name = "NSLog";
nslog.replacement = fxNSlog;
nslog.replaced = (void *)&sys_nslog;
複製代碼
  1. 調用rebind_symbols從新綁定符號
struct rebinding rebs[] = {nslog};
rebind_symbols(rebs, 1);
複製代碼

完整代碼以下:

#import "fishhook.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    struct rebinding nslog;
    nslog.name = "NSLog";
    nslog.replacement = fxNSlog;
    nslog.replaced = (void *)&sys_nslog;
    
    struct rebinding rebs[] = {nslog};
    rebind_symbols(rebs, 1);
}

static void(*sys_nslog)(NSString *format, ...);

void fxNSlog(NSString *format, ...) {
    format = [format stringByAppendingFormat:@"已hook"];
    sys_nslog(format);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"點擊屏幕");
}

@end
複製代碼
  • 定義rebinding來指定交換的函數
    • name中指定Hook的函數名稱(C字符串)
    • replacement中指定新的函數地址
      • c函數的名稱就是函數指針——靜態語言編譯時就已肯定
    • replaced中存放原始函數地址的指針
      • 因爲NSLog在Foundation庫中,在每一個手機的內存地址都不同,不能經過CMD+B就能肯定其內存地址
      • 這裏fishhook在運行的時刻會動態獲取到NSLog的地址,因此這裏定義新的函數指針保存函數實現
      • 使用&修改變量內部的值
      • 使用(void *)進行類型轉換
  • rebind_symbols中須要兩個參數
    • 參數一是rebinding數組
    • 參數二是rebinding數組的長度

2.fishhook的侷限性

前面介紹了fishhook是用來hook C函數的,但在此前提下仍是有它的侷限性——沒法交換自定義函數

  • C函數是靜態的,在編譯時就已經肯定了函數地址(函數實現地址在MachO本地文件中)
  • 系統C函數則是存在着動態的部分

那麼爲何系統級別的C函數就能夠呢?這就要說到PIC技術了

3.PIC技術

PIC技術(Position-independent code),又叫作位置獨立代碼,是爲了系統C函數在編譯時期可以確認一個地址的一種技術手段

  • 編譯時在MachO文件中預留出一段空間——符號表(DATA段中)
  • dyld把應用加載到內存中時(此時在Load Commands中會依賴Foundation),在符號表中找到了NSLog函數,就會進行連接綁定——將FoundationNSLog的真實地址賦值到DATA段NSLog符號上

而自定義的C函數不會生成符號表,直接就是一個函數地址,因此fishhook的侷限性就在於只有符號表內的符號能夠hook(從新綁定符號)

4.LLDB調試

由於NSLog是個懶加載的符號,因此加了行代言代碼

  1. 經過image list打印全部鏡像資源——第一個就是進程的內存首地址
  2. 獲取到MachO中NSLog的內存偏移量
    使用計算器,內存首地址+內存偏移量=符號地址
  3. 經過memory read 內存地址讀出符號地址
    因爲iOS是小端模式,內存地址8位倒着讀
  4. 經過dis -s 內存地址查看彙編
    此時的符號並無綁定,由於尚未使用
  5. 過掉斷點(使用NSLog)再進行查看
    此時的NSLog符號已經被綁定
  6. 再跳到下一步(重綁定符號)進行查看
    此時的NSLog符號已經被重綁定了

5.fishhook原理探索

fishhook的開源網站上有這麼一張工做原理圖

接下來分析下 rebind_symbols

  • prepend_rebindingsrebindings數組不斷添加到_rebindings_head鏈表的頭部成爲新的頭節點
  • 判斷鏈表_rebindings_head->next是否爲空來判斷是不是第一次調用
    • 若首次調用則使用_dyld_register_func_for_add_image進行回調監聽
    • 若不是則遍歷調用_rebind_symbols_for_image
  • 這個函數有個Int返回值
    • 若是重定向成功則返回 0
    • 若是失敗則返回 -1

接下來是_rebind_symbols_for_image對MachO文件的操做:按照規則計算各類表的地址和指針在表中的偏移量

最後一步,perform_rebinding_with_section根據算好的符號表地址和偏移量,找到在符號表中用於指向共享庫目標函數的指針,將該指針的值(即目標函數的地址)賦值給rebinding*replaced,最後修改該指針的值爲replacement(新的函數地址)

6.fishhook原理的實踐——在MachO中查找函數實現

接下來就根據字符串對應在符號表中的指針,找到其在共享庫的函數實現的

  1. NSLog在Lazy Symbol Pointers中位列第一個,下標爲0
  2. Dynamic System Table->Indirect Symbols中的value與Lazy Symbol Pointers一一對應,此表中的Data(0xA9=169)即爲另外一個表的下標
  3. 拿着這個下標在Symbol Table->Symbols中找到NSLog,此時的Data對應String Table Index中的偏移值(0x0000009B)
  4. 最後在String Table中計算表頭(0x000061EC)+偏移量(0x0000009B),找到NSLog的函數地址

7.fishhook應用場景

既然fishhook能夠交換C函數,那麼就能夠交換method_exchangeImplementations等函數來防止HOOK,項目自己若是須要使用method_exchangeImplementations則可使用新的函數地址來進行操做

  • 攻:能夠插入新的動態庫提早進行HOOK方法,此時fishhook的防禦就不起做用了
  • 防:也能夠提早插入動態庫進行fishhook交換(逆向注入的動態庫通常排在原項目的動態庫以後),此時fishhook代碼先於hook代碼,因此仍是fishhook仍是生效的
  • 攻:釜底抽薪——直接找到fishhook的函數地址進行修改,再次運行
  • ...

寫在後面

在安全攻防領域中,hook原理起到相當重要的做用,而使用fishhook作防禦雖然意義不大,可是瞭解fishhook原理對後續的學習仍是挺有幫助的

相關文章
相關標籤/搜索