在破解一款App
的时候,在实际破解之前肯定是在做调试。LLDB
之所以能附加进程时因为debugserver
,而debugserver
附加是通过ptrace
函数来trace process
的。
ptrace
是系统函数,此函数提供一个进程去监听和控制另一个进程,并且可以检测被控制进程的内存和寄存器里面的数据。ptrace
可以用来实现断点调试和系统调用跟踪。
一、反调试ptrace
在iOS
中#import <sys/ptrace.h>
头文件不能直接导入,所以需要我们自己导出头文件引入调用。当然也可以声明ptrace
函数直接调用。
1.1 ptrace 头文件
- 直接创建一个
macOS
程序导入#import <sys/ptrace.h>
头文件,点进去拷贝生成一个.h
文件就可以了:
/*
* Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
/*-
* Copyright (c) 1984, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ptrace.h 8.2 (Berkeley) 1/4/94
*/
#ifndef _SYS_PTRACE_H_
#define _SYS_PTRACE_H_
#include <sys/appleapiopts.h>
#include <sys/cdefs.h>
enum {
ePtAttachDeprecated __deprecated_enum_msg("PT_ATTACH is deprecated. See PT_ATTACHEXC") = 10
};
#define PT_TRACE_ME 0 /* child declares it's being traced */
#define PT_READ_I 1 /* read word in child's I space */
#define PT_READ_D 2 /* read word in child's D space */
#define PT_READ_U 3 /* read word in child's user structure */
#define PT_WRITE_I 4 /* write word in child's I space */
#define PT_WRITE_D 5 /* write word in child's D space */
#define PT_WRITE_U 6 /* write word in child's user structure */
#define PT_CONTINUE 7 /* continue the child */
#define PT_KILL 8 /* kill the child process */
#define PT_STEP 9 /* single step the child */
#define PT_ATTACH ePtAttachDeprecated /* trace some running process */
#define PT_DETACH 11 /* stop tracing a process */
#define PT_SIGEXC 12 /* signals as exceptions for current_proc */
#define PT_THUPDATE 13 /* signal for thread# */
#define PT_ATTACHEXC 14 /* attach to running process with signal exception */
#define PT_FORCEQUOTA 30 /* Enforce quota for root */
#define PT_DENY_ATTACH 31
#define PT_FIRSTMACH 32 /* for machine-specific requests */
__BEGIN_DECLS
int ptrace(int _request, pid_t _pid, caddr_t _addr, int _data);
__END_DECLS
#endif /* !_SYS_PTRACE_H_ */
- 直接声明函数:
int ptrace(int _request, pid_t _pid, caddr_t _addr, int _data);
-
_request
:要处理的事情 -
_pid
:要操作的进程 -
_addr
和_data
:取决于_pid
参数,要传递的数据地址和数据本身。
1.2 ptrace调用
//告诉系统当前进程拒绝被debugserver附加
ptrace(PT_DENY_ATTACH, 0, 0, 0);
//ptrace(31, 0, 0, 0);
PT_DENY_ATTACH
表示拒绝附加,值为31
。如果仅仅是声明函数就传31
就好了。_pid
为0
表示当前进程。这里不传递任何数据。
分别在以下方法中调用
-
load
方法中调用:
+ (void)load {
ptrace(PT_DENY_ATTACH, 0, 0, 0);
}
-
constructor
中调用:
__attribute__((constructor)) static void entry() {
ptrace(PT_DENY_ATTACH, 0, 0, 0);
}
-
main
函数中调用:
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
ptrace(PT_DENY_ATTACH, 0, 0, 0);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
-
didFinishLaunchingWithOptions
中调用:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
ptrace(PT_DENY_ATTACH, 0, 0, 0);
return YES;
}
1
、2
、3
情况下Xcode
启动调试后调试直接断开,App
能正常操作不能调试。4
在调试情况下App
直接闪退,正常打开没问题。同时调用的情况下以第一次为准。
也就是说 :ptrace
在main
函数之后调用App
会直接闪退,main
以及之前调用会停止进程附加,以第一次调用为准。正常打开App
没有问题,只影响LLDB
调试。
通过上面的验证说明在程序没有加载的时候调用ptrace
会设置一个标志,后续程序就不会被附加了,如果在已经被附加了的情况下调用ptrace
会直接退出(因为这里ptrace
附加传递的pid
是0
主程序本身)。
二、 破解ptrace
ptrace
的特征:附加不了、Xcode运行闪退/停止附加、使用正常。
既然ptrace
可以组织调试,那么我们只要Hook
了这个函数绕过PT_DENY_ATTACH
的调用就可以了。首先想到的就是fishhook
。
#import "fishhook.h"
int (*ptrace_p)(int _request, pid_t _pid, caddr_t _addr, int _data);
int hp_ptrace(int _request, pid_t _pid, caddr_t _addr, int _data){
if (_request != 31) {//不是拒绝附加
return ptrace_p(_request, _pid, _addr, _data);
}
return 0;
}
void hp_hook_ptrace() {
struct rebinding ptrace_rb;
ptrace_rb.name ="ptrace";
ptrace_rb.replacement = hp_ptrace;
ptrace_rb.replaced = (void *)&ptrace_p;
struct rebinding bds[] = {ptrace_rb};
rebind_symbols(bds, 1);
}
+ (void)load {
hp_hook_ptrace();
}
这样就能够进行附加调试了。
三、防止ptrace被破解
3.1 提前Hook防止ptrace被Hook
既然ptrace
能够被Hook
,那么自己先Hook
住ptrace
。调用的时候直接调用自己存储的地址就可以了。我们可以在自己的项目中增加一个Framework
。这个库在Link Binary With Libraries
中尽可能的靠前。这与dyld
加载动态库的顺序有关。
具体可以参考这个案例
这样就可以不被ptrace
Hook
了。代码逻辑和1.2
中相同,只不过调用要换成ptrace_p
。
记的头文件中导出ptrace_p
:
CF_EXPORT int (*ptrace_p)(int _request, pid_t _pid, caddr_t _addr, int _data);
创建一个Monkey
工程,将3.1
生成的.app
包拖入工程重签名,这个时候主程序通过调用ptrace
已经不能阻止我们调试了,但是调用ptrace_p
的地方Monkey
Hook
不到了。
3.2 修改二进制破解提前Hook ptrace
在Monkey
的工程中打ptrace
符号断点:
这个时候可以看到是didFinishLaunchingWithOptions
中调用了ptrace_p
函数:
用Hopper
打开MachO
文件找到didFinishLaunchingWithOptions
方法:
然后一直点下去找到ptrace_p
是属于Inject.framework
的:
在.app
的Frameworks
中找到Inject.framework
用Hopper
打开,可以看到_rebind_symbols
,上面的参数是ptrace
:
这里我们可以直接修改ptrace
让先Hook
的变成另外一个函数,但是有风险点是App
内部调用ptrace_p
的时候如果没有判断空就crash
了。如果判断了可以这么处理。
还有另外一个方式是修改didFinishLaunchingWithOptions
代码中的汇编,修改blr x8
为NOP
这样就绕过了ptrace_p
的调用。
修改blr x8
为NOP
:
替换00 01 3F D6
为1F 20 03 D5
:
修改后就变为NOP
了:
再次运行就绕过了ptrace_p
的调用。这里不Hook
didFinishLaunchingWithOptions
的原因是它内部可能还调用其它方法。
3.3 汇编调用ptrace
既然二进制能被修改那么怎么能不暴露ptrace
的Hook
呢?
答案是采用汇编:
__attribute__((constructor)) void entry() {
#ifdef DEBUG
//
#else
#ifdef __arm__
__asm__(
"mov r0,#0x1F\n"
"mov r1,#0x0\n"
"mov r2,#0x0\n"
"mov r12,#0x1A\n"
"svc #0x80");
#endif
#ifdef __arm64__
__asm__("mov X0, #0x1A\n"
"mov X1, #0x1F\n"
"mov X2, #0x0\n"
"mov X3, #0x0\n"
"mov X16,#0x0\n"
"svc #0x80");
#endif
}
#endif
这里的1A
就是26
,也就是ptrace
,在#import <sys/syscall.h>
中有它的编号:
3.4 汇编ptrace破解
分析找到svc #0x80
修改为nop
就可以了,抖音的防护就是通过汇编调用的。
参考:
http://www.45fan.com/article.php?aid=18070914507358856843184900