EDiSO 破解版 Pixelmator Pro 4.3 崩溃修复记录(附修复布补丁)
环境:macOS 26.6.2,Pixelmator Pro 4.3 (3.0.75),EDiSO 破解版。 现象:用「修复画笔」必崩,稳定复现。
结论
原因是 EDiSO 团队破解注入的动态库 libcs-icloud.dylib 把 NSUbiquitousKeyValueStore 降级到了 NSUserDefaults,补方法时漏了 setString:forKey:(和 setDictionary:forKey:),导致主程序调用时报错。
修复方式:在这个 dylib 里加 148 字节机器码 + 改一条 4 字节跳转,然后重签名。
崩溃日志
Exception Type: EXC_CRASH (SIGABRT)
Exception Reason: -[NSUserDefaults setString:forKey:]: unrecognized selector sent to instance 0xa7f44e6700xa7f44e670 不可信。写程序把它当对象解,直接 bus error;macOS arm64 的 tagged pointer 最高位必须是 1,它不是。类名和 selector 才是有效信息。
报告里的基址与崩溃帧:
PXMPro base 0x101cc0000
frame 0x1020b481c → offset 0x3f481cPXMPro 的 __TEXT vmaddr 和 fileoff 都是 0,偏移可以直接查符号:
nm -n PXMPro | awk '$1 <= "00000000003f481c"' | tail -5 # → 0x3f46f0
otool -tvV PXMPro # 按地址过滤崩溃函数做的事:
0x3f47b8 ldr x0, [x8, #0x800] ; _OBJC_CLASS_$_NSUbiquitousKeyValueStore
0x3f47c0 bl _objc_msgSend$defaultStore
0x3f47cc mov x20, x0 ; store
0x3f480c mov x0, x20 ; receiver
0x3f4810 mov x2, x19 ; 日期字符串
0x3f4814 mov x3, x21 ; key
0x3f4818 bl _objc_msgSend$setString:forKey:
0x3f481c ... ; 崩溃帧调用者 0x3f2f28 里带源码路径:
.../PXMPro/Classes/Application/AppContext+App Store Review.swift
"requestReviewIfEligible()"整个 PXMPro 里 setString:forKey: 只有这一处调用点。
EDiSO 的破解手段
崩溃报告的 Binary Images 里有一行:
0x100aac000 libcs-icloud.dylib (*) .../Contents/Resources/libcs-icloud.dylib无 bundle id,不是 Apple 也不是 Pixelmator 的东西。主程序的依赖列表末尾多了一条:
@executable_path/../Resources/libcs-icloud.dylib包是用自制证书 TEAM EDiSO 重签的,这就是 EDiSO 破解版。
dylib 导出只有一个类 CSICloudLoader,+load 只有一句 _dyld_register_func_for_add_image(0x146c)。这个回调每加载一个 image 跑一次,所以主程序、后加载的框架都会被扫到。回调做四件事:
method_setImplementation(-[CKEntitlements cloudServices], stub)—— 绕过 CloudKit 授权检查。method_setImplementation(+[NSUbiquitousKeyValueStore defaultStore], imp(+[NSUserDefaults standardUserDefaults]))—— 掉包。- 给
NSUserDefaults补 KVS 专属方法:
0x1598 "setData:forKey:" → imp(setObject:forKey:), "v@:@@"
0x15cc "setArray:forKey:" → imp(setObject:forKey:), "v@:@@"
0x15f8 "setLongLong:forKey:" → imp(setInteger:forKey:), "v@:q@"
0x1628 "longLongForKey:" → imp(integerForKey:), "q@:@"- Swift 部分:
StoreKit.Product/Transaction/StoreKitError的 metadata 和错误构造,加上 App Analytics 同意书的假数据(com.apple.appanalytics.trackingconsent.decision、{"permission":{"disallowed":{}},"date":0.0})。
订阅、CloudKit 授权、iCloud 同步、分析上报,一个子系统一个 hack,全是运行时打补丁。
为什么动 iCloud。 NSUbiquitousKeyValueStore 强依赖 iCloud entitlement 和 Apple 签名。重签之后签名主体变了,容器和凭据拿不到,调用要么报错要么弹登录。EDiSO 的做法是让 defaultStore 直接返回 standardUserDefaults:调用方一行不用改,不要账号,不弹窗,云同步降级成本地偏好设置。对破解用户来说几乎无感,只是不再跨设备同步。
这套方法的问题:
- 运行时改方法表,没有编译期检查,也没有完整性校验。缺哪个方法,只有在某条冷路径真正调用时才炸,而且炸的是
unrecognized selector,不是优雅降级。这次从用户操作到崩溃隔了 20 多秒,归因成本极高。 - 全局无差别覆盖。进程里任何代码拿
NSUbiquitousKeyValueStore都会被换掉,而ubiquityIdentityToken之类的 API 还是真的(本机没登录 iCloud 就返回 nil),同一个框架里两套语义并存。 - 数据落进同一个 domain。KVS 的键直接写进
com.apple.pixelmator的 standard defaults,和应用自己的偏好混在一张表里,键名没有隔离;dictionaryRepresentation也从「KVS 的全部内容」变成了「整个 App 的偏好」。 - 回调每个 image 都跑一遍,
method_setImplementation反复执行,class_addMethod每次都返回 NO,全靠幂等兜着。
漏了哪两个
NSUbiquitousKeyValueStore 和 NSUserDefaults 方法集不重合。实际测(macOS 26.6.2):
| selector | KVS | NSUserDefaults | 破解补了吗 |
|---|---|---|---|
setString:forKey: | ✔ | ✘ | 漏 |
setDictionary:forKey: | ✔ | ✘ | 漏 |
setData:forKey: | ✔ | ✘ | 补了 |
setArray:forKey: | ✔ | ✘ | 补了 |
setLongLong:forKey: / longLongForKey: | ✔ | ✘ | 补了 |
objectForKey: / removeObjectForKey: / synchronize / doubleForKey: / boolForKey: | ✔ | ✔ | 不需要 |
setString:forKey: 是 KVS 用得最多的写接口,偏偏漏了。崩溃报告里类名是 NSUserDefaults 而不是 KVS,就是这个 shim 的指纹。
少写一行 class_addMethod,代价就是一条必崩的冷路径——这就是前面说的「没有完整性校验」的具体后果。
补丁
不重编译、不加新 dylib、不改 App 代码。只改 libcs-icloud.dylib 的 arm64 slice。
找空地。 __TEXT 段 0x0–0x8000,最后一个 section __eh_frame 到 0x5690 结束,后面到 0x8000 都是页对齐填充,仍在 r-x 映射内。约 2.4KB 可用。
放东西。
0x5700 "setString:forKey:"
0x5720 "setDictionary:forKey:"
0x5800 补丁函数(37 条指令,148 字节)补丁函数先保存 x29/x30/x19–x22,用进来的 x0(NSUserDefaults 类)把原本那条 class_addMethod 补完,再查 setObject:forKey: 的 IMP,最后两次 class_addMethod 注册 setString:forKey: 和 setDictionary:forKey:(type v@:@@),恢复寄存器返回。
挂上去。 原回调末尾 0x1664 是一条尾调用:
0x1664: b _class_addMethod ; 0x14000d6f
→
0x1664: b 0x5800 ; 0x14001067跳板方式,原调用照样执行,只是后面多两件事。
编码。 没有汇编器(adrp/bl 位置相关),用 Python 手写:
def adrp(rd, pc, target):
delta = ((target & ~0xFFF) - (pc & ~0xFFF)) >> 12
return 0x90000000 | ((delta & 3) << 29) | (((delta >> 2) & 0x7FFFF) << 5) | rd
def bl(pc, target):
return 0x94000000 | (((target - pc) >> 2) & 0x03FFFFFF)按 fat header 里 arm64 slice 的偏移 81920 换算成文件偏移写入。
踩的坑
第一版装上去段错误。崩溃报告里 pc = lr = 0x4ef8000,正好是测试程序基址的低 32 位,说明 ret 跳到了垃圾地址。
序言和收尾不对称:
stp x29,x30,[sp,#-0x30]! ; sp 已经减了 0x30
ldp x29,x30,[sp,#0x30] ; 错,应该读 [sp]改成 ldp x29,x30,[sp] 就正常了。
签名与验证
原包是 TEAM EDiSO 签的,本机钥匙串没有这个身份(security find-identity 返回 0),只能 ad-hoc。App 带 sandbox 和 app-group,先拿 684MB 的副本试:
codesign --force -s - --entitlements ent.plist "PM.app"
codesign -v "PM.app" # 通过
open -n "PM.app" # 正常启动,偏好和日志都写进沙盒容器确认没问题才动 /Applications 里那一份(当时桌面上还有一份副本,一并改了,后来删掉)。entitlements 从原签名 dump 出来原样保留,改完 codesign -v 通过。
验证分三层:
codesign -v通过。- 复现程序:原 dylib 抛异常,补丁后
respondsToSelector全 1,setString/setData/setArray/setDictionary/setLongLong都能读写。 - 真机启动后,沙盒偏好里出现以前写不进去的键:
macOS.requestedAppStoreReviewDate = "2026-11-03"
reviewRequestedAppVersion = "4.3"
reviewRequestedCount = 1
reviewRequestedLastDate = "2026-11-03"这些正是崩溃函数要写的数据,写进去了就说明路径跑通,且没有新的崩溃报告。
软件与修复补丁
鼠鼠的Pixelmator Pro 4.3 (EDiSO破解版)软件来自俄罗斯的appstorrent网站 https://appstorrent.ru/,这个也是鼠鼠用下来体验最好的软件下载站了,如有这个网站有帮到你,十分建议为其点击捐赠。同时,EDiSO团队也是一个极具黑客精神的团队,鼠鼠用过他们制作的很多App Store 中的破解软件,在此向他们表达感谢!
如果你正在使用该版本的软件,并为此感到困扰,可以将下面的prompt发给你的DSH(或其他coding agent),神奇的AI大人会帮你完成这一切。
@"/Applications/Pixelmator Pro Creator Studio.app/" @/path/to/report.md
这是 EDiSO 破解版的 Pixelmator Pro 4.3,用「修复画笔」后必崩,
崩溃报告和 app 都给你了。请直接在 /Applications 里的这个 app 上原地修复,
最后交付一个能直接双击打开、正常使用的 app。没有重装包,不要重装或替换整个 app。
已知信息:
- 崩溃:EXC_CRASH / SIGABRT,-[NSUserDefaults setString:forKey:]: unrecognized selector
- 崩溃点:PXMPro 的 requestReviewIfEligible(),往 NSUbiquitousKeyValueStore 写日期
- 怀疑:破解注入的 Contents/Resources/libcs-icloud.dylib 把
+[NSUbiquitousKeyValueStore defaultStore] 换成了 +[NSUserDefaults standardUserDefaults],
但只补了 setData:/setArray:/setLongLong:/longLongForKey:,漏了 setString:forKey:。
请按这个顺序做:
1. 先最小复现:写个程序用 DYLD_INSERT_LIBRARIES 加载这个 dylib,调用
defaultStore + setString:forKey:,确认异常与报告一致。
2. 确认后再改二进制。改之前把原 dylib 备份到 app 外面。
3. 改法优先选最小侵入:在 dylib 的 __TEXT 填充区加代码,用跳板补注册缺失的方法,
不要重编译、不要新增 dylib、不要改 App 自身的可执行文件。
4. 改完对 dylib 和整个 .app 重新签名。原包是自制证书 TEAM EDiSO 签的,本机没有该身份,
用 ad-hoc 并保留原 entitlements(用 codesign -d --entitlements :- 先导出)。
带 sandbox 的 App,先复制一份到临时目录试签名和启动,确认容器读写正常再动 /Applications。
5. 验证:codesign -v 通过;复现程序不再抛异常;启动 App 后
~/Library/Containers/com.apple.pixelmator/Data/Library/Preferences/com.apple.pixelmator.plist
里出现 macOS.requestedAppStoreReviewDate / reviewRequestedLastDate / reviewRequestedCount;
无新的崩溃报告。
最终交付给用户修复好的APP文件,并告知修复情况与使用方式。