学习逆向是不是目的,我们是想通过逆向的手段来达到加强我们App安全的目的,那么应该怎么做呢?
我们知道逆向的终极策略就是动态调试和动态库注入,那么我们能不能在这两个维度加强安全性呢。
一、防止调试
调试是通过ptrace
进行附加,读取内存中的值,所以我们要能检测调试状态,拒绝调试器附加,那么首先我们要了解Xcode调试的原理,知其然知其所以然,才能从根源上解决问题。
1.1 Xcode调试原理
Xcode
的lldb
之所以能调试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
来监听31
的Attach
附加进程进行的。
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的成本与难度,相对程度里就是安全的。