Tweak原理、越狱防护
Tweak修改系统行为
-
分析
//系统 /System/Library/CoreServices/SpringBoard.app/SpringBoard //进程依附 $ cycript -p SpringBoard //查看当前VC $ QLCurrentVC() // 查看层级 $ UIApp.keyWindow.recursiveDescription().toString //查找badge //发现SBIconParallaxBadgeView类型 //筛选 $ choose(SBIconParallaxBadgeView) //隐藏 $ #0x1431d7060.hidden=0 //成功~ //Tweak思路:暴力解决直接干掉SBIconParallaxBadgeView的init方法
-
编写Tweak
$ nic.pl NIC 2.0 - New Instance Creator ------------------------------ [1.] iphone/activator_event [2.] iphone/application_modern [3.] iphone/cydget [4.] iphone/flipswitch_switch [5.] iphone/framework [6.] iphone/ios7_notification_center_widget [7.] iphone/library [8.] iphone/notification_center_widget [9.] iphone/preference_bundle_modern [10.] iphone/tool [11.] iphone/tweak [12.] iphone/xpc_service Choose a Template (required): 11 Project Name (required): badgeTweak Package Name [com.yourcompany.badgetweak]: com.bear.bage Author/Maintainer Name [Qionglin Fu]: //默认就是com.apple.springboard [iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: [iphone/tweak] List of applications to terminate upon installation (space-separated, '-' for none) [SpringBoard]: Instantiating iphone/tweak in badgetweak/... Done. //Makefile文件 export THEOS_DEVICE_IP=127.0.0.1 export THEOS_DEVICE_PORT=12345 //Tweak.xm文件编写logos %hook SBIconParallaxBadgeView -(id)init { return nil; } %end
-
打包安装
$ make $ make package $ make install //看效果~~~ //修改xm文件快速安装 $ make package;make install //也可以直接写成脚本
//每次生成的Tweak都需要编辑Makefile文件 //加入环境变量即可 $ vim ~/.bash_profile //添加 export THEOS_DEVICE_IP=127.0.0.1 export THEOS_DEVICE_PORT=12345 //立即执行 $ source ~/.bash_profile
Tweak原理
//查看隐藏文件
$ ls -a
- make的时候出现一堆文件
- .theos/obj/debug/badgeTweak.dylib这个动态库,就是将我们的makefile编译生成的
- 如何证明注入?
- makepage.此时多了deb包
- install时通过端口号SSH映射安装
- 解压出来就是我们的动态库。放在/Library/MobileSubstrate/DynamicLibraries/
- plist文件就是外面创建的.theos/obj/debug/plist
- 动态库能够被加载,而且调用?
- 如果一个应用的二进制被修改了,注定要重签名
- dyld调用的:
- 注入到某个进程
- 插入到某个进程
- 查看MachO文件不存在我们的dylib文件,通过plist里面的BundleID找到那个进程,
DYLD_INSERT_LIBRARIES
-
//加载任意插入的动态库 if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) { //遍历所有lib for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) //加载 loadInsertedDylib(*lib); } //不能修改判断条件。系统的也会改变 //复写DYLD_INSERT_LIBRARIES这个变量,注入动态库在app里面hook。这个代码在操作系统启动的。没有意义 #if __MAC_OS_X_VERSION_MIN_REQUIRED //判断processIsRestricted进程是不是拒绝加载、插入 //processIsRestricted为真,后面的插入动态库的事情就不再有了 if ( gLinkContext.processIsRestricted ) { pruneEnvironmentVariables(envp, &apple); // 直接将插入的动态库移除 setContext(mainExecutableMH, argc, argv, envp, apple); } //让这两个函数返回值为真 //issetugid()在上架的app里面不能设置 //唯一的机会在hasRestrictedSegment() if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) { gLinkContext.processIsRestricted = true; } #if __MAC_OS_X_VERSION_MIN_REQUIRED //遍历一个段 static bool hasRestrictedSegment(const macho_header* mh) { //把当前进程的MachO的头给我、指针 const uint32_t cmd_count = mh->ncmds; const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); //拿到load_command段 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; //比较一下。如果段的名称segname是__RESTRICT 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 = §ionsStart[seg->nsects]; //在遍历里面的每一个值 for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { //判断sectionName是不是__restrict if (strcmp(sect->sectname, "__restrict") == 0) //如果有。return true return true; } } } break; } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } return false; }
修改RESTRICT段防护
- Build Setting/Other Linker Flags下添加-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null
- 创建一个section
- 查看MachO多了一个RESTRICT section
- 此时防护的是越狱环境下的注入
利用二进制修改破坏防护
- 无法修改other linke flag,通过MachO修改RESTRICT段
-
我们编译的工程依赖的动态库在LoadCommands,而RESTRICT段拒绝临时插入的动态库
- 修改二进制
- 工具:Synalyze It!Pro
- com+F 搜索restrict。随便修改一个字段即可
- 破坏二进制后记得重签名
使用dyld源码防护
#import "ViewController.h"
#import <mach-o/loader.h>
#import <mach-o/dyld.h>
// ARM and x86_64 are the only architecture that use cpu-sub-types
#define CPU_SUBTYPES_SUPPORTED ((__arm__ || __arm64__ || __x86_64__) && !TARGET_IPHONE_SIMULATOR)
#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
@interface ViewController ()
@end
@implementation ViewController
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;
printf("seg name: %s\n", seg->segname);
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 = §ionsStart[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;
}
+(void)load
{
//DYLD启动APP的时候,最先加载的是自己的 MachO (通过LLDB:image list 查看角标)
struct mach_header * header = _dyld_get_image_header(0);
if (hasRestrictedSegment(header)) {
NSLog(@"防护没变化!!");
}else{
NSLog(@"改我二进制干啥??");
exit(0);
}
}