写点什么

Tweak 原理与越狱防护

发布于: 2020 年 06 月 22 日

本文不包含具体编写插件的内容,只是从Tweak的原理去探究怎么防护插件的注入。

生成一个Tweak插件

有两种方式生成Tweak插件,一种是MonkeyDev,一种是Theos。

安装MonkeyDev

MonkeyDev安装与说明https://github.com/AloneMonkey/MonkeyDev

Monkey的使用





  • MonkeyApp 重签名app,可以选择一个脱壳的ipa直接跑起来debug

  • MonkeyPod 通过pod集成插件

  • Command-line 命令行工具

  • Tweak 越狱插件



我们选择Logos Tweak 来创建插件

项目中xm就是需要编写的hook文件、mm生成的目标文件

plist是注入目标的配置,截图上默认的是springboard应用



build settings里的设置,有三个空的需要填一下,如果是ssl登录,则不需要填写密码。最后一个是安装插件的时候,杀掉的目标进程。



安装Theos

Thoes的安装与使用https://github.com/theos/theos





编译生成Tweak



从编译产物中,可看出是一个dylib动态库文件

原理

Tweak通过dyld insert librarys 环境变量 插入到系统里



插入动态库的核心源码

// load any inserted libraries(越狱的环境都是用这个!)
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
loadInsertedDylib(*lib);
}


通过dyld源码查看到环境变量sEnv.DYLDINSERTLIBRARIES不为空的时候,会插入动态库,所以我们继续看源码环境变量相关的部分

在insert之前pruneEnvironmentVariables这行代码表示移除相关的环境变量,因此我们只要关注!gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache这个判断为true就可以

if ( !gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache ) {
pruneEnvironmentVariables(envp, &apple);
// set again because envp and apple may have changed or moved
setContext(mainExecutableMH, argc, argv, envp, apple);
}


继续查看源码看到hasRestrictedSegment这个函数,Mach-O里如果包含了Restricted段就可以是值为true.

// support chrooting from old kernel
bool isRestricted = false;
bool libraryValidation = false;
// any processes with setuid or setgid bit set or with __RESTRICT segment is restricted
// issetugid 这个函数不能在上架的app使用
if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) {
isRestricted = true;
}
bool usingSIP = (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0);
uint32_t flags;
if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) {
// On OS X CS_RESTRICT means the program was signed with entitlements
if ( ((flags & CS_RESTRICT) == CS_RESTRICT) && usingSIP ) {
isRestricted = true;
}
// Library Validation loosens searching but requires everything to be code signed
if ( flags & CS_REQUIRE_LV ) {
isRestricted = false;
libraryValidation = true;
}
}
gLinkContext.allowAtPaths = !isRestricted;
gLinkContext.allowEnvVarsPrint = !isRestricted;
gLinkContext.allowEnvVarsPath = !isRestricted;
gLinkContext.allowEnvVarsSharedCache = !libraryValidation || !usingSIP;
gLinkContext.allowClassicFallbackPaths = !isRestricted;
gLinkContext.allowInsertFailures = false;




static bool hasRestrictedSegment(const macho_header* mh)
{
const uint32_t cmd_count = mh->ncmds;
const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(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;
//dyld::log("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 = &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;
}

防护

通过上边的源码,得到了一个结论,只要在Mach-O里包含了__RESTRICT段就能防止insert library。图里在otherLinker里增加了几个参数



增加完后,用MachOview查看内容,Mach-O里成功的增加了__RESTRICT段,防护住了应用插件





再突破

修改Mach-O,使用MachOview,修改完成后保存。然后需要重签名后运行,重签名后,bundleId变了,可以通过其他方式监测Hook情况



再防护

应用程序内校验Mach-O情况,通过上边的源码hasRestrictedSegment函数,去查看是否__RESTRICT段被破坏。



后续

hook hasRestrictedSegment 方法,继续突破与防护,永无止境。

发布于: 2020 年 06 月 22 日阅读数: 76
用户头像

还未添加个人签名 2017.11.10 加入

还未添加个人简介

评论

发布
暂无评论
Tweak原理与越狱防护