0
点赞
收藏
分享

微信扫一扫

iOS 攻防(二)ptrace

松鼠树屋 2021-09-30 阅读 71

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

一、反调试ptrace

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

1.1 ptrace 头文件

  1. 直接创建一个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_ */
  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主程序本身)。

二、 破解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符号断点:

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

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

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

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

修改blr x8NOP

替换00 01 3F D61F 20 03 D5

修改后就变为NOP了:

再次运行就绕过了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 <sys/syscall.h>中有它的编号:

3.4 汇编ptrace破解

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

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

举报

相关推荐

0 条评论