不越狱给iOS App装Tweak/插件:LiveContainer环境介绍与Tweak编写

news/2025/11/6 18:31:25/文章来源:https://www.cnblogs.com/yyfll/p/19196763

自iOS 16.5.1之后,Arm64e设备的越狱一直处于停滞状态,没有新的进展。
不少越狱开发者也逐渐脱离越狱开发,转向JIT等新方式面对iOS的新环境。

iOS App相关的Tweak/插件在越狱鼎盛时代生机勃勃,Flex3的注入和Cydia等包管理商店里的注入层出不穷,如今想起来还觉得是个好时代。

那么如今iOS环境下,还可以写Tweak,还可以注入dylib,还可以Hook吗?

可以的!

项目LiveContainer正为我们提供了这样一个平台。

这几周编写时发现很难找到类似的文章,所以打算在这里记录一下。

LiveContainer环境

LiveContainer为未越狱iOS用户提供了一个不受签名限制,可自由安装App的美妙平台。
并且LiveContainer自带TweakLoader.dylib,可以在App启动时自动加载Tweak进行注入;
同时还内置ElleKit,Tweak不必自己链接CydiaSubstrate或其他Hook框架,直接使用即可。

选择直接将Tweak打包到IPA中侧载虽然也是个选择,但是3 Apps限制还是太难受;
借助LiveContaier,可以安装任意数量的应用,并且这种Tweak载入方式可以让使用者只需要更新IPA,不需要在每次更新时都反反复复给IPA注入一次,开发者也可以省心不少。

虽然LiveContainer可能会遇到一些兼容性问题,但是其带来的利好实在是太过庞大,以至于这些兼容性问题在我看来皆可以忽略。

为LiveContainer环境开发Tweak

插件Android有,iOS没有一直是普遍存在的情况,在网上冲浪时看到LiveContainer的Tweak注入功能后,我就决心打破这一僵局。
这里为大家说明一下在LiveContainer下的Tweak需要注意的部分:

开发环境依然可以选用Theos,并且不需要使用jailed模版(或者说我没用过不知道啊)。

因为LiveContainer环境的特殊性,我们Hook所需要的函数(MSHookFunction/MSHookMessageEx)均已经由TweakLoader加载了,我们的Tweak在加载时,只需要使用dlsym找到对应的函数即可,不需要再链接库。

如果你的Tweak需要链接第三方库,尽量让LiveContainer去加载,然后找标志,而不要自己链接,否则很容易出现各种问题。

MSHookFunction是针对C/C++等无法通过别的方式劫持(fishhook提供了一种以篡改Mach-O导入表Hook函数的方案,但是该方案局限性大,只适用于动态链接库的函数,所以这里不再提及),必须通过篡改可执行文件__TEXT内存段进行Hook的函数,需要绕过Apple内存保护。(这点我们稍后会提及)

MSHookMessageEx则是针对Objective-C函数,通过劫持ObjC的方法信息Hook函数。不需要绕过内存保护,在非越狱环境不需要任何额外权限,可直接运行。

// 这里演示如何使用TweakLoader已经加载好的两个Hook函数
#import <Foundation/Foundation.h>
#import <dlfcn.h>// 定义将要用到的两个Hook函数的函数指针
typedef void (*MSHookFunction_t)(void *symbol, void *hook, void **old);
static MSHookFunction_t MSHookFunction_p = nullptr;typedef void (*MSHookMessageEx_t)(Class _class, SEL message, IMP hook, IMP *old);
static MSHookMessageEx_t MSHookMessageEx_p = nullptr;// 在初始化时通过dlsym找到函数
__attribute__((constructor))
static void tweakConstructor() {MSHookFunction_p = (MSHookFunction_t)dlsym(RTLD_DEFAULT, "MSHookFunction");if (!MSHookFunction_p) {NSLog(@"Failed to find MSHookFunction.");return;}MSHookMessageEx_p = (MSHookMessageEx_t)dlsym(RTLD_DEFAULT, "MSHookMessageEx");if (!MSHookMessageEx_p) {NSLog(@"Failed to find MSHookMessageEx.");return;}
}

也正是因为这种导入方式,我们不能使用Logos的快捷语法,否则Theos会自动链接CydiaSubstrate库,导致加载库时找不到路径。

最好是老实使用Objective-C/C++语法,手动进行Hook。(其实也不是太复杂)

// 使用MSHookMessageEx钩取一个ObjC函数(以[AppController didFinishLaunchingWithOptions:]为例)// 先定义函数指针和Hook后函数
static BOOL (*original_didFinishLaunchingWithOptions)(id self, SEL _cmd, UIApplication *application, NSDictionary *launchOptions) = nullptr;
BOOL hooked_didFinishLaunchingWithOptions(id self, SEL _cmd, UIApplication *application, NSDictionary *launchOptions) {// 你自己的实现...// 如果要调用原函数:original_didFinishLaunchingWithOptions(self, _cmd, application, launchOptions);
}// 初始化函数或你自己的某个函数:
void hookAppControllerFunc() {Class appController = NSClassFromString(@"AppController");// 找不到Class的时候应当通过NSLog等方式输出错误信息,并避免下一步执行if (!appController) return;MSHookMessageEx_p(targetClass,@selector(application:didFinishLaunchingWithOptions:),(IMP)hooked_didFinishLaunchingWithOptions,(IMP *)&original_didFinishLaunchingWithOptions);
}

绕过内存保护做Inline Hook MSHookFunction

上面我们提到,对于使用C/C++及其他语言编写的程序,并没有ObjC这样不需要高权限的Hook方案,需要直接对内存打补丁。

以往越狱环境下,Tweak一般有权限将R-X内存页设置为RW-再设置回R-X
Inline Hook需要这一过程去写入跳转。

但别忘了,我们现在是在非越狱环境,可没有那个权限。
那么我的Unity IL2CPP应用和其他那些非ObjC应用怎么办?

当然是有办法滴!

iOS 15 ~ iOS 18:JIT

在iOS 26以前的版本中,通过启用JIT就能让应用拿到这个权限,允许程序修改内存后再将内存设置为可执行。

即在LiveContainer中,为你的应用开启“带JIT启动”即可破解这个难关!

如果是侧载应用,在SideStore里对应用启用JIT即可!

虽然要多折腾那么一点,但是除了iOS 17.0 ~ iOS 17.3.1的JIT启用有些烦人,都蛮简单的,在设备上就可以完成。

iOS 26+:JIT+SkitDebug(挂载Geode.js)

iOS 26 RC版本开始,Apple加强了其保护策略,即使应用启用了JIT,也没有权限将已经改为RW的内存改回RX。
导致程序在运行到对应Hook片段时,会因没有执行权限而直接崩溃。

寻找破局之法

我想到SkitDebug对于UTM等虚拟机应用的支持,是通过JS脚本实现的。
那我是否可以借助这个机制,让我的程序可以做Inline Hook呢?

于是我到SkitDebug的源代码中寻找,发现了下面这个文件Geode.js的这个片段:

... else if (brkImmediate === 0x70) { // patching instructslog(`Received command to patch instructions (0x70)`);let x2Match = /02:(?<reg>[0-9a-f]{16});/.exec(brkResponse);let x2 = x2Match ? x2Match.groups['reg'] : null;if (!x1 || !x2) {log(`Missing x1 or x2 for function patching`);continue;}let destAddr = x0Num;let srcAddr = x1Num;let size = x2 ? littleEndianHexStringToNumber(x2) : 10n;log(`Patching: dest=0x${destAddr.toString(16)}, src=0x${srcAddr.toString(16)}, size=0x${size.toString(16)}`);// Unsure exactly why, but anything over 4 MB freezes the app for some reason, so we will set a soft limitif (size > 0x400000n) {log(`Size too large (0x${size.toString(16)}), skipping`);let pcPlus4 = numberToLittleEndianHexString(pcNum + 4n);let pcPlus4Response = send_command(`P20=${pcPlus4};thread:${tid};`);log(`pcPlus4Response = ${pcPlus4Response}`);validBreakpoints++;continue;}try {// m (read) = `m${curPointer.toString(16)},<size>`// M (write) = `M${curPointer.toString(16)},<size>:<your hex instructions goes here>`const CHUNK_SIZE = 0x4000n; // 16 KBfor (let i = 0n; i < size; i += CHUNK_SIZE) {let chunkSize = i + CHUNK_SIZE <= size ? CHUNK_SIZE : size - i;let readAddr = srcAddr + i;let writeAddr = destAddr + i;let readRes = send_command(`m${readAddr.toString(16)},${chunkSize.toString(16)}`);if (readRes && readRes.length > 0) {let writeResponse = send_command(`M${writeAddr.toString(16)},${chunkSize.toString(16)}:${readRes}`);if (writeResponse !== "OK") {log(`Write failed at offset ${i.toString(16)}`);break;}}if (Number(i / CHUNK_SIZE) % 10 === 0) {log(`Progress: 0x${i.toString(16)}/0x${size.toString(16)}`);}}log(`Memory write completed!`);} catch (e) {log(`Memory write failed: ${e}`);}let pcPlus4 = numberToLittleEndianHexString(pcNum + 4n);let pcPlus4Response = send_command(`P20=${pcPlus4};thread:${tid};`);log(`pcPlus4Response = ${pcPlus4Response}`);validBreakpoints++;
}

这个片段展示了SkitDebug接收到应用程序发出的BRK #0x70断点,从x0x1寄存器中取出地址,x2寄存器中取出长度,依赖调试器实现高权限内存写入的过程。

这一过程可以完全绕开原本的实现途径,由具有高权限的调试器进行内存写入,不需要进行权限修改

通过这种方式,即使修改内存,也不会引起可感知的内存段权限变化!
(即RX内存写入后仍然为RX)

这不就把权限问题绕开了吗?真巧妙!

随后我查看了这段代码的源头Geode(即Geode SDK iOS),找到了对应的代码片段
dyld_bypass_validation_txm.m

...// ldr x8, value; br x8; value: .ascii "\x41\x42\x43\x44\x45\x46\x47\x48"
static char patch[] = { 0x88, 0x00, 0x00, 0x58, 0x00, 0x01, 0x1f, 0xd6, 0x1f, 0x20, 0x03, 0xd5, 0x1f, 0x20, 0x03, 0xd5, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 };// x0 (dest), x1 (src), x2 (bytes)
__attribute__((noinline,optnone,naked))
void BreakJITWrite(void* dest, void* src, size_t bytes) {asm("brk #0x70 \n""ret");
}static bool redirectFunction(char* name, void* patchAddr, void* target) {if (has_txm()) {BreakJITWrite(patchAddr, patch, sizeof(patch));}...
}

看来Geode SDK似乎利用BreakJITWrite写入了一个无条件跳转,进行函数重定向。
(下面的代码段显示Hook了dyld_mmapdyld_fcntl

这不正是我们要做的吗!?

有了方法,要怎么实现呢?

改写Dobby实现Inline Hook

我也尝试过自己造一个Hook框架,但是太过复杂,我的知识水平和时间都不足以实现这样一个东西。

那——有没有现成的轮子呢?有的!

先后考察了祖师爷CydiaSubstrateSubstituteElleKitlitehook之后,我认为改写起来最简便、功能最全的还是跨平台框架Dobby

那就动手吧!

直接搜索mach_vm寻找内存操作函数,可以发现最底层进行内存操作的是code-patch-tool-darwin.cc文件中的DobbyCodePatch函数

由于代码比较长,原始代码这里就不再粘贴了,总之修改起来非常简单:
保留跨页处理,后面所有的mach_vm操作都可以不要,直接用BreakJITWrite即可

如果彻底不要兼容性(毕竟我只是拿来给iOS 26以上用,以下的有内置的ElleKit)
修改后大概是这个样子:

// code-patch-tool-darwin.cc => DobbyCodePatch
// ...这里是 Cross over page 跨页逻辑部分...// 采用分块写入方式,每次写page_size以免卡死const size_t CHUNK_SIZE = page_size;size_t remaining = buffer_size;uint8_t *src_ptr = buffer;addr_t dest_addr_base = (addr_t)address;while (remaining > 0) {size_t this_chunk = remaining > CHUNK_SIZE ? CHUNK_SIZE : remaining;void *dest_chunk = (void *)(dest_addr_base + (buffer_size - remaining));void *src_chunk = (void *)src_ptr;BreakJITWrite(dest_chunk, src_chunk, this_chunk);src_ptr += this_chunk;remaining -= this_chunk;}ClearCache(address, (void *)((addr_t)address + buffer_size));return 0;
}

虽然可能线程不安全,但也没权限做成线程安全啊。

为iOS平台编译之后,导入LiveContainer,成了!

由于DobbyHook的声明和MSHookFunction一模一样,我们可以直接:

if (@available(iOS 26.0, *)) {MSHookFunction_p = (MSHookFunction_t)dlsym(RTLD_DEFAULT, "DobbyHook");
} else {MSHookFunction_p = (MSHookFunction_t)dlsym(RTLD_DEFAULT, "MSHookFunction");
}

LiveContainer是递归加载Dylib的,可以利用这一点让libdobby.dylib始终早于你的Tweak加载。

LiveContainer的Nightly Release版本已经支持为应用单独指定JIT启动脚本,只要选择Geode.js,配合上面魔改的libdobby.dylib,即可让你的Tweak在iOS 26正常Hook!

至此从iOS 15(LiveContainer最低支持版本)至iOS 26均可以进行Inline Hook。

写在最后

这些内容是我两周的从零开始开发所遇到的,关于Hook的所有问题的集合。

我这方面水平真的还是有所欠缺,如果有什么地方说得不对,还请各位见谅。

开发过程中,真的感受到iOS作为一个封闭系统,社区却有着震撼人心的蓬勃生机,有着各种奇思妙想,想尽办法将iOS给出的限制逐一打破。

写着、思考着,我也感受到了这种快乐,震撼于前辈开发者们的惊人智慧,为自己绞尽脑汁终于成功运行的东西而心花怒放。

只要愿意再深入那么一点点,庞大iOS社区所开拓的一个辽阔的世界就在你的面前。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/958004.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

课后作业2(异常处理)

Java项目异常处理实战指南:从规范到落地 在Java项目开发中,异常处理是保障系统健壮性的核心能力。一份优秀的异常处理方案能将故障排查时间缩短50%以上,同时提升系统可用性30%。但实际开发中,空catch块、滥用异常控…

Bigtop 从零开始搭建大数据集群

背景 公司目前在线上环境使用的是基于 HDP 2.6.3 搭建的大数据集群,在持续使用4年之后,是否要给集群做个升级成为了一个值得思考的问题 现在集群的 Hadoop 版本是 2.7.3,继续使用倒也没什么问题,但一些使用痛点还是…

chatgpt-to-md优化并重新复习

chatgpt-to-md优化并重新复习之前原本写的又重新改了改 [https://www.cnblogs.com/tokepson/p/19152535](记录 | 个人开发库推送至PyPi流程梳理(ChatGPT to Markdown 工具发布完整流程) )以上废话 总之因为发现只支持…

从零开始制作 MyOS(六)

从零开始制作 MyOS(六) 今天的任务是:添加异常处理代码——除零操作。 除零操作的过程概述: C代码中的除零代码被编译成汇编语言,然后CPU在执行的时候发现除数为0后,直接触发除零错误,然后保存上下文,关中断,…

2025年11月介电常数测试仪推荐厂家排行:应该如何选择靠谱供应商

在材料科学和电子工程领域,介电常数测试仪是评估材料电气性能的关键设备。随着技术的不断进步,市场上涌现出众多介电常数测试仪供应商。本文将基于2025年11月的市场情况,推荐一些值得信赖的介电常数测试仪厂家,并提…

2025年11月电阻率测试仪工厂推荐:北京冠测精电——信誉、口碑与售后的三重保障

在材料科学和电子工程领域,电阻率测试仪是不可或缺的重要设备。无论是研究新型材料的电学特性,还是确保产品质量,选择一台可靠的电阻率测试仪至关重要。2025年11月,如果您正在寻找一家信誉好、口碑好、售后好的电阻…

SaaS版MES系统PC端后台特性清单与设计说明

SaaS版MES系统PC端后台特性清单与设计说明pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "…

【2025臻选指南】酸角糕十大品牌深度解析:传承古法与现代创新的完美融合

【2025臻选指南】酸角糕十大品牌深度解析:传承古法与现代创新的完美融合寻味之旅:唤醒记忆中的自然酸甜 在追求健康饮食的今天,酸角糕这款承载着无数人童年记忆的传统零食,正以全新姿态重回大众视野。据中国休闲食…

2025年水上游乐及泳池设备标杆厂家推荐:山东汇川,室内水上乐园/儿童水上乐园/大型水上乐园/室内泳池/定制化服务引领行业新标​

随着休闲消费升级与全民健身理念普及,水上游乐设施与各类泳池需求持续攀升,2025 年市场规模预计稳步扩大。但行业快速发展也带来厂商设计能力、技术实力、服务质量参差不齐的问题,投资者在选购时往往面临场景适配、…

深入解析:开源 C++ QT QML 开发(十四)进程用途

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

各种扩展模块

各种扩展模块S7-1200 系列提供了各种模块和插入式板, 用于通过附加 I/O 或其它通信协议来扩展 CPU 的功能。 CM 通信模块 通信模块(CM)和通信处理器(CP)将增加 CPU 的通信选项,例如 PROFIBUS或 RS232/RS485 的连接性…

2025年冷冻式干燥机标杆厂家最新推荐:凌宇机械,冷冻式压缩空气干燥机/风冷高温冷冻式干燥机/水冷高温冷冻式干燥机/吸附式干燥机/以高效节能与全场景方案树立行业新标准

随着工业自动化程度的不断深化及企业对能耗管控、生产稳定性要求的日益提高,作为核心气源净化设备的压缩空气干燥机,其重要性愈发凸显。2025年,在制造业持续升级与“双碳”目标的双重驱动下,市场对干燥设备的节能效…

2025全日制艺考生文化课机构推荐榜:全日制艺考生文化课,小众优质机构精准适配需求

随着艺考生文化课要求逐步提高,“复习周期短、基础不均衡、需求个性化” 成为普遍困扰。为帮助艺考生筛选适配的学习平台,本文聚焦全日制教学模式,规模适中、专注度高的机构,其中深圳得到教育凭借扎实的教学体系位…

2025艺术涂料厂家推荐榜:进口艺术涂料、意大利进口艺术涂料、意大利艺术涂料厂家,装修选品不踩坑

在消费升级与家居美学需求提升的双重驱动下,艺术涂料凭借独特的装饰质感与环保性能,成为装修市场的热门选择。为帮助消费者精准筛选优质品牌,本文结合品牌底蕴、产品实力、市场口碑等维度,整理出 2025 年艺术涂料推…

2025济南艺考文化课培训推荐榜:艺考文化课培训,艺考文化课培训机构适配不同艺考生需求

艺考生专业统考结束后,文化课冲刺成为关键环节。为帮助艺考生精准选择适配机构,结合济南本地培训市场口碑、教学成效及学员反馈,整理出 2025 年优质艺考文化课培训机构推荐榜,兼顾基础巩固、技巧提升等多元需求。 …

For xinye666

#include <stdio.h> #include <iostream> #define int long long // 开 long long !!! #define con putchar_unlocked( ) #define ent putchar_unlocked(\n) #define op(c) putchar_unlocked(c) #define …

从手写周报到智能生成:PandaCoder如何让你的工作汇报效率提升10倍

从手写周报到智能生成:PandaCoder如何让你的工作汇报效率提升10倍"在我们的工作中,最浪费的不是机器的时间,而是人的时间。" —— 弗雷德里克泰勒📊 开篇:每周五下午的"噩梦" 每到周五下午,…

2025优质媒体服务商推荐榜:媒体邀约靠谱平台助力品牌高效传播

在信息传播多元化的当下,优质媒体服务商成为品牌连接用户的重要桥梁。2025 年行业竞争加剧,合规性、专业性与服务适配性成为筛选核心。本文结合行业洞察与用户反馈,值得关注的媒体服务商推荐榜,为需求方提供参考。…

2025大连汽车凹陷修复厂家推荐榜:震城汽车领衔,汽车数据修复厂家靠谱机构守护原厂漆质感

随着汽车保有量持续攀升,轻微碰撞、冰雹侵袭带来的车身凹陷成为车主高频困扰。2025 年汽车凹陷修复行业迈向标准化发展,无损修复技术因保留原厂漆、保值性强等优势备受青睐。这份大连本地优质机构推荐榜,结合技术实…

详细介绍:iBizModel 实体主状态(PSDEMAINSTATE)及主状态逻辑(PSDELOGIC`MAINSTATELOGIC`)模型详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …