iOS安全之防止调试与防止注入

学习逆向是不是目的,我们是想通过逆向的手段来达到加强我们App安全的目的,那么应该怎么做呢?

我们知道逆向的终极策略就是动态调试和动态库注入,那么我们能不能在这两个维度加强安全性呢。

一、防止调试

调试是通过ptrace进行附加,读取内存中的值,所以我们要能检测调试状态,拒绝调试器附加,那么首先我们要了解Xcode调试的原理,知其然知其所以然,才能从根源上解决问题。

1.1 Xcode调试原理

Xcodelldb之所以能调试App,是因为手机运行App,lldb会把调试指令发给手机的debugServer; debugServer是由Xcode第一次运行程序给安装到手机上。

Xcode上查看debugserver路径:Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/11.3/DeveloperDiskImage.dmg 找到DeveloperDiskImage.dmg里的debugserver

手机的根目录下的 /usr/bin debugserver里能找到,

那么debugserver如何调试App的呢?

debugserver是通过ptrace来监听31Attach附加进程进行的。

ptrace是系统函数,此函数提供一个进程去监听和控制另一个进程,并且可以检测被控制进程的内存和寄存器里面的数据。ptrace可以用来实现断点调试和系统调用跟踪。

1.2 如何使用ptrace

正常情况下的main.m文件的内容是

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

我们改成:

#import <UIKit/UIKit.h>
#import "AppDelegate.h"    
#import <dlfcn.h>
#import <sys/types.h>

typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data);
#if !defined(PT_DENY_ATTACH)
#define PT_DENY_ATTACH 31
#endif  // !defined(PT_DENY_ATTACH)

void disable_gdb() {
    void* handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);
    ptrace_ptr_t ptrace_ptr = dlsym(handle, "ptrace");
    ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);
    dlclose(handle);
}

int main(int argc, char *argv[])
{

#ifndef DEBUG 
    disable_gdb();
#endif


    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

除了使用ptrace,我们还可以使用syscall,及其他的方式

syscall(26, 31, 0,0,0);

使用syscall的代码如下:

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import <dlfcn.h>
#import <sys/types.h>

int main(int argc, char * argv[]) {
#ifndef DEBUG
    syscall(26, 31, 0,0,0);
#endif


    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

添加后的现象就是通过release模式连接手机后,会直接断开Xcode,就是防止Xcode进行调试

可以看到运行结束后,按钮直接变灰色:

都可以有效的预防其他人对我们App在release状态下进行动态调试。

二、防止动态库注入

其实越狱后能够注入代码就是通过CydiaSubstrate动态库,动态链接到我们App上的Mach-O文件上,这样程序执行的时候就会加载他们的目标动态库,进而实现对我们App的Hook,而前文刷趣头条的阅读金币也是通过这种方式进行的,所以我们在项目工程里设置一下参数,就可以防止动态库注入,注入后没有效果。

通过 Xcode 里的 Other Linker Flags 设置如下参数:

-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null

三、总结

其实即便我们把上边的防止调试与防止注入做到了,还是可以通过Hook C 语言及修改二进制的方式来破坏我们的防护策略,但是我们提高了逆向的成本。

攻防总是对立存在的,没有绝地的安全,所以我们要加强自身的安全性的策略,提高黑产逆向我们App的成本与难度,相对程度里就是安全的。

您的支持将鼓励我继续创作!