mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-25 04:17:17 +00:00
feature: App 逆向防护
This commit is contained in:
@@ -270,7 +270,7 @@ int hooked_sysctl(int *name, u_int namelen, void *info, size_t *infosize, void *
|
||||
|
||||
|
||||
|
||||
## sysctl 安全版本
|
||||
## sysctl 安全性改进
|
||||
|
||||
修改思路参考上面的 ptrace,知道 fishhook 的原理,绕开懒加载符号表,绕开 dyld 修正符号和填充地址这个过程。
|
||||
|
||||
@@ -284,10 +284,75 @@ int hooked_sysctl(int *name, u_int namelen, void *info, size_t *infosize, void *
|
||||
|
||||
|
||||
|
||||
## syscall 简易版本
|
||||
|
||||
`int syscall(int, ...) `,`syscall`函数是一种用于调用系统调用的方法。系统调用是用户空间程序请求操作系统内核服务的一种机制。
|
||||
|
||||
在用户空间和内核空间之间,有一个叫做 Syscall (系统调用, system call )的中间层,是连接用户态和内核态的桥梁。这样即提高了内核的安全型,也便于移植,只需实现同一套接口即可。Linux系统,用户空间通过向内核空间发出 syscall,产生软中断,从而让程序陷入内核态,执行相应的操作。对于每个系统调用都会有一个对应的系统调用号,比很多操作系统要少很多。
|
||||
|
||||
引入头文件 `#import <sys/syscall.h>`
|
||||
|
||||
```c++
|
||||
syscall(26, 31, 0, 0);
|
||||
// 等价于 syscall(SYS_ptrace, PT_DENY_ATTACH, 0, 0);
|
||||
```
|
||||
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/SyscallParam.png" style="zoom:30%" />
|
||||
|
||||
syscall 调用的时候第一个参数是调用函数的名称,后面的参数是调用函数的参数。
|
||||
|
||||
|
||||
|
||||
## syscall 安全吗
|
||||
|
||||
syscall 本质也是系统函数,最后还是躲不开 fishhook 的追杀。所以是不安全的。有没有什么办法可以解决?
|
||||
|
||||
这里就不再去写一遍 fishhook 的代码了,很重复...
|
||||
|
||||
|
||||
|
||||
## syscall 安全性改进
|
||||
|
||||
- 隐藏符号,还原符号
|
||||
|
||||
- 使用 dlopen、dlsym 的方式,找到 syscall 符号的地址
|
||||
- syscall 发起系统调用,调用 sysctl 能力
|
||||
- GCD 定时器检测,判断是否处于调试模式
|
||||
- 如果处于调试模式,调用汇编 quit_process 结束进程
|
||||
|
||||
可以看到:即使 fishhook hook 了 ptrace、sysctl 绕过 hook,但是这种方式还是可以对非法调试 App 的行为进行了保护,立马会结束进程。
|
||||
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/HiddenSyscallSymbol.png" style="zoom:30%" />
|
||||
|
||||
|
||||
|
||||
## svc 调用
|
||||
|
||||
SVC指令在ARM体系中被归于异常处理类指令,该指令能允许用户程序调用内核,其格式如下:
|
||||
|
||||
```c++
|
||||
SVC{cond} #imm // Supervisor call, allows application program to call the kernel (EL1)
|
||||
```
|
||||
|
||||
传统 arm中使用 svc 0 表示中断,在 xnu 中使用的是 svc 0x80。具体的看下面的例子
|
||||
|
||||
|
||||
|
||||
## 更安全的版本
|
||||
|
||||
### 隐藏符号名称
|
||||
|
||||
iOS 中常量字符串可以在 Mach-O 文件的 `__TEXT` 段中找到。如果加密的 salt、一些支付的 key、地图的 key,直接明文存储很不安全,一个可能的方案是采用 c 字符脱符号,比如通过下面的方式获取字符串
|
||||
|
||||
```objective-c
|
||||
char name[] = {'s', 'y', 's', 'c', 'a', 'l', 'l', '\0'};
|
||||
NSString *funcName = [NSString stringWithUTF8String:name];
|
||||
```
|
||||
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/StringValueVisableInMachO.png" style="zoom:30%" />
|
||||
|
||||
|
||||
|
||||
更安全的是不让分析者在 MachO 中显示的看到 ptrace、sysctl 符号名称。所以采用异或运算一个固定的 key,再根据指针指向字符串初始值,再次异或,得到原始字符串。
|
||||
|
||||
隐藏 ptrace 符号名称的方法,如下所示
|
||||
@@ -376,8 +441,8 @@ asm volatile(
|
||||
"mov x1,#31\n"
|
||||
"mov x2,#0\n"
|
||||
"mov x3,#0\n"
|
||||
"mov x16,#0\n" //这里就是syscall的函数编号
|
||||
"svc #0x80\n" //这条指令就是触发中断(系统级别的跳转)
|
||||
"mov x16,#0\n" // 这里就是syscall的函数编号
|
||||
"svc #0x80\n" // 这条指令就是触发中断(系统级别的跳转)
|
||||
);
|
||||
```
|
||||
|
||||
@@ -385,12 +450,12 @@ asm volatile(
|
||||
|
||||
```assembly
|
||||
asm volatile(
|
||||
"mov x0,#31\n" //参数1
|
||||
"mov x1,#0\n" //参数2
|
||||
"mov x2,#0\n" //参数3
|
||||
"mov x3,#0\n" //参数4
|
||||
"mov x16,#26\n"//中断根据x16 里面的值,跳转ptrace
|
||||
"svc #0x80\n" //这条指令就是触发中断去找x16执行(系统级别的跳转!)
|
||||
"mov x0,#31\n" // 参数1
|
||||
"mov x1,#0\n" // 参数2
|
||||
"mov x2,#0\n" // 参数3
|
||||
"mov x3,#0\n" // 参数4
|
||||
"mov x16,#26\n"// 中断根据 x16 里面的值,跳转 ptrace
|
||||
"svc #0x80\n" // 这条指令就是触发中断去找 x16 执行
|
||||
);
|
||||
```
|
||||
|
||||
@@ -401,7 +466,7 @@ static __attribute__((always_inline)) void quit_process () {
|
||||
#ifdef __arm64__
|
||||
asm(
|
||||
"mov x0,#0\n"
|
||||
"mov x16,#1\n" //这里相当于 Sys_exit,调用exit函数
|
||||
"mov x16,#1\n" // 这里相当于 Sys_exit,调用exit函数
|
||||
"svc #0x80\n"
|
||||
);
|
||||
return;
|
||||
@@ -409,7 +474,7 @@ static __attribute__((always_inline)) void quit_process () {
|
||||
#ifdef __arm__
|
||||
asm(
|
||||
"mov r0,#0\n"
|
||||
"mov r16,#1\n" //这里相当于 Sys_exit
|
||||
"mov r16,#1\n" // 这里相当于 Sys_exit
|
||||
"svc #80\n"
|
||||
);
|
||||
return;
|
||||
@@ -424,6 +489,72 @@ static __attribute__((always_inline)) void quit_process () {
|
||||
|
||||
|
||||
|
||||
## 一些其他的思路
|
||||
|
||||
### 符号混淆
|
||||
|
||||
做 iOS 开发的同学,在类名、方法名等命名上,都会做到见名知意,这点在开发阶段、日常维护阶段是好事情。但是站在黑客和攻击者角度来看的话,这对他们来说也是一件好事,但对 App 的安全来讲就是一件坏事。所以需要做**符号混淆**。
|
||||
|
||||
在静态分析应用的时候,常常会使用 `class-dump` 导出应用的头文件,通过头文件中的函数名或变量名 猜测这些函数的功能,然后进行 `Hook` 动态分析窥探大概逻辑。如果让这些方法名、变量名、类名从名称上看没有任何意义,那么就能从一定程度干扰攻击者猜测,这叫做代码混淆
|
||||
|
||||
|
||||
|
||||
如果一个登陆注册如下所示(伪代码):
|
||||
|
||||
```objective-c
|
||||
@interface LoginViewController: UIViewController
|
||||
- (void)handleLoginAction;
|
||||
@end
|
||||
```
|
||||
|
||||
这样的代码上传到 App Store 后,攻击者利用 class-dump 还原后,还是很清晰的,见名知意,一下子就可以判断这是登陆事件的处理函数。如果对符号进行混淆,如下所示
|
||||
|
||||
```objective-c
|
||||
@interface $38wiewh81_Controller: UIViewController
|
||||
- (void)0jjd1;
|
||||
@end
|
||||
```
|
||||
|
||||
攻击者看到这样的符号,无疑会增大破解难度,至少不会像以前的一样,代码做到裸奔。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 动态库白名单检测
|
||||
|
||||
除了保护应用中的关键代码,还可以通过代码检测应用中动态库是否是合法的。无论是越狱环境还是非越狱环境,如果要入侵除了修改二进制就是注入动态库,所以可以写段逻辑判断 App 中除了我们自己项目中的动态库,是否还存在入侵的动态库。
|
||||
|
||||
通过 dyld API 函数获取应用中的动态库名称,把这些字符串名称合并作为一个白名单,如果发现动态库不在白名单中,则结束进程。
|
||||
|
||||
```objective-c
|
||||
const char *whitstr= "";
|
||||
void checkWhiteStr(){
|
||||
uint32_t count= _dyld_image_count();
|
||||
for(int i=1;i<count;i++){
|
||||
const char* dyname=_dyld_get_image_name(i);
|
||||
//printf(dyname);
|
||||
if(!strstr(whitstr, dyname)){ // 不在白名单中
|
||||
quit_process();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
当然,更严谨的做法就是 App 启动完成,主页加载显示完毕后,拉取接口,服务端告诉当前版本的 App 有哪些库,App 内存保存这些数据,GCD 定时器检测,发现有库不在该白名单内存中,则结束进程。
|
||||
|
||||
服务端白名单数据怎么来?App 打出 release 模式的包,然后运行上述代码,包库的名称数组,上传到服务端。
|
||||
|
||||
|
||||
|
||||
### 逻辑混淆
|
||||
|
||||
一些核心逻辑还是有必要混淆的。编译工程时使用这个自定义的 `OLLVM` 工具可以编写 `Pass` 来混淆 `IR`。
|
||||
|
||||
具体查看 [obfuscator](https://github.com/obfuscator-llvm/obfuscator) 这个 Repo。具体使用这里不展开。
|
||||
|
||||
|
||||
|
||||
完整代码可以这里:
|
||||
|
||||
- [AppHook](https://github.com/FantasticLBP/BlogDemos/tree/master/AppHook)
|
||||
|
||||
Reference in New Issue
Block a user