应用安全

这段时间 iOS12 越狱工具发布了,屁颠屁颠的去研究了一键砸壳,昨天忽然想起来之前公司的产品被人破解,拿到了关键信息进而薅羊毛的事。那时的解决方案是检测异常请求和独立出一个加密framework。现在想想,这些措施都太简单或者不够高效,还需要更进一步研究应用的安全问题。

纲目

1.被破解的危害

  • 被人逆向分析通信协议、API、核心算法等;
  • 篡改IPA、植入广告和木马;
  • 重打包并发布盗版应用;
  • 破解内购;

2.防范

在这方面我是真没有经验,于是抱着学习的态度去查看了相关的论坛和资料,学学别人的经验,这里做一个简单的记录~

2.1.检测是否越狱(念茜)

这是最常见的被动防御方法,比如当应用启动时,先检测设备是否已经越狱,如果已越狱则禁止应用启动;在用户发起内购支付时,如果发现设备已越狱,则直接拒绝内购请求并给出相应的提示。根据我的测试,部落冲突游戏就是通过闪退直接禁止越狱设备启动自己,而腾讯旗下的产品基本上都会在越狱设备发起支付时给出提示,别踩音乐块儿也是如此。

以下摘自念茜的博客:

  • 检测越狱文件或目录

越狱后的设备中会有相关的文件:

1
2
3
4
5
/Applications/Cydia.app
/Library/MobileSubstrate/MobileSubstrate.dylib
/bin/bash
/usr/sbin/sshd
/etc/apt

检测方法:

1
2
3
4
5
6
+(BOOL)isJailbroken{
if ([[NSFileManager defaultManager] fileExistsAtPath:@"/Applications/Cydia.app"]){
return YES;
}
// ...
}
  • 检测 Cydia 的 URL scheme

越狱后的设备一般都会安装 Cydia,这里可以通过检测其 URL Scheme 来反推设备是否越狱。这个方法在 iOS9 之后会因为权限问题而出现问题,所以你需要配置好LSApplicationQueriesSchemes白名单。

1
2
3
if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://package/com.example.package"]]){
NSLog(@"Device is jailbroken");
}
  • 读取应用列表,看有无权限:

在未越狱的情况下,用户是没有权限查看设备上的某些目录的,也就无法知晓有哪些已安装的应用,除非你像某些三方市场一样用苹果的私有API,但这会导致上架被拒。所以,我们可以利用这一特性来检测设备是否越狱:

1
2
3
4
5
6
if ([[NSFileManager defaultManager] fileExistsAtPath:@"/User/Applications/"]){
NSLog(@"Device is jailbroken");
NSArray *applist = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"/User/Applications/"
error:nil];
NSLog(@"applist = %@",applist);
}
  • 用 stat 系列函数检测 Cydia 等工具:
1
2
3
4
5
6
7
8
9
#import <sys/stat.h>  

void checkCydia(void)
{
struct stat stat_info;
if (0 == stat("/Applications/Cydia.app", &stat_info)) {
NSLog(@"Device is jailbroken");
}
}
  • 检测 DYLD_INSERT_LIBRARIES 环境变量

DYLD_INSERT_LIBRARIES是一个环境变量,dyld动态链接器在加载二进制文件时会检测这个变量,如果有配置过则会加载其指向的动态库,从而实现动态注入并将程序的内存dump出来,这正是越狱设备中调试时常用的方法。所以我们可以检测此环境变量来确定设备是否越狱:

1
2
3
4
5
void printEnv(void)
{
char *env = getenv("DYLD_INSERT_LIBRARIES");
NSLog(@"%s", env);
}

未越狱设备返回结果是null,已越狱设备返回自己的配置。

2.2.代码 & 逻辑混淆

  • 关键字:类名、属性、方法名等;
  • 常量:URL、字符串;
  • 将原逻辑拆分成各种怪癖语法;

代码混淆和逻辑混淆,其本质上只是在应用编译时将指定的字符串或逻辑进行混淆替换,从而增加应用破解后阅读源码的难度。具体的混淆方法,我之前的文章中也已经介绍过,这里不再赘述~

2.3.__RESTRICT反动态库注入

restrict,C语言中的一种类型限定符(Type Qualifiers),用于告诉编译器,对象已经被指针所引用,不能通过除该指针外所有其他直接或间接的方式修改该对象的内容。

restrict是c99标准引入的,它只可以用于限定和约束指针,并表明指针是访问一个数据对象的唯一且初始的方式。即它告诉编译器,所有修改该指针所指向内存中内容的操作都必须通过该指针来修改,而不能通过其它途径(其它变量或指针)来修改;这样做的好处是,能帮助编译器进行更好的优化代码,生成更有效率的汇编代码。如 int *restrict ptr, ptr 指向的内存单元只能被 ptr 访问到,任何同样指向这个内存单元的其他指针都是未定义的,直白点就是无效指针。

示例:

1
2
3
int ar[10];
int * restrict restar=(int *)malloc(10*sizeof(int));
int *par=ar;

这里说明restar是访问由malloc()分配的内存的唯一且初始的方式。par就不是了。

通过在 Xcode 里的 Other Linker Flags 设置参数,可以防止应用的二进制文件被注入 dylib(仅限于iOS 10 以下系统)。dylib 无法注入,也就意味着没办法用cycript动态调试进程

  • Other Linker Flags 参数:
1
-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null

参考博文:

2.4.反ptrace调试

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

参考博文:https://www.jianshu.com/p/ebdfb0a25c85

2.5.第三方加固

缺点:

  • 安装包变大;
  • 运行速度变慢;

第三方加固提供商:

  • 网易易盾
  • 百度加固
  • 腾讯乐固
  • 通付盾

根据应用对安全等级的要求衡量成本~~


相关参考:

#©念茜

#©反注入

#©iosre论坛snakeninny

#©pewpewthespells-blocking_code_injection


应用安全
https://davidlii.cn/2019/05/01/safeapp.html
作者
Davidli
发布于
2019年5月1日
许可协议