前言
在逆向和保护的过程中,总会涉及到反调试和反反调试的问题,这篇文章主要是总结一下几种常见的反调试手段以及反反调试的方法。
反调试
ptrace
为了方便应用软件的开发和调试,从Unix的早期版本开始就提供了一种对运行中的进程进行跟踪和控制的手段,那就是系统调用
ptrace()
。
通过
ptrace
可以对另一个进程实现调试跟踪,同时
ptrace
还提供了一个非常有用的参数那就是
PT_DENY_ATTACH
,这个参数用来告诉系统,阻止调试器依附。
所以最常用的反调试方案就是通过调用
ptrace
来实现反调试。
#ifndef PT_DENY_ATTACH
#define PT_DENY_ATTACH 31
#endif
typedef
int
(*ptrace_ptr_t)
(
int
_request, pid_t _pid, caddr_t _addr,
int
_data)
;
ptrace(PT_DENY_ATTACH,
0
,
0
,
0
);
void
*handle = dlopen(
0
, RTLD_GLOBAL | RTLD_NOW);
ptrace_ptr_t
ptrace_ptr = (
ptrace_ptr_t
)dlsym(handle,
"ptrace"
);
ptrace_ptr(PT_DENY_ATTACH,
0
,
0
,
0
);
sysctl
当一个进程被调试的时候,该进程会有一个标记来标记自己正在被调试,所以可以通过
sysctl
去查看当前进程的信息,看有没有这个标记位即可检查当前调试状态。
BOOL
isDebuggerPresent(){
int
name[
4
];
//指定查询信息的数组
struct
kinfo_proc info;
//查询的返回结果
size_t info_size =
sizeof
(info);
info.kp_proc.p_flag =
0
;
name[
0
] = CTL_KERN;
name[
1
] = KERN_PROC;
name[
2
] = KERN_PROC_PID;
name[
3
] = getpid();
if
(sysctl(name,
4
, &info, &info_size, NULL,
0
) ==
-1
){
NSLog
(
@"sysctl error ..."
);
return
NO;
}
return
((info.kp_proc.p_flag & P_TRACED) !=
0
);
}
检测到调试器就退出,或者制造崩溃,或者隐藏工程啥的,当然也可以定时去查看有没有这个标记。
syscall
为从实现从用户态切换到内核态,系统提供了一个系统调用函数
syscall
,上面讲到的
ptrace
也是通过系统调用去实现的。
26. ptrace 801e812c T
所以如下的调用等同于调用
ptrace
:
syscall
(
26
,
31
,
0
,
0
,
0
);
arm
syscall
是通过软中断来实现从用户态到内核态,也可以通过汇编
svc
调用来实现。
#ifdef __arm__
asm
volatile
(
"mov r0,#31\n"
"mov r1,#0\n"
"mov r2,#0\n"
"mov r12,#26\n"
"svc #80\n"
)
;
#endif
#ifdef __arm64__
asm
volatile
(
"mov x0,#26\n"
"mov x1,#31\n"
"mov x2,#0\n"
"mov x3,#0\n"
"mov x16,#0\n"
"svc #128\n"
)
;
#endif
下面几种可能在实际中用到的比较少,不过也可以尝试一下。
SIGSTOP
通过捕获系统
SIGSTOP
信号来判断。
dispatch_
source
_t
source
= dispatch_
source
_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGSTOP, 0, dispatch_get_main_queue());
dispatch_
source
_
set
_event_handler(
source
, ^{
NSLog(@
"SIGSTOP!!!"
);
exit
(0);
});
dispatch_resume(
source
);
网上看到的,未广泛测试~
task_get_exception_ports
获取异常端口:
struct
macosx_exception_info{
exception_mask_t
masks[EXC_TYPES_COUNT];
mach_port_t
ports[EXC_TYPES_COUNT];
exception_behavior_t
behaviors[EXC_TYPES_COUNT];
thread_state_flavor_t
flavors[EXC_TYPES_COUNT];
mach_msg_type_number_t
cout
;
};
struct
macosx_exception_info *info =
malloc
(
sizeof
(
struct
macosx_exception_info));
task_get_exception_ports(mach_task_self(),
EXC_MASK_ALL,
info->masks,
&info->
cout
,
info->ports,
info->behaviors,
info->flavors);
for
(
uint32_t
i =
0
; i < info->
cout
; i ++){
if
(info->ports[i] !=
0
|| info->flavors[i] == THREAD_STATE_NONE){
NSLog(@
"debugger detected via exception ports (null port)!\n"
);
}
}
isatty
if
(isatty(
1
)) {
NSLog
(
@"Being Debugged isatty"
);
}
ioctl
if
(!ioctl(
1
, TIO
CGWINSZ
)) {
NSLog
(
@"Being Debugged ioctl"
);
}
反反调试(Tweak)
了解了几种不同的反调试的方法,那么就可以根据几种常用的反调试方法来反反调试。
这里主要针对
ptrace
、
sysctl
、
syscall
来反反调试,做法就很简单了,hook函数,判断参数,返回结果。
#import <substrate.h>
#import <sys/sysctl.h>
static
int
(*orig_ptrace) (
int
request, pid_t pid, caddr_t addr,
int
data);
static
int
my_ptrace (
int
request, pid_t pid, caddr_t addr,
int
data){
if
(request ==
31
){
NSLog
(
@"[AntiAntiDebug] - ptrace request is PT_DENY_ATTACH"
);
return
0
;
}
return
orig_ptrace(request,pid,addr,data);
}
static
void
* (*orig_dlsym)(
void
* handle,
const
char
* symbol);
static
void
* my_dlsym(
void
* handle,
const
char
* symbol){
if
(strcmp(symbol,
"ptrace"
) ==
0
){
NSLog
(
@"[AntiAntiDebug] - dlsym get ptrace symbol"
);
return
(
void
*)my_ptrace;
}
return
orig_dlsym(handle, symbol);
}
static
int
(*orig_sysctl)(
int
* name, u_int namelen,
void
* info, size_t * infosize,
void
* newinfo, size_t newinfosize);
static
int
my_sysctl(
int
* name, u_int namelen,
void
* info, size_t * infosize,
void
* newinfo, size_t newinfosize){
int
ret = orig_sysctl(name,namelen,info,infosize,newinfo,newinfosize);
if
(namelen ==
4
&& name[
0
] ==
1
&& name[
1
] ==
14
&& name[
2
] ==
1
){
struct
kinfo_proc *info_ptr = (
struct
kinfo_proc *)info;
if
(info_ptr && (info_ptr->kp_proc.p_flag & P_TRACED) !=
0
){
NSLog
(
@"[AntiAntiDebug] - sysctl query trace status."
);
info_ptr->kp_proc.p_flag ^= P_TRACED;
if
((info_ptr->kp_proc.p_flag & P_TRACED) ==
0
){
NSLog
(
@"[AntiAntiDebug] trace status reomve success!"
);
}
}
}
return
ret;
}
static
void
* (*orig_syscall)(
int
code, va_list args);
static
void
* my_syscall(
int
code, va_list args){
int
request;
va_list newArgs;
va_copy(newArgs, args);
if
(code ==
26
){
request = (
long
)args;
if
(request ==
31
){
NSLog
(
@"[AntiAntiDebug] - syscall call ptrace, and request is PT_DENY_ATTACH"
);
return
nil;
}
}
return
(
void
*)orig_syscall(code, newArgs);
}
%ctor{
MSHookFunction((
void
*)MSFindSymbol(NULL,
"_ptrace"
),(
void
*)my_ptrace,(
void
**)&orig_ptrace);
MSHookFunction((
void
*)dlsym,(
void
*)my_dlsym,(
void
**)&orig_dlsym);
MSHookFunction((
void
*)sysctl,(
void
*)my_sysctl,(
void
**)&orig_sysctl);
MSHookFunction((
void
*)syscall,(
void
*)my_syscall,(
void
**)&orig_syscall);
NSLog
(
@"[AntiAntiDebug] Module loaded!!!"
);
}
反反调试(lldb)
通过
lldb
下断点,然后修改参数,或者直接返回也可以达到反反调试的效果。不过这里讲的是通过
python
脚本把过程自动化的一种方法。
为了方便直接使用
facebook
的
chisel
来增加脚本。
新建一个文件夹,然后使用
commandsDirectory = os.path.
join
(lldbHelperDir,
'文件名'
)
loadCommandsInDirectory(commandsDirectory)
加载即可。
下面是
python
代码:
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
反反调试脚本,过了反调试后记得:
aadebug -d
否则会很卡,如果有定时器定时检测,建议写tweak
"""
import
lldb
import
fblldbbase
as
fb
import
fblldbobjcruntimehelpers
as
objc
def
lldbcommands
():
return
[
AMAntiAntiDebug()
]
class
AMAntiAntiDebug
(fb.FBCommand):
def
name
(self):
return
'aadebug'
def
description
(self):
return
"anti anti debug ptrace syscall sysctl"
def
options
(self):
return
[
fb.FBCommandArgument(short=
'-d'
, long=
'--disable'
, arg=
'disable'
, boolean=
True
, default=
False
, help=
'disable anti anti debug.'
)
]
def
run
(self, arguments, options):
if
options.disable:
target = lldb.debugger.GetSelectedTarget()
print
"anti anti debug is disabled!!!"
else
:
self.antiPtrace()
self.antiSyscall()
self.antiSysctl()
print
"anti anti debug finished!!!"
def
antiPtrace
(self):
ptrace = lldb.debugger.GetSelectedTarget().BreakpointCreateByName(
"ptrace"
)
if
is64Bit():
ptrace.SetCondition(
'$x0==31'
)
else
:
ptrace.SetCondition(
'$r0==31'
)
ptrace.SetScriptCallbackFunction(
'sys.modules[\''
+ __name__ +
'\'].ptrace_callback'
)
self.ptrace = ptrace
def
antiSyscall
(self):
syscall = lldb.debugger.GetSelectedTarget().BreakpointCreateByName(
"syscall"
)
if
is64Bit():
syscall.SetCondition(
'$x0==26 && *(int *)$sp==31'
)
else
:
syscall.SetCondition(
'$r0==26 && $r1==31'
)
syscall.SetScriptCallbackFunction(
'sys.modules[\''
+ __name__ +
'\'].syscall_callback'
)
self.syscall = syscall
def
antiSysctl
(self):
sysctl = lldb.debugger.GetSelectedTarget().BreakpointCreateByName(
"sysctl"
)
if
is64Bit():
sysctl.SetCondition(
'$x1==4 && *(int *)$x0==1 && *(int *)($x0+4)==14 && *(int *)($x0+8)==1'
)
else
:
sysctl.SetCondition(
'$r1==4 && *(int *)$r0==1 && *(int *)($r0+4)==14 && *(int *)($r0+8)==1'
)
sysctl.SetScriptCallbackFunction(
'sys.modules[\''
+ __name__ +
'\'].sysctl_callback'
)
self.sysctl = sysctl
def
antiExit
(self):
self.exit = lldb.debugger.GetSelectedTarget().BreakpointCreateByName(
"exit"
)
exit.SetScriptCallbackFunction(
'sys.modules[\''
+ __name__ +
'\'].exit_callback'
)
#暂时只考虑armv7和arm64
def
is64Bit
():
arch = objc.currentArch()
if
arch ==
"arm64"
:
return
True
return
False
def
ptrace_callback
(frame, bp_loc, internal_dict):
print
"find ptrace"
register =
"x0"
if
not
is64Bit():
register =
"r0"
frame.FindRegister(register).value =
"0"
lldb.debugger.HandleCommand(
'continue'
)
def
syscall_callback
(frame, bp_loc, internal_dict):
print
"find syscall"
#不知道怎么用api修改sp指向的内容QAQ
lldb.debugger.GetSelectedTarget().GetProcess().SetSelectedThread(frame.GetThread())
if
is64Bit():
lldb.debugger.HandleCommand(
'memory write "$sp" 0'
)
else
:
lldb.debugger.HandleCommand(
'register write $r1 0'
)
lldb.debugger.HandleCommand(
'continue'
)
def
sysctl_callback
(frame, bp_loc, internal_dict):
module = frame.GetThread().GetFrameAtIndex(
1
).GetModule()
currentModule = lldb.debugger.GetSelectedTarget().GetModuleAtIndex(
0
)
if
module == currentModule:
print
"find sysctl"
register =
"x2"
if
not
is64Bit():
register =
"r2"
frame.FindRegister(register).value =
"0"
lldb.debugger.HandleCommand(
'continue'
)
def
exit_callback
(frame, bp_loc, internal_dict):
print
"find exit"
lldb.debugger.GetSelectedTarget().GetProcess().SetSelectedThread(frame.GetThread())
lldb.debugger.HandleCommand(
'thread return'
)
lldb.debugger.HandleCommand(
'continue'
)
总结
反调试的方式有多种,可以通过:
ebugserver *:
1234
-x
auto
/path/to/executable
启动附加,然后断点测试。
本文代码地址:
有问题或者有更好的方法欢迎一起探讨。