22、Tweak原理及部分逆向防护

编程入门 行业动态 更新时间:2024-10-24 22:22:24

22、Tweak原理及部分逆向<a href=https://www.elefans.com/category/jswz/34/1753663.html style=防护"/>

22、Tweak原理及部分逆向防护

一、Tweak原理

1.1 Tweak产物.dylib

  • 执行make命令时,在 .theos的隐藏目录中,编译出obj/debug目录,包含 arm64、arm64e两种架构,同时生成readbadges.dylib动态库

  • 在arm64、arm64e目录下,有各自架构的readbadges.dylib,而debug目录下的readbadges.dylib,是一个胖二进制文件
file readbadges.dylib
Mach-O universal binary with 2 architectures: [arm64:Mach-O 64-bit dynamically linked shared library arm64Mach-O 64-bit dynamically linked shared library arm64] [arm64e:Mach-O 64-bit dynamically linked shared library arm64eMach-O 64-bit dynamically linked shared library arm64e]
readbadges.dylib (for architecture arm64):    Mach-O 64-bit dynamically linked shared library arm64
readbadges.dylib (for architecture arm64e):    Mach-O 64-bit dynamically linked shared library arm64e

Tweak的编译产物是动态库,将其注入的方式有两种:

  • 修改MachO文件的Load Commands,注入 LC_LOAD_DYLIB(XXX),然后根据路径找到动态库.这种方式对程序的污染比较严重,容易被开发者检测出来
  • 通过DYLD_INSERT_LIBRARIES环境变量,插入动态库

Tweak插件,使用的是方式二,因为程序没有被污染.在MachO中,并没有找到LC_LOAD_DYLIB(XXX)

1.2 Tweak packages分析

执行make package命令时,在packages目录中,生成.deb文件.每执行一次打包命令,都会生成一个新的.deb文件

.deb 格式类似于 .ipa格式

  • .ipa包通过AppStore安装,将.ipa包中的App包装到设备中
  • .deb包通过Cydia安装,将 .deb包中的动态库安装到设备中
    • data.tar.lzma解压后就是动态库

  • 执行 make install命令时,在.deb包中的动态库,会被安装到设备的 /Library/MobileSubstrate/DynamicLibraries目录中
  • 以相同的名称,分别存储 .dylib和 .plist 文件
    • .dylib为动态库,而 .plist,记录 .dylib所依附的App包名

二、 DYLD_INSERT_LIBRARIES防护

  • 当前官方的dyld源码中关于DYLD_INSERT_LIBRARIES的解释
DYLD_INSERT_LIBRARIES是一个以冒号分隔的附加动态库列表,要在程序中指定的动态库之前加载。
相反,如果您的目标是替换通常会加载的库,请使用 DYLD_LIBRARY_PATH 或者 DYLD_FRAMEWORK_PATH 代替。
  • 在早期的dyld源码中,有进程限制的判断.一旦符号条件,使用 DYLD_INSERT_LIBRARIES环境变量插入的动态库将被清空

  • 打开dyld-519.2.2源码
  • 搜索DYLD_INSERT_LIBRARIES
  • 进入dyld.cpp文件,来到5907行

    • DYLD_INSERT_LIBRARIES 为NULL的判断
  • 这段代码的上面,来到5692行

    • 判断进程限制, 符合条件,调用pruneEnvironmentVariables 方法,清空插入的动态库
  • 一旦插入的动态库被清空,意味着越狱插件将会全部失效.如果我们找到进程限制的开启条件,并将其使用在项目中,相当于对越狱插件进行了防护
  • 找到 processIsRestricted 设置为 true 的代码

    • 判断条件有两个,分别是 issetugid 和 hasRestrictedSegment 两个函数
    • issetugid函数,无法在上架的App中设置,放弃使用
    • hasRestrictedSegment函数,判断主程序的MachO是否受限,可以使用
  • 进入 hasRestrictedSegment 函数

    • 传入主程序的Header
    • 读取 segment, 如果为 __RESTRICT 段
    • 读取 segment,如果为 __restrict 节
    • 如果都存在,返回 true, 表示进程限制
  • __RESTRICT 段防护
    • 在项目中, 添加 __RESTRICT段, __restrict节,开启进程限制,对越狱插件进行防护
  • 搭建App项目,命名: AntiTweak
    • 打开ViewController.m ,写入以下代码: 点击屏幕即退出App
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {exit(0);
}
    • 进程限制,是早期dyld源码中的逻辑,在低系统下才能生效
  • 搭建 Tweak插件,附加 AntiTweak应用
    • 打开 Tweak.xm 文件,写入Hook代码
#import <UIKit/UIKit.h>%hook ViewController
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {NSLog(@"touchesBegan hooked");
}
%end
    • 运行插件项目,启动应用,开启控制台查看手机日志,touchesBegan方法被插件HOOK.点击屏幕,不会闪退.
  • 为AntiTweak项目,添加 __RESTRICT段, __restrict节
    • 在Build Setting的Other Linker Flags,加入以下设置
-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null
    • 编译项目,查看MachO文件

    • 成功插入 __RESTRICT 段, __restrict节
  • 低系统版本iOS手机,运行项目,点击屏幕闪退,说明插入的动态库已经被清空,越狱插件全部失效
  • 这种防护手段,在早期系统中比较有效.但在iOS11及更高系统中,dyld源码发生变化,这种方式已失去作用

针对上面这种防护手段,可以通过修改MachO破解

  • 在老系统的越狱设备上,遇到使用此方法防护的应用,导致我们的越狱插件无法使用,可以通过修改MachO文件破解防护
    • 使用MachOView打开MachO文件

    • 修改Data值,将 72改为73,52改为53.只在以前的数值上替换,位置不要改变

    • 当MachO文件修改后,使用重签名安装应用,此时 __RESTRICT段和 __restrict节已经不存在了,进程限制不会启动,越狱插件可正常使用.

三、 使用dyld源码防护

如果是自己的App,我们开启了进程限制,如果禁止攻击者的肆意修改呢?

  • 借鉴dyld的代码,循环读取segment和section, 如果缺少 __RESTRICT段或 __restrict节.说明我们的防护代码被人篡改
  • 用刚才的AntiTweak项目,将dyld中的代码迁移到项目中,
  • 打开ViewController.m文件,写入以下代码
    • 导入需要用到的头文件
#import <mach-o/loader.h>
#import <mach-o/dyld.h>
    • 添加需要用到的宏定义
#if __LP64__#define macho_header                mach_header_64#define LC_SEGMENT_COMMAND          LC_SEGMENT_64#define LC_SEGMENT_COMMAND_WRONG    LC_SEGMENT#define LC_ENCRYPT_COMMAND          LC_ENCRYPTION_INFO#define macho_segment_command       segment_command_64#define macho_section               section_64
#else#define macho_header                mach_header#define LC_SEGMENT_COMMAND          LC_SEGMENT#define LC_SEGMENT_COMMAND_WRONG    LC_SEGMENT_64#define LC_ENCRYPT_COMMAND          LC_ENCRYPTION_INFO_64#define macho_segment_command       segment_command#define macho_section               section
#endif
    • 添加hasRestrictedSegment函数, 循环读取segment和section. 如果缺少 __RESTRICT段或 __restrict节,返回false
static bool hasRestrictedSegment(const struct macho_header *mh) {const uint32_t cmd_count = mh->ncmds;const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(struct macho_header));const struct load_command* cmd = cmds;for (uint32_t i = 0; i < cmd_count; ++i) {switch (cmd->cmd) {case LC_SEGMENT_COMMAND: {const struct macho_segment_command *seg = (struct macho_segment_command*)cmd;if (strcmp(seg->segname, "__RESTRICT") == 0) {const struct macho_section*const sectionsStart = (struct macho_section*)((char *)seg + sizeof(struct macho_segment_command));const struct macho_section*const sectionsEnd = &sectionsStart[seg->nsects];for (const struct macho_section*sect=sectionsStart; sect<sectionsEnd; ++sect) {if (strcmp(sect->sectname, "__restrict")==0) {return true;}}}}break;}cmd = (const struct load_command *)(((char *)cmd)+cmd->cmdsize);}return false;
}
    • 加入 load方法作为调用防护代码的时机
// 加入load方法,调用防护代码
+ (void)load {//获取当前运行程序的MachOstruct macho_header *mhmh = _dyld_get_image_header(0);if (hasRestrictedSegment(mhmh)) {NSLog(@"防护代码有效");} else {NSLog(@"被篡改");}
}
  • 修改 Other Linker Flags 中的配置,模拟MachO被篡改
-Wl,-sectcreate,__SESTRICT,__sestrict,/dev/null
    • 运行项目,输出以下结果
2023-04-15 00:12:39.528419+0800 AntiTweak[47753:1566353] 被篡改
  • 注意:
    • 当检测到MachO被篡改,不要使用痕迹明显的代码进行防护,例如: exit(0).此类代码相当于记号,让攻击者很容易找到防护的位置和逻辑
    • 高明的防护手段,应该让攻击者不易察觉,在不知不觉中被系统屏蔽封杀

四、白名单检测

进程限制的防护手段,仅低版本系统有效.对于高版本系统的防护,我们可以自制白名单进行检测.

4.1 获取正常设备上的白名单

  • 延用AntiTweak项目: 整理出App依赖库的白名单
  • 打开ViewController.m文件,写入以下代码:
+ (void)load {//获取镜像文件个数uint32_t intCount = _dyld_image_count();for (int intIndex = 0; intIndex < intCount; intIndex++) {// 获取指定索引的镜像名称const char *strName = _dyld_get_image_name(intIndex);printf("%s",strName);}
}
  • 在未越狱设备上,运行项目,遍历出所有image名称

  • 打印结果,相当于一份白名单.如果App运行时,加载了白名单以外的动态库,该库很可能是被第三方注入的.将输出结果拷贝出来存放

4.2 检测越狱设备上注入的动态库

  • 获取思路为: 在load方法中,循环遍历依赖的动态库.如果动态库是当前的MachO文件,或者包含在白名单中,属于合法库,直接跳过.否则,将其打印.
+ (void)load {//获取镜像文件个数uint32_t intCount = _dyld_image_count();for (int intIndex = 0; intIndex < intCount; intIndex++) {// 获取指定索引的镜像名称const char *strName = _dyld_get_image_name(intIndex);// 0位置是当前的 MachO文件if (intIndex == 0 || strstr(strList, strName)) {continue;}printf("注入的动态库: %s\n",strName);}
}
    • 在越狱设备上运行项目,输出很多白名单以外的动态库,其中包含自制的TouchHookDemo插件

使用白名单做防护,需要注意:

  • 在不同系统下运行项目,整理出尽可能完善的白名单.
  • 检测到白名单以外的动态库,不要直接处理.建议先收集数据,如果此动态库是因为不同系统差异造成的,将其补充到白名单中.如果确定是恶意注入,再做处理.
  • 白名单列表,由服务器下发,或者将逻辑直接做到服务器

白名单写在客户端的弊端:

  • 白名单的字符串,位于MachO的常量区,容易被攻击者发现并HOOK篡改.
  • 当系统更新,可能会出现白名单以外的依赖库,老版本App将无法使用.

五、ptrace

5.1 ptrace概述

  • App可以被lldb动态调试,因为App被设备中的debugserver附加,它会跟踪我们的应用进程(trace process), 而这一过程利用的就是ptrace 函数
  • ptrace是系统内核函数,它可以决定应用能否被debugserver附加.如果我们在项目中,调用ptrace函数,将程序设置为拒绝附加,即可对lldb动态调试进行有效的防护.
  • ptrace在iOS系统中,无法直接使用,需要导入头文件
  • ptrace函数的定义:
int ptrace(int _request, pid _pid, caddr_t _addr, int _data);
    • request: 请求ptrace执行的操作
    • pid: 目标进程的ID
    • addr: 目标进程的地址值,和request参数有关
    • data: 根据request的不同而变化.如果需要向目标进程中写入数据,data存放的是需要写入的数据.如果从目标进程中读数据,data将存放返回的数据
  • 上面的函数怎么来的?
    • 不妨回想一下,当初编译llvm源码或者Swift源码的时候,编译出附带的lldb源码,tools中就有debugserver源码,下边是编译Swift5.3.1的源码截图

    • 打开编译运行解决了lldb_codesign问题后,就可以得到自己编译的debugserver MachO了,并且可以直接调试源码,附加Mac端的进程

5.2 ptrace防护应用

  • 搭建App项目AntiDebug
  • 导入MyPtraceHeader.h头文件.
  • 写入调用代码
#import "ViewController.h"
#import "MyPtraceHeader.h"
@interface ViewController ()
@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];ptrace(PT_DENY_ATTACH, 0, 0, 0);
}
    • 使用Xcode运行项目,启动后立即退出.使用ptrace设置为拒绝附加,只能手动启动App,也就是说,用户在使用App时,不会有任何影响.一旦被debugserver附加,就会闪退.
  • 如果在越狱环境,手动对App进行debugserver附加呢?
    • 找到AntiDebug进程,手动对App进行debugserver附加
Holothurian6P:~ root# ps -A | grep AntiDebug
48329 ??         0:00.23 /var/containers/Bundle/Application/3D3083F3-295D-4F55-83DB-40887FF4FF46/AntiDebug.app/AntiDebug
48336 ttys000    0:00.00 grep AntiDebug
Holothurian6P:~ root# debugserver localhost:22 -a 48329
debugserver-@(#)PROGRAM:LLDB  PROJECT:lldb-900.3.87for arm64.
Attaching to process 48329...
Segmentation fault: 11
    • 同样附加失败,无论以何种方式,都会被ptrace函数阻止.

5.3 ptrace防护破解

  • ptrace是系统内核函数,被开发者所熟知.ptrace的防护痕迹也很明显,手动运行程序正常,Xcode运行程序闪退
  • 我们在逆向一款App时,遇到上述情况,第一时间就会想到ptrace防护
  • 由于ptrace是系统函数,需要间接符号表,我们可以尝试下ptrace的符号断点

    • ptrace的断点命中,确定了对方的防护手段,接下来尝试破解
  • 采用AntiDebug项目,模拟应用重签名,注入动态库
  • 创建 Inject动态库,创建 InjectCode类
  • 在Inject动态类中,导入fishhook,导入MyPtraceHeader.h头文件
#import "InjectCode.h"
#import "MyPtraceHeader.h"
#import "fishhook.h"@implementation InjectCodeint (*sys_ptrace)(int _request, pid_t _pid, caddr_t _addr, int _data);int my_ptrace(int _request, pid_t _pid, caddr_t _addr, int _data){if (_request == PT_DENY_ATTACH) {return 0;}return sys_ptrace(_request,_pid,_addr,_data);
}
+ (void)load {struct rebinding rebPtrace;rebPtrace.name = "ptrace";rebPtrace.replacement = my_ptrace;rebPtrace.replaced    = (void *)&sys_ptrace;struct rebinding rebs[] = {rebPtrace};rebind_symbols(rebs, 1);
}@end
  • 在my_ptrace函数中,如果是 PT_DENY_ATTACH 宏定义值,直接返回,如果是其他类型,系统由特定的作用,需要执行ptrace原始函数.
  • 运行项目,进入lldb动态调试,ptrace防护破解成功

六、总结

Tweak原理

  • Tweak编译产物是动态库
  • 打包时,将动态库打包成 .deb 格式
  • 插件安装到 /Library/MobileSubstrate/DynamicLibraries目录中
    • 安装.dylib和 .plist文件
    • .plist记录 .dylib 所要附加的App包名
  • Tweak插件使用 DYLD_INSERT_LIBRARIES方式,插入动态库

DYLD_INSERT_LIBRARIES

  • 早期的dyld源码中,有进程限制的判断( processIsRestricted)
  • 启动进程限制,segment 存在 __RESTRICT段,section存在 __restrict节
  • 符合进程限制的条件,清空插入动态库,越狱插件失效

__RESTRICT段防护

  • 在Build Setting的 Other Linker Flags中配置
    • -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null
  • iOS11.0及更高系统,此防护无效

修改MachO破解__RESTRICT段防护

  • 使用MachOView打开MachO文件,修改Data段
  • 只在以前的数值上替换,不要对其增减,位数不要改变

使用dyld源码防护

  • 借鉴dyld源码,读取 segment和section.如果缺少 __RESTRICT段或 __restrict节,说明我们的防护代码被人篡改
  • 检测到程序被篡改,不要使用痕迹明显的代码进行防护,容易暴露
  • 尽量让攻击者在不知不觉中被系统屏蔽封杀

白名单检测

  • 遍历image名称
    • _dyld_image_count()
    • _dyld_get_image_name()
  • 在不同系统下运行项目,整理出尽可能完善的白名单
  • 检测到白名单以外的动态库,不要直接处理
  • 白名单列表,由服务端下发,或者将逻辑直接做到服务端

ptrace

  • 可阻止App被debugserver附加
  • 在 iOS系统中,无法直接使用,需要导入头文件
  • ptrace函数的定义
    • int ptrace(int _request, pid_t _pid, caddr_t _addr, int _data);
  • 破解ptrace
  • 防护效果: 手动运行程序正常,Xcode运行程序闪退
  • 使用ptrace符号断点试探
  • 使用fishhook对ptrace函数HOOK
  • 是PT_DENY_ATTACH宏定义值,直接返回.其他类型,执行原始函数

更多推荐

22、Tweak原理及部分逆向防护

本文发布于:2024-02-11 08:19:14,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1679999.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:防护   原理   Tweak

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!