001--反調(diào)試sysctl(代碼防護(hù))
// sysctl:檢測(cè)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秒鐘檢測(cè)一次
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(@"檢測(cè)到了!!");
}else{
NSLog(@"正常!!");
}
});
dispatch_resume(timer);
}
// 真機(jī)運(yùn)行 沒(méi)有DebuggerServer,所以不會(huì)檢測(cè)到調(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)試過(guò)程 */
//檢測(cè)是否被調(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就是沒(méi)有錯(cuò)誤,其他就是錯(cuò)誤碼
//1011 1000 1010 1010 1101 0101 1101 0101
//&
//0000 0000 0000 1000 0000 0000 0000 0000
// == 0 ? 沒(méi)有、有!!
return ((info.kp_proc.p_flag & P_TRACED) != 0); // P_TRACED: 跟蹤調(diào)試過(guò)程
}
- (void)viewDidLoad {
[super viewDidLoad];
debugCheck();
}
@end
002--破解sysctl(攻擊)
創(chuàng)建動(dòng)態(tài)庫(kù):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)建動(dòng)態(tài)庫(kù):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(@"檢測(cè)到了!!");
}else{
NSLog(@"正常!!");
}
});
dispatch_resume(timer);
}
//檢測(cè)是否被調(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就是沒(méi)有錯(cuò)誤,其他就是錯(cuò)誤碼
//1011 1000 1010 1010 1101 0101 1101 0101
//&
//0000 0000 0000 1000 0000 0000 0000 0000
// == 0 ? 沒(méi)有 有!!
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: 改變?cè)即a
ptrace (process trace 進(jìn)程跟蹤)
ptrace 系統(tǒng)函數(shù),是有符號(hào)的
查看 ptrace

下個(gè)符號(hào)斷點(diǎn)



運(yùn)行程序,立刻進(jìn)入斷點(diǎn):目的 就是為了看函數(shù)調(diào)用棧
(lldb)bt // 顯示當(dāng)前線程的調(diào)用堆棧(bt:back stack)
(lldb)image list // 查看庫(kù)

看不到函數(shù)調(diào)用棧,解決方案:用 Debug 模式

重新編譯運(yùn)行


用 Hopper Disassembler 分析MacO文件,找到上面??對(duì)應(yīng)的地址


快捷鍵:alt + A
同上

修改MacO文件:跳出 ptrace 方法的執(zhí)行


導(dǎo)出 新的MacO文件

新的MacO 文件 導(dǎo)入MonkeyApp 創(chuàng)建的工程里,運(yùn)行,可以調(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 庫(kù) 再加載 ZMHookManager,也就是hook代碼在先,防護(hù)在后,所以防護(hù)失效
2、先加載ZMHookManager 庫(kù) 再加載 ZMHook,現(xiàn)在防護(hù),再hook 就交互交換不到方法了,已經(jīng)被防護(hù)住了
3、對(duì)于檢測(cè)到對(duì)方的hook,采取的方法式 退出程序 exit !!
#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);
}
//保留原來(lái)的交換函數(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(@"檢測(cè)到了HOOK!!!");
//強(qiáng)制退出!
exit(1);
}
-(void)click1Hook:(id)sendr{
NSLog(@"原來(lái)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:檢測(cè)app進(jìn)程是否被附加 放在最前面執(zhí)行
002--破解 sysctl.wmv (攻)
創(chuàng)建動(dòng)態(tài)庫(kù):injectSysctl
導(dǎo)入fishhook
#import "injectCode.h"
#import "fishhook.h"
#import <sys/sysctl.h>
// fishhook 交換 sysctl 方法
003--ptrace&sysctl提前執(zhí)行.wmv (防護(hù))
創(chuàng)建動(dòng)態(tài)庫(kù):antiDebug
注意:antiDebugCode 防護(hù)在前(MonkeyApp 也進(jìn)攻不了)
- 反調(diào)試 (上節(jié)課有講解)
ptrace (process trace 進(jìn)程跟蹤)
此函數(shù)提供了一個(gè)進(jìn)程監(jiān)聽控制另外一個(gè)進(jìn)程.并且可以檢測(cè)被控制進(jìn)程的內(nèi)存和寄存器里面的數(shù)據(jù)!
它可以用來(lái)實(shí)現(xiàn)斷點(diǎn)調(diào)試和系統(tǒng)調(diào)用跟蹤.debugserver就是用的它
iOS 中沒(méi)有提供相關(guān)的頭.
書籍:<程序員的自我修養(yǎng)>
*/
原理:防護(hù)的代碼執(zhí)行的太早!
導(dǎo)致:我Hook的代碼執(zhí)行在其后!
004--攻防博弈!找到你就贏.wmv
MacO loadCommand 段:改變?cè)即a
ptrace (process trace 進(jìn)程跟蹤)
ptrace 系統(tǒng)函數(shù),是有符號(hào)的
下個(gè)ptrace符號(hào)斷點(diǎn)
運(yùn)行程序,立刻進(jìn)入斷點(diǎn):目的 就是為了看函數(shù)調(diào)用棧
(lldb)bt // 顯示當(dāng)前線程的調(diào)用堆棧(bt:back stack)
(lldb)image list // 查看庫(kù)
修改MacO文件:跳出 ptrace 方法的執(zhí)行
導(dǎo)出 新的MacO文件
新的MacO 文件 導(dǎo)入MonkeyApp 創(chuàng)建的工程里,運(yùn)行,可以調(diào)試了
005--破解懸疑已久的反HOOK.wmv
這里涉及到以前的章節(jié):1011- HOOK-代碼的防護(hù)
1、先加載ZMHook 庫(kù) 再加載 ZMHookManager,也就是hook代碼在先,防護(hù)在后,所以防護(hù)失效
2、先加載ZMHookManager 庫(kù) 再加載 ZMHook,現(xiàn)在防護(hù),再hook 就交互交換不到方法了,已經(jīng)被防護(hù)住了
3、對(duì)于檢測(cè)到對(duì)方的hook,采取的方法式 退出程序 exit ?。?/p>
反hook方案:
5.1 定位反hook的 退出進(jìn)程的 地方
5.2 hopper 查看 MacO 文件
5.3 hopper 修改內(nèi)存地址,讓防護(hù)的代碼,找不到要交換的方法,就可以去hook了