iOS 攻防(二)ptrace

在破解一款App的时候,在实际破解之前肯定是在做调试。LLDB之所以能附加进程时因为debugserver,而debugserver附加是通过ptrace函数来trace process的。
ptrace是系统函数,此函数提供一个进程去监听和控制另一个进程,并且可以检测被控制进程的内存和寄存器里面的数据。ptrace可以用来实现断点调试和系统调用跟踪。

一、反调试ptrace

iOS#import 头文件不能直接导入,所以需要我们自己导出头文件引入调用。当然也可以声明ptrace函数直接调用。

1.1 ptrace 头文件

  1. 直接创建一个macOS程序导入#import 头文件,点进去拷贝生成一个.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 
#include 

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_ */
  1. 直接声明函数:
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就好了。_pid0表示当前进程。这里不传递任何数据。

分别在以下方法中调用

  1. load方法中调用:
+ (void)load {
    ptrace(PT_DENY_ATTACH, 0, 0, 0);
}
  1. constructor中调用:
__attribute__((constructor)) static void entry() {
    ptrace(PT_DENY_ATTACH, 0, 0, 0);
}
  1. 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);
}
  1. didFinishLaunchingWithOptions中调用:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    ptrace(PT_DENY_ATTACH, 0, 0, 0);
    return YES;
}

123情况下Xcode启动调试后调试直接断开,App能正常操作不能调试。4在调试情况下App直接闪退,正常打开没问题。同时调用的情况下以第一次为准。

也就是说 :ptracemain函数之后调用App会直接闪退,main以及之前调用会停止进程附加,以第一次调用为准。正常打开App没有问题,只影响LLDB调试。

通过上面的验证说明在程序没有加载的时候调用ptrace会设置一个标志,后续程序就不会被附加了,如果在已经被附加了的情况下调用ptrace会直接退出(因为这里ptrace附加传递的pid0主程序本身)。

PT_DENY_ATTACH
This request is the other operation used by the traced process; it allows a process that is not currently being traced to deny future traces by its parent. All other arguments are ignored. If the process is currently being traced, it will exit with the exit status of ENOTSUP; otherwise, it sets a flag that denies future traces. An attempt by the parent to trace a process which has set this flag will result in a segmentation violation in the parent.
原文地址
ENOTSUP含义如下:
define ENOTSUP 45 //Operation not supported
之前在手机端通过debugserver附加程序直接报错11,定义如下:
PT_DETACH 11 // stop tracing a process

二、 破解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,那么自己先Hookptrace。调用的时候直接调用自己存储的地址就可以了。我们可以在自己的项目中增加一个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符号断点:

image.png

这个时候可以看到是didFinishLaunchingWithOptions中调用了ptrace_p函数:
Hopper打开MachO文件找到didFinishLaunchingWithOptions方法:

image.png

然后一直点下去找到ptrace_p是属于Inject.framework的:

image.png

.appFrameworks中找到Inject.frameworkHopper打开,可以看到_rebind_symbols,上面的参数是ptrace

image.png

这里我们可以直接修改ptrace让先Hook的变成另外一个函数,但是有风险点是App内部调用ptrace_p的时候如果没有判断空就crash了。如果判断了可以这么处理。
还有另外一个方式是修改didFinishLaunchingWithOptions代码中的汇编,修改blr x8NOP这样就绕过了ptrace_p的调用。

修改blr x8NOP

image.png

替换00 01 3F D61F 20 03 D5

image.png

修改后就变为NOP了:

image.png

再次运行就绕过了ptrace_p的调用。这里不Hook didFinishLaunchingWithOptions的原因是它内部可能还调用其它方法。

3.3 汇编调用ptrace

既然二进制能被修改那么怎么能不暴露ptraceHook呢?
答案是采用汇编:

__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 中有它的编号:

image.png

3.4 汇编ptrace破解

分析找到svc #0x80修改为nop就可以了,抖音的防护就是通过汇编调用的。

参考:
http://www.45fan.com/article.php?aid=18070914507358856843184900

你可能感兴趣的:(iOS 攻防(二)ptrace)