001--反調(diào)試sysctl(代碼防護(hù))
// sysctl:檢測app進(jìn)程是否被附加 (防護(hù)進(jìn)程被調(diào)試) 《程序員的自我修養(yǎng)》
#import "ViewController.h"
#import <sys/sysctl.h>
@interface ViewController ()
@end
static dispatch_source_t timer;
@implementation ViewController
// 1秒鐘檢測一次
void debugCheck(){
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0.0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
if (isDebugger()) {
NSLog(@"檢測到了!!");
}else{
NSLog(@"正常!!");
}
});
dispatch_resume(timer);
}
// 真機(jī)運行 沒有DebuggerServer遏片,所以不會檢測到調(diào)試
// sysctl(int * 控制碼, u_int 字節(jié), void * 查詢結(jié)果, size_t * 結(jié)構(gòu)體, void * 結(jié)構(gòu)體的大小, size_t)
// #define P_TRACED 0x00000800 /* Debugged process being traced: 跟蹤調(diào)試過程 */
//檢測是否被調(diào)試
BOOL isDebugger(){
//控制碼
int name[4];//里面放字節(jié)碼.查詢信息
name[0] = CTL_KERN; //內(nèi)核查看
name[1] = KERN_PROC; //查詢進(jìn)程
name[2] = KERN_PROC_PID;//傳遞的參數(shù)是進(jìn)程的ID(PID) //同:$ ps -A
name[3] = getpid(); //PID的值告訴(進(jìn)程id)
struct kinfo_proc info; //接受進(jìn)程查詢結(jié)果信息的結(jié)構(gòu)體
size_t info_size = sizeof(info);//結(jié)構(gòu)體的大小
//int error = sysctl(name, 4, &info, &info_size, 0, 0);
int error = sysctl(name, sizeof(name)/sizeof(*name), &info, &info_size, 0, 0);
assert(error == 0);//0就是沒有錯誤,其他就是錯誤碼
//1011 1000 1010 1010 1101 0101 1101 0101
//&
//0000 0000 0000 1000 0000 0000 0000 0000
// == 0 ? 沒有、有!!
return ((info.kp_proc.p_flag & P_TRACED) != 0); // P_TRACED: 跟蹤調(diào)試過程
}
- (void)viewDidLoad {
[super viewDidLoad];
debugCheck();
}
@end
002--破解sysctl(攻擊)
創(chuàng)建動態(tài)庫:injectSysctl
導(dǎo)入fishhook
#import "injectCode.h"
#import "fishhook.h"
#import <sys/sysctl.h>
@implementation injectCode
//原始函數(shù)的地址
int (*sysctl_p)(int *, u_int, void *, size_t *, void *, size_t);
//自定義函數(shù)
int mySysctl(int *name, u_int namelen, void *info, size_t *infosize, void *newinfo, size_t newinfosize){
if (namelen == 4
&& name[0] == CTL_KERN
&& name[1] == KERN_PROC
&& name[2] == KERN_PROC_PID
&& info
&& (int)*infosize == sizeof(struct kinfo_proc))
{
int err = sysctl_p(name, namelen, info, infosize, newinfo, newinfosize);
//拿出info做判斷
struct kinfo_proc * myInfo = (struct kinfo_proc *)info;
if((myInfo->kp_proc.p_flag & P_TRACED) != 0){ //取與 是否等于零
//使用異或取反
myInfo->kp_proc.p_flag ^= P_TRACED;
}
return err;
}
return sysctl_p(name, namelen, info, infosize, newinfo, newinfosize);
}
+(void)load
{
//交換
rebind_symbols((struct rebinding[1]){{"sysctl",mySysctl,(void *)&sysctl_p}}, 1);
}
@end
003--ptrace&sysctl提前執(zhí)行 (再防護(hù))
創(chuàng)建動態(tài)庫:antiDebug
注意:antiDebugCode 防護(hù)在前
#import "antiDebugCode.h"
#import "fishhook.h"
#import "MyPtraceHeader.h"
#import <sys/sysctl.h>
static dispatch_source_t timer;
@implementation antiDebugCode
void debugCheck(){
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0.0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
if (isDebugger()) {
NSLog(@"檢測到了!!");
}else{
NSLog(@"正常!!");
}
});
dispatch_resume(timer);
}
//檢測是否被調(diào)試
BOOL isDebugger(){
//控制碼
int name[4];//里面放字節(jié)碼.查詢信息
name[0] = CTL_KERN;//內(nèi)核查看
name[1] = KERN_PROC;//查詢進(jìn)程
name[2] = KERN_PROC_PID;//傳遞的參數(shù)是進(jìn)程的ID(PID)
name[3] = getpid();//PID的值告訴
struct kinfo_proc info;//接受進(jìn)程查詢結(jié)果信息的結(jié)構(gòu)體
size_t info_size = sizeof(info);//結(jié)構(gòu)體的大小
int error = sysctl(name, sizeof(name)/sizeof(*name), &info, &info_size, 0, 0);
assert(error == 0);//0就是沒有錯誤,其他就是錯誤碼
//1011 1000 1010 1010 1101 0101 1101 0101
//&
//0000 0000 0000 1000 0000 0000 0000 0000
// == 0 ? 沒有 有!!
return ((info.kp_proc.p_flag & P_TRACED) != 0);
}
void debugerCheck(){
if (isDebugger()) {
NSLog(@"進(jìn)程被調(diào)試!!");
}
//開啟反調(diào)試
ptrace(PT_DENY_ATTACH, getpid(), 0, 0);
}
+(void)load
{
debugerCheck();
}
@end
004--攻防博弈匀借!找到你就贏
loadCommand: 改變原始代碼
ptrace (process trace 進(jìn)程跟蹤)
ptrace 系統(tǒng)函數(shù),是有符號的
查看 ptrace
下個符號斷點
運行程序蛮粮,立刻進(jìn)入斷點:目的 就是為了看函數(shù)調(diào)用棧
(lldb)bt // 顯示當(dāng)前線程的調(diào)用堆棧(bt:back stack)
(lldb)image list // 查看庫
看不到函數(shù)調(diào)用棧出吹,解決方案:用 Debug 模式
重新編譯運行
用 Hopper Disassembler 分析MacO文件,找到上面??對應(yīng)的地址
快捷鍵:alt + A
同上
修改MacO文件:跳出 ptrace 方法的執(zhí)行
導(dǎo)出 新的MacO文件
新的MacO 文件 導(dǎo)入MonkeyApp 創(chuàng)建的工程里掂名,運行据沈,可以調(diào)試了!
005--破解懸疑已久的反HOOK
這里涉及到以前的章節(jié):1011- HOOK-代碼的防護(hù)
終端命令:
壓縮成ipa包
zip -ry ZMHook--基本防護(hù).ipa Payload
zip -ry antiHook基本防護(hù).ipa Payload
zip -ry antiHook基本防護(hù)2.ipa Payload
zip -ry antiHook基本防護(hù)exit.ipa Payload
1饺蔑、先加載ZMHook 庫 再加載 ZMHookManager锌介,也就是hook代碼在先,防護(hù)在后膀钠,所以防護(hù)失效
2掏湾、先加載ZMHookManager 庫 再加載 ZMHook,現(xiàn)在防護(hù)肿嘲,再hook 就交互交換不到方法了融击,已經(jīng)被防護(hù)住了
3、對于檢測到對方的hook雳窟,采取的方法式 退出程序 exit W鹄恕匣屡!
#import "ZMHookManager.h"
#import "fishhook.h"
#import <objc/message.h>
@implementation ZMHookManager
//專門HOOK
+(void)load
{
NSLog(@"ZMHookManager--Load");
//內(nèi)部用到的交換代碼!
Method old = class_getInstanceMethod(objc_getClass("ViewController"), @selector(btnClick1:));
Method new = class_getInstanceMethod(self, @selector(click1Hook:));
method_exchangeImplementations(old, new);
//基本防護(hù)
struct rebinding bd;
bd.name = "method_exchangeImplementations";
bd.replacement = myExchang;
bd.replaced = (void *)&exchangeP;
// struct rebinding rebindings[] = {bd};
// rebind_symbols(rebindings, 1);
// method_getImplementation
// method_setImplementation
struct rebinding bd1;
bd1.name = "method_getImplementation";
bd1.replacement = myExchang;
bd1.replaced = (void *)&getIMP;
struct rebinding bd2;
bd2.name = "method_setImplementation";
bd2.replacement = myExchang;
bd2.replaced = (void *)&setIMP;
struct rebinding rebindings[] = {bd,bd1,bd2};
rebind_symbols(rebindings, 3);
}
//保留原來的交換函數(shù)
IMP _Nonnull (*setIMP)(Method _Nonnull m, IMP _Nonnull imp);
IMP _Nonnull (*getIMP)(Method _Nonnull m);
void (*exchangeP)(Method _Nonnull m1, Method _Nonnull m2);
//新的函數(shù)
void myExchang(Method _Nonnull m1, Method _Nonnull m2){
NSLog(@"檢測到了HOOK!!!");
//強(qiáng)制退出!
exit(1);
}
-(void)click1Hook:(id)sendr{
NSLog(@"原來APP的HOOK保留!!");
}
@end
5.1 定位反hook的 退出進(jìn)程的 地方
5.2 hopper 查看 MacO 文件
5.3 hopper 修改內(nèi)存地址,讓防護(hù)的代碼拇涤,找不到要交換的方法捣作,就可以去hook了
%hook ViewController
- (void)btnClick1:(id)sender {
NSLog(@"HOOK到了!!");
}
%end
總結(jié):
001--反調(diào)試 sysctl.wmv (防護(hù))
#import <sys/sysctl.h>
// sysctl:檢測app進(jìn)程是否被附加 放在最前面執(zhí)行
002--破解 sysctl.wmv (攻)
創(chuàng)建動態(tài)庫:injectSysctl
導(dǎo)入fishhook
#import "injectCode.h"
#import "fishhook.h"
#import <sys/sysctl.h>
// fishhook 交換 sysctl 方法
003--ptrace&sysctl提前執(zhí)行.wmv (防護(hù))
創(chuàng)建動態(tài)庫:antiDebug
注意:antiDebugCode 防護(hù)在前(MonkeyApp 也進(jìn)攻不了)
- 反調(diào)試 (上節(jié)課有講解)
ptrace (process trace 進(jìn)程跟蹤)
此函數(shù)提供了一個進(jìn)程監(jiān)聽控制另外一個進(jìn)程.并且可以檢測被控制進(jìn)程的內(nèi)存和寄存器里面的數(shù)據(jù)!
它可以用來實現(xiàn)斷點調(diào)試和系統(tǒng)調(diào)用跟蹤.debugserver就是用的它
iOS 中沒有提供相關(guān)的頭.
書籍:<程序員的自我修養(yǎng)>
*/
原理:防護(hù)的代碼執(zhí)行的太早!
導(dǎo)致:我Hook的代碼執(zhí)行在其后鹅士!
004--攻防博弈券躁!找到你就贏.wmv
MacO loadCommand 段:改變原始代碼
ptrace (process trace 進(jìn)程跟蹤)
ptrace 系統(tǒng)函數(shù),是有符號的
下個ptrace符號斷點
運行程序掉盅,立刻進(jìn)入斷點:目的 就是為了看函數(shù)調(diào)用棧
(lldb)bt // 顯示當(dāng)前線程的調(diào)用堆棧(bt:back stack)
(lldb)image list // 查看庫
修改MacO文件:跳出 ptrace 方法的執(zhí)行
導(dǎo)出 新的MacO文件
新的MacO 文件 導(dǎo)入MonkeyApp 創(chuàng)建的工程里也拜,運行,可以調(diào)試了
005--破解懸疑已久的反HOOK.wmv
這里涉及到以前的章節(jié):1011- HOOK-代碼的防護(hù)
1趾痘、先加載ZMHook 庫 再加載 ZMHookManager慢哈,也就是hook代碼在先,防護(hù)在后永票,所以防護(hù)失效
2卵贱、先加載ZMHookManager 庫 再加載 ZMHook,現(xiàn)在防護(hù)侣集,再hook 就交互交換不到方法了键俱,已經(jīng)被防護(hù)住了
3、對于檢測到對方的hook肚吏,采取的方法式 退出程序 exit 7窖!
反hook方案:
5.1 定位反hook的 退出進(jìn)程的 地方
5.2 hopper 查看 MacO 文件
5.3 hopper 修改內(nèi)存地址罚攀,讓防護(hù)的代碼党觅,找不到要交換的方法,就可以去hook了