一、 反 hook 初探
我們Hook別人的代碼一般使用OC的MethodSwizzle,如果我們用
fishhook將MethodSwizzle hook了,別人是不是就hook不了我們的代碼了?
1.1 創(chuàng)建主工程 AntiHookDemo
創(chuàng)建一個工程AntiHookDemo,頁面中有兩個按鈕btn1和btn2:

對應(yīng)兩個事件:
- (IBAction)btn1Click:(id)sender {
NSLog(@"click btn1");
}
- (IBAction)btn2Click:(id)sender {
NSLog(@"click btn2");
}
1.2 創(chuàng)建防護(hù) HookManager (FrameWork 動態(tài)庫)
這個時候要使用fishhook防護(hù),在FrameWork中寫防護(hù)代碼。基于兩點:
-
Framework在主工程+ load執(zhí)行之前執(zhí)行+ load。 - 別人注入的
Framework也在防護(hù)代碼之后。
創(chuàng)建一個HookManager Framework,文件結(jié)構(gòu)下:

AntiHookManager.h:
#import <Foundation/Foundation.h>
#import <objc/message.h>
//暴露給外界使用
CF_EXPORT void (*exchange_p)(Method _Nonnull m1, Method _Nonnull m2);
@interface AntiHookManager : NSObject
@end
AntiHookManager.m:
#import "AntiHookManager.h"
#import "fishhook.h"
@implementation AntiHookManager
+ (void)load {
//基本防護(hù)
struct rebinding exchange;
exchange.name = "method_exchangeImplementations";
exchange.replacement = hp_exchange;
exchange.replaced = (void *)&exchange_p;
struct rebinding bds[] = {exchange};
rebind_symbols(bds, 1);
}
//指回原方法
void (*exchange_p)(Method _Nonnull m1, Method _Nonnull m2);
void hp_exchange(Method _Nonnull m1, Method _Nonnull m2) {
//可以在這里進(jìn)行上報后端等操作
NSLog(@"find Hook");
}
@end
HookManager.h中導(dǎo)出頭文件:
#import <HookManager/AntiHookManager.h>
然后將AntiHookManager.h放入public Headers:

修改主工程的ViewController.m如下:
#import <HookManager/HookManager.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
exchange_p(class_getInstanceMethod(self.class, @selector(btn2Click:)),class_getInstanceMethod(self.class, @selector(test)));
}
- (void)test {
NSLog(@"self Hook Success");
}
- (IBAction)btn1Click:(id)sender {
NSLog(@"click btn1");
}
- (IBAction)btn2Click:(id)sender {
NSLog(@"click btn2");
}
@end
在工程中Hook自己的方法,這個時候運行主工程:
AntiHookDemo[1432:149145] click btn1
AntiHookDemo[1432:149145] self Hook Success
btn2能夠被自己正常Hook。
1.3 創(chuàng)建注入工程 HookDemo
- 在根目錄創(chuàng)建
APP文件夾以及Payload文件夾,拷貝AntiHookDemo.app到APP/Payload目錄,壓縮zip -ry AntiHookDemo.ipa Payload/生成.ipa文件。 - 拷貝
appResign.sh重簽名腳本以及yololib注入工具到根目錄。 - 創(chuàng)建
HPHook注入Framework。
HPHook代碼如下:
#import "HPInject.h"
#import <objc/message.h>
@implementation HPInject
+ (void)load {
method_exchangeImplementations(class_getInstanceMethod(objc_getClass("ViewController"), @selector(btn1Click:)), class_getInstanceMethod(self, @selector(my_click)));
}
- (void)my_click {
NSLog(@"inject Success");
}
@end
編譯運行:
AntiHookDemo[1437:149999] find Hook
AntiHookDemo[1437:149999] click btn1
AntiHookDemo[1437:149999] self Hook Success
首先是檢測到了Hook,其次自己內(nèi)部btn2 hook成功了,btn1 hook沒有注入成功。到這里暴露給自己用和防止別人Hook都已經(jīng)成功了。對于三方庫中正常使用到的Hook可以在防護(hù)代碼中做邏輯判斷可以加白名單等調(diào)用回原來的方法。如果自己的庫在image list最后一個那么三方庫其實已經(jīng)Hook完了。
當(dāng)然只Hook method_exchangeImplementations不能完全防護(hù),還需要Hook class_replaceMethod以及method_setImplementation。
這種防護(hù)方式破解很容易,一般不這么處理:
1.在Hopper中可以找到method_exchangeImplementations,直接在MachO中修改這個字符串HookManager中就Hook不到了(這里會直接crash,因為viewDidLoad中調(diào)用了exchange_p,對于有保護(hù)邏輯的就可以繞過了,并且method_exchangeImplementations沒法做混淆)

2.可以很容易定位到防護(hù)代碼,直接在防護(hù)代碼之前Hook,或者將fishhook中的一些系統(tǒng)函數(shù)Hook也能破解。本質(zhì)上是不執(zhí)行防護(hù)代碼。
二、MonkeyDev
MonkeyDev是逆向開發(fā)中一個常用的工具 MonkeyDev。能夠幫助我們進(jìn)行重簽名和代碼注入。
2.1 安裝 MonkeyDev
theos安裝(Cydia Substrate就是 theos中的工具)
sudo git clone --recursive https://github.com/theos/theos.git /opt/theos
配置環(huán)境變量
#逆向相關(guān)配置
#export THEOS=/opt/theos
#寫入環(huán)境變量
#export PATH=$THEOS/bin:$PATH
運行nic.pl查看theos信息。

[error] Cowardly refusing to make a project inside $THEOS (/opt/theos/)出現(xiàn)這個錯誤則是export配置有問題。
指定Xcode
sudo xcode-select -s /Applications/Xcode.app
安裝命令
sudo /bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/AloneMonkey/MonkeyDev/master/bin/md-install)"
這里是安裝Xcode插件。安裝完成后重啟Xcode在Xcode中會出現(xiàn)MonkeyDev對應(yīng)的功能:

-
MonkeyApp:自動給第三方應(yīng)用集成Reveal、Cycript和注入dylib的模塊,支持調(diào)試dylib和第三方應(yīng)用,支持Pod給第三放應(yīng)用集成SDK,只需要準(zhǔn)備一個砸殼后的ipa或者app文件即可。 -
MonkeyPod:提供了創(chuàng)建Pod的項目。 -
CaptainHook Tweak:使用CaptainHook提供的頭文件進(jìn)行OC函數(shù)的Hook以及屬性的獲取。 -
Command-line Tool:可以直接創(chuàng)建運行于越獄設(shè)備的命令行工具。 -
Logos Tweak:使用theos提供的logify.pl工具將.xm文件轉(zhuǎn)成.mm文件進(jìn)行編譯,集成了CydiaSubstrate,可以使用MSHookMessageEx和MSHookFunction來Hook OC函數(shù)和指定地址。
卸載命令
sudo /bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/AloneMonkey/MonkeyDev/master/bin/md-uninstall)"
更新命令
sudo /bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/AloneMonkey/MonkeyDev/master/bin/md-update)"
錯誤處理
1.MonkeyDev 安裝出現(xiàn):Types.xcspec not found
添加一個軟連接:
sudo ln -s /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/PrivatePlugIns/IDEOSXSupportCore.ideplugin/Contents/Resources /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications
https://github.com/AloneMonkey/MonkeyDev/issues/266
2.2 重簽名
創(chuàng)建一個MonkeyDemo工程:

工程目錄如下:

在工程目錄下有一個TargetApp目錄,直接將微信8.0.2版本拖進(jìn)去:

編譯運行工程:

這個時候就重簽名成功了。相比用腳本自己跑方便很多,也能避免很多異常。
2.3 MonkeyDev 代碼注入
工程配置
用MonkeyDemo注入一下AntiHookDemo,將AntiHookDemo編譯生成的App加入MonkeyDemo的TargetApp中:

代碼注入
在MonkeyDemo工程MonkeyDemoDylib->Logos目錄,.xm文件可以寫OC、C++、C:

將MonkeyDemoDylib.xm的type改為Objective-C++ Preprocessed Source:

這里面的默認(rèn)代碼就是Logos語法:

將
.xm默認(rèn)打開方式修改為Xcode后重啟Xcode就能識別代碼了,否則就還是默認(rèn)文本文件。將默認(rèn)的代碼刪除,寫Hook btn1Click的代碼:
#import <UIKit/UIKit.h>
//要hook的類
%hook ViewController
//要hook的方法
- (void)btn1Click:(id)sender {
NSLog(@"Monkey Hook Success");
//調(diào)用原來的方法
%orig;
}
%end
直接運行工程后點擊btn1:
AntiHookDemo[9306:5972601] find Hook
AntiHookDemo[9306:5972601] find Hook
AntiHookDemo[9309:5973617] Monkey Hook Success
AntiHookDemo[9350:5987306] click btn1

這個時候就
Hook成功了,并且檢測到了Hook。這里沒有防護(hù)住是因為Monkey中用的是getImp和setImp。對
AntiHookManager做下改進(jìn):AntiHookManager .h:
#import <Foundation/Foundation.h>
#import <objc/message.h>
//暴露給外界使用
CF_EXPORT void (*exchange_p)(Method _Nonnull m1, Method _Nonnull m2);
CF_EXPORT IMP _Nonnull (*getImp_p)(Method _Nonnull m);
CF_EXPORT IMP _Nonnull(*setImp_p)(Method _Nonnull m, IMP _Nonnull imp);
@interface AntiHookManager : NSObject
@end
AntiHookManager .m:
#import "AntiHookManager.h"
#import "fishhook.h"
@implementation AntiHookManager
+ (void)load {
//基本防護(hù)
struct rebinding exchange;
exchange.name = "method_exchangeImplementations";
exchange.replacement = hp_exchange;
exchange.replaced = (void *)&exchange_p;
struct rebinding setIMP;
setIMP.name = "method_setImplementation";
setIMP.replacement = hp_setImp;
setIMP.replaced = (void *)&setImp_p;
struct rebinding getIMP;
getIMP.name = "method_getImplementation";
getIMP.replacement = hp_getImp;
getIMP.replaced = (void *)&getImp_p;
struct rebinding bds[] = {exchange,setIMP,getIMP};
rebind_symbols(bds, 3);
}
//指回原方法
void (*exchange_p)(Method _Nonnull m1, Method _Nonnull m2);
IMP _Nonnull (*getImp_p)(Method _Nonnull m);
IMP _Nonnull(*setImp_p)(Method _Nonnull m, IMP _Nonnull imp);
void hp_exchange(Method _Nonnull m1, Method _Nonnull m2) {
//可以在這里進(jìn)行上報后端等操作
NSLog(@"find Hook");
}
void (hp_getImp)(Method _Nonnull m) {
NSLog(@"find Hook getImp");
}
void (hp_setImp)(Method _Nonnull m, IMP _Nonnull imp) {
NSLog(@"find Hook setImp");
}
@end
這個時候控制臺輸出:
AntiHookDemo[1488:207119] find Hook getImp
AntiHookDemo[1488:207119] find Hook
AntiHookDemo[1488:207119] find Hook getImp
AntiHookDemo[1488:207119] find Hook
AntiHookDemo[1488:207119] click btn1
點擊btn1也沒有Hook到了。在這里運行時有可能Crash在JSEvaluateScript的時候,直接刪除App重新跑一次就可以了。
libsubstrate.dylib解析的,
其實這里.xm文件是被libsubstrate.dylib解析成MonkeyDemoDylib.mm中的內(nèi)容(.xm代碼是不參與編譯的):

MSHookMessageEx底層用的是setImp和getImp對OC進(jìn)行Hook的。
錯誤問題
1.Signing for "MonkeyDemoDylib" requires a development team. Select a development team in the Signing & Capabilities editor.
直接在該target的build settings 中添加CODE_SIGNING_ALLOWED=NO

https://iosre.com/t/xcode11-monkeydev/17021
2.Failed to locate Logos Processor. Is Theos installed? If not, see https://github.com/theos/theos/wiki/Inst allation.
出現(xiàn)這個錯誤一般是theos沒有安裝好?;蛘呗窂脚渲玫挠袉栴}。
3.library not found for -libstdc++
需要下載對應(yīng)的庫到XCode目錄中。參考:https://github.com/longyoung/libstdc.6.0.9-if-help-you-give-a-star
4.The WatchKit app’s Info.plist must have a WKCompanionAppBundleIdentifier key set to the bundle identifier of the companion app.
刪除DerivedData重新運行。
5.This application or a bundle it contains has the same bundle identifier as this application or another bundle that it contains. Bundle identifiers must be unique.
這種情況大概率是手機上之前安裝過相同bundleId的App安裝不同版本導(dǎo)致,需要刪除重新安裝。還有問題的話刪除DerivedData改bundleId。
6.This app contains a WatchKit app with one or more Siri Intents app extensions that declare IntentsSupported that are not declared in any of the companion app's Siri Intents app extensions. WatchKit Siri Intents extensions' IntentsSupported values must be a subset of the companion app's Siri Intents extensions' IntentsSupported values.
需要刪除com.apple.WatchPlaceholder(在/opt/MonkeyDev/Tools目錄中修改pack.sh):
rm -rf "${TARGET_APP_PATH}/com.apple.WatchPlaceholder" || true

然后刪除DerivedData重新運行。
-
LLVM Profile Error: Failed to write file "default.profraw": Operation not permitted
這個說明App內(nèi)部做了反調(diào)試防護(hù)。直接在Monkey中開啟sysctl:
rebind_symbols((struct rebinding[1]){{"sysctl", my_sysctl, (void*)&orig_sysctl}},1);

8.Attempted to load Reveal Library twice. Are you trying to load dynamic library with Reveal Framework already linked?
直接刪除dylib中Other Linker Flags的設(shè)置即可(可能的原因是手機端已經(jīng)導(dǎo)入了這個庫):

??遇見莫名其妙的錯誤建議刪除DerivedData重啟Xcode重新運行。
總結(jié)
- 反
Hook- 使用
fishhookHookmethod_exchangeImplementations、class_replaceMethod、method_setImplementation - 需要在動態(tài)庫中添加防護(hù)代碼。
- 本地導(dǎo)出原函數(shù)
IMP供自己項目使用,配合白名單。 - 這種防護(hù)很容易破解,一般不推薦這么使用。
- 使用
-
MonkeyDev:逆向開發(fā)中一個常用的工具。- 重簽名:很容易,直接拖進(jìn)去
.ipa或者.app運行工程就可以了。 - 代碼注入:
Logos主要是編寫.xm文件。底層依然是getImp和setImp的調(diào)用。
- 重簽名:很容易,直接拖進(jìn)去