iOSHook系統(tǒng)C函數(shù)(二):FishHook

iOSHook系統(tǒng)C函數(shù)(一):使用動(dòng)態(tài)庫一文中,我們探討了如何使用動(dòng)態(tài)庫去HooK系統(tǒng)的C函數(shù),在本文中,我們將探索另外一種方式:fishhook,同樣我們也去勾取getenv函數(shù)

實(shí)現(xiàn)

在fishhook中,主要的hook對(duì)象為:

     struct rebinding {
       const char *name; // 需要HOOK的函數(shù)名稱,C字符串
       void *replacement; //新函數(shù)地址
       void **replaced; // 原始函數(shù)地址的指針
     };
  • 1,name: 需要HOOK的函數(shù)名稱,C字符串。
  • 2,replacement: 新函數(shù)地址。
  • 3,replaced: 原始函數(shù)地址的指針。

為什么要獲取原始函數(shù)地址指針?
通過保留一個(gè)變量來保存系統(tǒng)的函數(shù),方便調(diào)用系統(tǒng)的函數(shù)。

我們?cè)?code>viewDidLoad里面來進(jìn)行hook:

- (void)viewDidLoad {
    [super viewDidLoad];

    struct rebinding getEnvb; // 1
    getEnvb.name = "getenv"; //2
    getEnvb.replacement = my_getenv; //3
    getEnvb.replaced = (void *)&sys_getenv; //4

    struct rebinding rebs[1] = {getEnvb};
    rebind_symbols(rebs, 1); // 5
 m
    char * c = getenv("HOME");
    NSString *str = [NSString stringWithUTF8String:c];
    NSLog(@"%@",str);

    char * cTwo = getenv("PATH");
    NSString *strTwo = [NSString stringWithUTF8String:cTwo];

    NSLog(@"%@",strTwo);
}

static char *(* sys_getenv)(const char * str);  // 6

char  *my_getenv(const char * str){  // 7
    if (strcmp(str, "HOME") == 0 ) {
        return  "YAY";
    }else {
        return sys_getenv(str);
    }
}
  • 6: 根據(jù)getenv函數(shù)的簽名,創(chuàng)建一個(gè)靜態(tài)的函數(shù)名,用來保存系統(tǒng)的函數(shù)。

  • 7: 創(chuàng)建和系統(tǒng)函數(shù)交換自定義函數(shù),簽名需和系統(tǒng)函數(shù)保持一致。

  • 1: 創(chuàng)建 rebinding結(jié)構(gòu)體。

  • 2: 需要hook的函數(shù)的名字。

  • 3: 自定義的函數(shù),其方法簽名需于被Hook函數(shù)的簽名保持一致。

  • 4: 函數(shù)聲明,用來存儲(chǔ)系統(tǒng)原函數(shù)。

  • 5: 對(duì)符號(hào)進(jìn)行重新綁定。

我們運(yùn)行一下工程,運(yùn)行結(jié)果如下:

2020-11-22 16:25:29.210773+0800 FishHookDemo[13208:8651857] YAY
2020-11-22 16:25:29.211194+0800 FishHookDemo[13208:8651857] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/bin:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/bin:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/sbin:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/sbin:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/local/bin

是的,當(dāng)我們獲取HOME環(huán)境變量時(shí),被我們自定義函數(shù)替換了值,獲取PATH環(huán)境變量時(shí),成功的調(diào)用了系統(tǒng)的函數(shù)方法。

原理分析

fishhook是怎樣把系統(tǒng)的C函數(shù)替換的呢,我們應(yīng)用加載的時(shí)候,使用的是 PIC技術(shù):

  • 1,首先會(huì)在內(nèi)存的數(shù)據(jù)段生成符號(hào)表。
  • 2,dyld將真實(shí)地址賦值到數(shù)據(jù)段里面去,又叫做符號(hào)綁定

我們根據(jù)其文檔我們可知dyld是通過更新符號(hào)表的內(nèi)存指針來實(shí)現(xiàn)綁定的,符號(hào)表存在于Mach-O二進(jìn)制文件的__DATA段 ,fishhook通過 rebind_symbols函數(shù),對(duì)符號(hào)表進(jìn)行重綁定,來實(shí)現(xiàn)響應(yīng)替換

下面我們通過lldb指令來探索一下該過程。

我們修改一下代碼

- (void)viewDidLoad {
    [super viewDidLoad];

    char * c = getenv("HOME");
     NSString *str = [NSString stringWithUTF8String:c];

     NSLog(@"%@",str);

    struct rebinding getEnvb; // 1
    getEnvb.name = "getenv"; //2
    getEnvb.replacement = my_getenv; //3
    getEnvb.replaced = (void *)&sys_getenv; //4


    struct rebinding rebs[1] = {getEnvb};
    rebind_symbols(rebs, 1); // 5

    char * cOne = getenv("HOME");
    NSString *strOne = [NSString stringWithUTF8String:cOne];

    NSLog(@"%@",strOne);

    char * cTwo = getenv("PATH");
    NSString *strTwo = [NSString stringWithUTF8String:cTwo];

    NSLog(@"%@",strTwo);
}

首先我們將程序 comand + B進(jìn)行編譯一下,得到 FishHookDemo.app文件,然后 顯示包內(nèi)容,得到 Mach-O 文件。

App包內(nèi)容.png

然后我們將 Mach-O文件拖到Mach-O View中:
MachOView.jpg

符號(hào)表存在于 __DATA_CONST, __got__DATA,__la_symbol_ptr段中,分別為非懶加載符號(hào)表和懶加載符號(hào)表。這兩種符號(hào)表的區(qū)別在于它們綁定的時(shí)機(jī)不一樣,非懶加載符號(hào)表是在程序一啟動(dòng)就綁定了,懶加載符號(hào)表是在用到該符號(hào)時(shí),才會(huì)進(jìn)行綁定。_getenv符號(hào)存在于 Lazy Symbol Pointers,只有在用到時(shí)才會(huì)進(jìn)行綁定。

我們?cè)诘谝淮握{(diào)用的時(shí)候,設(shè)置一個(gè)斷點(diǎn),并運(yùn)行程序,進(jìn)入lldb模式

進(jìn)入lldb.png

我們使用 image list命令,來查找當(dāng)前進(jìn)程加載的所有鏡像,并找到FishHookDemo鏡像的首地址。

(lldb) image list
[  0] 19B79118-4135-3E49-BE15-A7C9B8EC16DF 0x00000001077e8000 /Users/ritamashin/Library/Developer/Xcode/DerivedData/FishHookDemo-ehajjrtywlbbfkfqujggbdpwoaos/Build/Products/Debug-iphonesimulator/FishHookDemo.app/FishHookDemo 
......

我們可以看到 0x00000001077e8000為該鏡像的 內(nèi)存地址(首地址) ,我們通過 首地址 + 偏移地址我們可以得到 符號(hào)所得地址值,通過 MachOView我們可以看到_getenv符號(hào)的偏移地址(offset)00005058,我們來查看一下其內(nèi)存值

(lldb) x 0x00000001077e8000+0x00005058
0x1077ed058: 88 a4 7e 07 01 00 00 00 92 a4 7e 07 01 00 00 00  ..~.......~.....
0x1077ed068: 9c a4 7e 07 01 00 00 00 e8 a3 7e 07 01 00 00 00  ..~.......~.....
  • x: memory read的縮寫,讀取內(nèi)存空間值。

因?yàn)槲覀兪?64位的系統(tǒng),我們從 0x1077ed058往右數(shù)8個(gè)字節(jié),就讀取到我們想要的值,因?yàn)?getenv函數(shù)還未調(diào)用,此時(shí)該值為系統(tǒng)的默認(rèn)值,我們將斷點(diǎn)移至第29行,

line29.png

然后查看內(nèi)存值

(lldb) x 0x00000001077e8000+0x00005058
0x1077ed058: 67 b1 32 52 ff 7f 00 00 92 a4 7e 07 01 00 00 00  g.2R......~.....
0x1077ed068: 9c a4 7e 07 01 00 00 00 e8 a3 7e 07 01 00 00 00  ..~.......~.....

此時(shí)此刻,因?yàn)檎{(diào)用了getenv函數(shù),對(duì)該符號(hào)進(jìn)行了綁定,內(nèi)存值已經(jīng)發(fā)生變化。我們將該內(nèi)存值使用匯編的形式進(jìn)行查看

(lldb) dis -s 0x00007fff5232b167
libsystem_c.dylib`getenv:
    0x7fff5232b167 <+0>:  pushq  %rbp
    0x7fff5232b168 <+1>:  movq   %rsp, %rbp
    0x7fff5232b16b <+4>:  pushq  %r14
    0x7fff5232b16d <+6>:  pushq  %rbx
    0x7fff5232b16e <+7>:  subq   $0x10, %rsp
    0x7fff5232b172 <+11>: movq   %rdi, %rbx
    0x7fff5232b175 <+14>: leaq   0x37b7b494(%rip), %r14    ; __environ_lock_obj
    0x7fff5232b17c <+21>: movq   %r14, %rdi
    0x7fff5232b17f <+24>: movl   $0x10000, %esi            ; imm = 0x10000 
  • dis -s:使用匯編的形式進(jìn)行查看

接下來,我們?cè)?調(diào)用完rebind_symbols函數(shù)之后,我們來看下其符號(hào)表關(guān)聯(lián)的內(nèi)存情況是否有變化。

line42.png

(lldb) x 0x00000001077e8000+0x00005058
0x1077ed058: a0 94 7e 07 01 00 00 00 af 7a 43 52 ff 7f 00 00  ..~......zCR....
0x1077ed068: d4 8d 3b 52 ff 7f 00 00 e8 a3 7e 07 01 00 00 00  ..;R......~.....
(lldb) dis -s 0x00000001077e94a0
FishHookDemo`my_getenv:
    0x1077e94a0 <+0>:  pushq  %rbp
    0x1077e94a1 <+1>:  movq   %rsp, %rbp
    0x1077e94a4 <+4>:  subq   $0x10, %rsp
    0x1077e94a8 <+8>:  movq   %rdi, -0x10(%rbp)
    0x1077e94ac <+12>: movq   -0x10(%rbp), %rdi
    0x1077e94b0 <+16>: leaq   0x1d62(%rip), %rsi        ; "HOME"
    0x1077e94b7 <+23>: callq  0x1077ea3b8               ; symbol stub for: strcmp
    0x1077e94bc <+28>: cmpl   $0x0, %eax

此時(shí)此刻,_getenv符號(hào)綁定的地址已變?yōu)?FishHookDemo中的my_getenv函數(shù)了,當(dāng)執(zhí)行函數(shù)時(shí),會(huì)執(zhí)行我們自定義的my_getenv函數(shù)。這樣我們證明到這里就結(jié)束了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容