一、inlinehook概述
inlineHook
(內(nèi)聯(lián)鉤子):所謂InlineHook
就是直接修改目標函數(shù)的頭部代碼。讓它跳轉到我們自定義的函數(shù)里面執(zhí)行我們的代碼,從而達到Hook
的目的。這種Hook
技術一般用在靜態(tài)語言的HOOK
上面。
Inline Hook
就是在運行的流程中插入跳轉指令來搶奪運行流程的一個方法。大體分為三步:
- 將原函數(shù)的前
N
個字節(jié)搬運到Hook
函數(shù)的前N
個字節(jié); - 然后將原函數(shù)的前
N
個字節(jié)填充跳轉到Hook
函數(shù)的跳轉指令; - 在
Hook
函數(shù)末尾幾個字節(jié)填充跳轉回原函數(shù)+N
的跳轉指令;
image.png
Dobby
(原名:HOOKZz
)。是一個全平臺的inlineHook
(內(nèi)聯(lián)鉤子)框架,用起來就和fishhook
一樣。Dobby
是通過插入 __zDATA
段和__zTEXT
段到 Mach-O
中。
-
__zDATA
用來記錄Hook
信息(Hook
數(shù)量、每個Hook
方法的地址)、每個Hook
方法的信息(函數(shù)地址、跳轉指令地址、寫Hook
函數(shù)的接口地址)、每個Hook
的接口(指針)。 -
__zText
用來記錄每個Hook
函數(shù)的跳轉指令。
Dobby
通過 mmap
把整個 Mach-O
文件映射到用戶的內(nèi)存空間,寫入完成保存本地。所以 Dobby
并不是在原 Mach-O
上進行操作,而是重新生成并替換。
Doddy
二、 編譯Dobby
首先clone
工程
#depth用于指定克隆深度,為1即表示只克隆最近一次commit.
git clone https://github.com/jmpews/Dobby.git --depth=1
由于Dobby
是跨平臺的,所以項目并不是一個Xcode
工程,要使用cmake
將這個工程編譯成為Xcode
工程。進入Dobby
目錄,創(chuàng)建一個文件夾,然后cmake
編譯工程:
cd Dobby && mkdir build_for_ios_arm64 && cd build_for_ios_arm64
cmake .. -G Xcode \
-DCMAKE_TOOLCHAIN_FILE=cmake/ios.toolchain.cmake \
-DPLATFORM=OS64 -DARCHS="arm64" -DCMAKE_SYSTEM_PROCESSOR=arm64 \
-DENABLE_BITCODE=0 -DENABLE_ARC=0 -DENABLE_VISIBILITY=1 -DDEPLOYMENT_TARGET=9.3 \
-DDynamicBinaryInstrument=ON -DNearBranch=ON -DPlugin.SymbolResolver=ON -DPlugin.Darwin.HideLibrary=ON -DPlugin.Darwin.ObjectiveC=ON
編譯完成后,會生成一個Xcode
工程(這里在build_for_ios_arm64
目錄中 )。接下來編譯Xcode
工程生成我們的Framework
。
新版本的Dobby
多了很多功能,這里我們只編譯DpbbyX
或者dobby
,區(qū)別就是一個是.Framework
一個是.dylib
:
到這里
Dobby
庫就已經(jīng)準備好了。
和上一篇文章一樣如果提示需要賬戶直接添加
CODE_SIGNING_ALLOWED
配置。
三、導入 DobbyX.Framework / libdobby.dylib
新建一個工程DoddyDemo
,導入DobbyX.Framework
:
問題處理
-
bitcode
問題'/Users/zaizai/HookTest/DoddyDemo/DobbyX.framework/DobbyX' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. file '/Users/zaizai/HookTest/DoddyDemo/DobbyX.framework/DobbyX' for architecture arm64
兩個方案:
1.生成Framework
/dylib
的時候設置支持bitcode
image.png
2.項目關閉bitcode
。
image.png 拷貝
Framework
:dyld: Library not loaded: @rpath/DobbyX.framework/DobbyX Referenced from: /private/var/containers/Bundle/Application/BFDFC7CA-7045-4392-881D-1BFEA22E6BFA/DoddyDemo.app/DoddyDemo Reason: image not found
這里就是DobbyX
在Macho
文件中已經(jīng)有了,但是在Frameworks
目錄中沒有。Xcode
不會幫我們自動Copy
,需要添加Copy
:
處理完成后運行工程輸出以為內(nèi)容表示成功:
[*] ================================
[*] Dobby
[*] ================================
[*] dobby in debug log mode, disable with cmake flag "-DDOBBY_DEBUG=OFF"
四、 DobbyDemo
dobby
最核心的函數(shù)DobbyHook
定義如下:
// replace function
int DobbyHook(void *address, void *replace_call, void **origin_call);
-
address
:需要HOOK
的函數(shù)地址。 -
replace_call
:新函數(shù)地址。 -
origin_call
:保留原始函數(shù)的指針的地址。
從函數(shù)的參數(shù)就能看出來和fishhook
很相似。使用下試試:
#import <DobbyX/dobby.h>
+ (void)load {
//Hook sum
DobbyHook(sum, HP_sum, (void *)&sum_p);
}
//要Hook的函數(shù)
int sum(int a, int b){
return a + b;
}
//新函數(shù)
int HP_sum(int a,int b) {
NSLog(@"before hook: %d + %d = %d",a,b,sum_p(a,b));
return a - b;
}
//原函數(shù)指針
static int(*sum_p)(int a,int b);
調(diào)用:
NSLog(@"after hook: %d + %d = %d",10,20,sum(10, 20));
輸出:
DoddyDemo[9679:6241363] before hook: 10 + 20 = 30
DoddyDemo[9679:6241363] after hook: 10 + 20 = -10
這里也就Hook
了自定義C
函數(shù),彌補了fishhook
不能hook
自定義c
函數(shù)的問題。
那么外部函數(shù)可以Hook
么?Hook
下NSLog
試下:
//新函數(shù)
void HP_NSLog(NSString *format, ...) {
NSLog_p([format stringByAppendingString:@"\nHook Success"]);
}
//原函數(shù)指針
static void(*NSLog_p)(NSString *format, ...);
+ (void)load {
DobbyHook(NSLog, HP_NSLog, (void *)&NSLog_p);
}
調(diào)用:
NSLog(@"HotpotCat");
輸出:
HotpotCat
Hook Success
可以看到也是可以的,由于是直接插入跳轉指令來執(zhí)行自己的代碼肯定都可以。
那么Dobby
是怎么做的呢?以sum
函數(shù)為例,分別在Hook
前后在sum
函數(shù)中打斷點看下匯編代碼:
//before hook
DoddyDemo`sum:
0x104ab9c3c <+0>: sub sp, sp, #0x10 ; =0x10
0x104ab9c40 <+4>: str w0, [sp, #0xc]
0x104ab9c44 <+8>: str w1, [sp, #0x8]
-> 0x104ab9c48 <+12>: ldr w8, [sp, #0xc]
0x104ab9c4c <+16>: ldr w9, [sp, #0x8]
0x104ab9c50 <+20>: add w0, w8, w9
0x104ab9c54 <+24>: add sp, sp, #0x10 ; =0x10
0x104ab9c58 <+28>: ret
//after hook
DoddyDemo`sum:
0x104ab9c3c <+0>: adrp x17, 0
0x104ab9c40 <+4>: add x17, x17, #0xc5c ; =0xc5c
0x104ab9c44 <+8>: br x17
-> 0x104ab9c48 <+12>: ldr w8, [sp, #0xc]
0x104ab9c4c <+16>: ldr w9, [sp, #0x8]
0x104ab9c50 <+20>: add w0, w8, w9
0x104ab9c54 <+24>: add sp, sp, #0x10 ; =0x10
0x104ab9c58 <+28>: ret
通過匯編對比可以看到前面3
行發(fā)生了變化(??第二個sum
調(diào)用沒有拉伸棧空間,這里也就確實修改后匯編的行數(shù)是不變的,棧平衡放到了后面的函數(shù)中),打斷點查看x17
的內(nèi)容:
可以看到在
sum
的頭部跳轉到了HP_sum
中執(zhí)行HP_sum
函數(shù)。這也就驗證了inlinehook
是hook
函數(shù)的頭部進行跳轉。在這里br x17
后面的代碼就不執(zhí)行了,只有在HP_sum
中調(diào)用sum_p
的時候才執(zhí)行原來的代碼。
繼續(xù)進入HP_sum
:
進入blr x9
:
可以看到這里拉伸了棧空間,也就是說只有調(diào)用原來的函數(shù)才開辟空間,在
sum
的br x17
后面做棧回收。這樣也就棧平衡了。
繼續(xù)往下調(diào)用,最后會分別回到HP_sum->sum
也就是執(zhí)行sum
后面的一段匯編:
繼續(xù)進入:
驗證確實回到了
sum
中繼續(xù)執(zhí)行。??修改的
__Text
段實際上是替換。
五、 地址替換符號
在正常的逆向開發(fā)中,我們一般情況下拿到的都是地址,所以DobbyHook
的第一個參數(shù)應該是一個地址。那么怎么將符號替換成地址呢?還是以sum
函數(shù)為例。
在這里我們能算出函數(shù)的偏移值為:
0x102dc5c70 - 0x0000000102dc0000 = 0x5C70
,查看MachO
也確實是sum
函數(shù)。那么在代碼中直接通過
偏移值 + ASLR
就能得到函數(shù)執(zhí)行時候的地址了。(一般我們并不知道這個地址是sum
,這里只是演示。正常逆向是要分析代碼邏輯找地址然后嘗試的)。修改
sum
函數(shù)hook
為地址代碼如下:
#import <DobbyX/dobby.h>
#import <mach-o/dyld.h>
//sum 函數(shù)地址 偏移值: PAGEZERO(0x100000000) + offset(0x5C70),這里用 uintptr_t 類型是為了方便計算。這里只是64位,暫不考慮32位。如果最低版本從`iOS11`開始,就不用考慮32位的情況了。
static uintptr_t sum_address_offset = 0x100005C70;
+ (void)load {
//地址hook
//獲取ASLR,相當于rebase
uintptr_t slide = _dyld_get_image_vmaddr_slide(0);
uintptr_t sum_address = sum_address_offset + slide;
NSLog(@"sum_address_offset:%p\nslide:%p\nsum_address:%p",(void *)sum_address_offset,(void *)slide,(void *)sum_address);
DobbyHook((void *)sum_address, HP_sum, (void *)&sum_p);
}
這個時候運行并沒有hook
成功:
sum_address_offset:0x100005c70
slide:0x4150000
sum_address:0x104155c70
hook: 10 + 20 = 30
因為我們在主工程添加了代碼,MachO
變了,所以對應的獲取的到的0x5C70
也發(fā)生了改變:
這里也就說明了一些
app
的插件在app
更新后就不能用的原因。這里有兩種方式處理偏移值改變的情況。重新獲取偏移值,或者將Hook
代碼放入動態(tài)庫中。重新獲取計算偏移值發(fā)現(xiàn)是
0x5D08
,直接將sum_address_offset
修改:
static uintptr_t sum_address_offset = 0x100005D08;
再次運行工程發(fā)現(xiàn)Hook
成功了。
sum_address_offset:0x100005d08
slide:0x2570000
sum_address:0x102575d08
before hook: 10 + 20 = 30
hook: 10 + 20 = -10
??這里有個問題是
sum_address_offset
的值變了,為什么偏移值沒有變。這里是因為這塊改動很小,數(shù)據(jù)大小是沒有變化的,不會影響到偏移值的變化。當然最好的方案當然是放入動態(tài)庫中。
六、 Dobby注入
正常情況寫分析其它App
我們是要注入自己的動態(tài)庫的,寫個AppDemo
試下下整個流程。
AppDemo
的主頁面有如下代碼:
int sum(int a,int b) {
return a + b;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"sum result: %d",sum(10, 20));
}
計算下偏移值為0x5E54
。編譯生成AppDemo.app
。新建一個Hook
工程HookAppDemo
。利用HookAppDemo
工程對AppDemo.app
重簽名和注入。
將DobbyX.framework
和AppDemo.app
拷貝到HookAppDemo
工程的目錄。
目錄結構如下:
HPHook
是自定義注入庫。
這里HPHook
依賴DobbyX
有兩種方式:
-
DobbyX
加入到主工程:
image.png
然后在HPHook
Target
配置:
這個時候兩個庫都已經(jīng)在Frameworks
中了,并且注入成功了。
-
DobbyX
加入到HPHook
中:
配置HPHook
或者主工程
Copy Files
(任意一個都可以):
image.png
- 配置
HPHook
則主工程和Framework
MachO
分別如下:
HookAppDemo
HPHook
- 配置主工程
MachO
分別如下:
HookAppDemo
原理一樣,層級不同。
七、 Hook自定義函數(shù)
HPHook中寫Hook代碼
#import "HPInject.h"
#import <DobbyX/dobby.h>
#import <mach-o/dyld.h>
@implementation HPInject
//sum 函數(shù)地址 偏移值: PAGEZERO(0x100000000) + offset(0x5E54),這里用 uintptr_t 類型是為了方便計算。
static uintptr_t sum_address_offset = 0x100005E54;
+ (void)load {
//獲取ASLR,相當于rebase
uintptr_t slide = _dyld_get_image_vmaddr_slide(0);
uintptr_t sum_address = sum_address_offset + slide;
NSLog(@"sum_address_offset:%p\nslide:%p\nsum_address:%p",(void *)sum_address_offset,(void *)slide,(void *)sum_address);
DobbyHook((void *)sum_address, HP_sum, (void *)&sum_p);
}
//要Hook的函數(shù)
int sum(int a, int b){
return a + b;
}
//新函數(shù)
int HP_sum(int a,int b) {
NSLog(@"Hook Success");
return sum_p(a,b);
}
//原函數(shù)指針
static int(*sum_p)(int a,int b);
@end
輸出:
Hook Success
sum result: 30
這個時候就Hook
成功了。
八、Hook Swift
還是同樣的邏輯,將AppDemo
中sum
改寫為swift
實現(xiàn),創(chuàng)建一個Swift AppDemo
,代碼如下:
func sum(a: Int, b: Int) -> Int {
return a + b
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print(sum(a: 10, b: 20))
}
同樣的方式計算偏移值0x6650
在HookAppDemo
中用SwiftAppDemo.app
替換AppDemo.app
,修改sum_address_offset
值。運行工程:
Hook Success
30
這個時候Swift
代碼也Hook
成功了。
inlinehook(Dobby
)運行時Hook
:對目標函數(shù)的匯編代碼進行修改,修改的是內(nèi)存中的MachO
的代碼段(強制替換)。
修改目標函數(shù)后,函數(shù)棧平衡放到調(diào)用原函數(shù)的地方。這么做為了確認是否需要調(diào)用原始的方法,需要的話會找時機拉伸棧后返回執(zhí)行后面的代碼。