说在前面
- 原文传送门
- 开发环境:macOS11.2.3、Xcode12.4、IDA7.0、class-dump、XtraFinder1.6.1
- 具备技能:X64汇编基础、OC基础知识
- 目标结果:XtraFinder是macOS上超级好用的资源管理器软件,对它爱不释手。一直使用的是无限试用(非付费)版本,每次重启都会有下面这个等待30s的弹框,今天决定去除试用弹框。
分析界面
- iOS逆向使用Reveal查看界面,macOS逆向直接使用Xcode就可以,不需要使用三方工具,原生的就是最好的。
- 这里我们不能直接查看XtraFinder界面,XtraFinder是注入到Finder里面的,是一个寄生App,依靠Finder存活的,所以需要查看Finder界面。
- 新建一个macOS App项目。因为Finder属于macOS App,macOS项目才能调试Finder,如下图:
- 附加成功后,点击查看视图,分析视图结构。发现XtraFinder属于
RegisterWindowController,如图

- 那么接下来就以
RegisterWindowController为突破点,让它不显示出来,又能正常的使用XtraFinder功能。
寻找Mach-O
- 注意:在使用
class-dump导出头文件之前,要执行otool -l XtraFinderPlugins(XtraFinder) | grep cryptid确认没有加壳,要不然导不出头文件。 - 按照常识,进入
/Applications/XtraFinder.app/Contents/MacOS找到XtraFinder,使用class-dump导出头文件,发现并没有RegisterWindowController.h这个文件,并不是我们需要的Mach-O文件 - 跟上面同样的方法,继续寻找,发现
/Applications/XtraFinder.app/Contents/Resources/XtraFinderPlugins.bundle/Contents/MacOS/XtraFinderPlugins包含RegisterWindowController.h文件,XtraFinderPlugins就是我们需要的Mach-O文件。
汇编基础
- rdi、rsi、rdx、rcx、r8、r9等寄存器常用于存放函数参数
- eax、rax常用于函数的返回值
- rax是64位的寄存器,eax是32位的寄存器,ax是eax的低16位,al是ax的低8位
- 指令
jz是Jump if Zero的别名,表示如果为0就跳转 - 指令
test用于两个操作数的按位与运算
寻找弹窗方法
- 上文说过XtraFinder是注入到Finder的,所以相当于直接动态调式Finder
- 等待连接Finder,
-w参数说明要lldb等待应用程序启动$ lldb -n Finder -w (lldb) process attach --name "Finder" --waitfor - 点击
访达→XtraFinder→Tools→Restart XtraFinder进行重启,lldb 就会附加到进程上。Executable module set to "/System/Library/CoreServices/Finder.app/Contents/MacOS/Finder". Architecture set to: x86_64h-apple-macosx-. - 给
RegisterWindowController的所有方法下断点,判断哪里进行它的显示br set -r '\[RegisterWindowController .*\]' - 命中断点后,使用
bt查看掉用堆栈,发现-[XtraFinder showRegisterWindow:]决定了它的显示。Target 0: (Finder) stopped. (lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.8 * frame #0: 0x00000001213f09b9 XtraFinderPlugins` -[RegisterWindowController setCountdown:] frame #1: 0x00000001213f4098 XtraFinderPlugins` -[XtraFinder showRegisterWindow:] + 114 frame #2: 0x00007fff21303adb Foundation` __NSFireDelayedPerform + 415 - 根据堆栈可知
showRegisterWindow:调用上一级为Foundation模块中的__NSFireDelayedPerform。这里是不能继续往下寻找哪里调用了showRegisterWindow:,我们转到IDA看看有没有线索。
寻找弹框逻辑
- 把
XtraFinderPlugins拖进去IDA进行分析,分析完成后,搜索showRegisterWindow,得到如图结果:
- 我们可以很容易发现
checkRegistrationAndShowRegisterWindow的方法,猜测这个方法就是检测有没有注册,没有注册就有会有30s的弹框。点击一下这个方法,并Fn+F5一下,得到如下伪代码:void __cdecl -[XtraFinder checkRegistrationAndShowRegisterWindow](XtraFinder *self, SEL a2) { -[XtraFinder checkRegistration](self, "checkRegistration"); if ( !(unsigned __int8)-[XtraFinder registered](self, "registered") ) { if ( -[XtraFinder daysInUse](self, "daysInUse") ) objc_msgSend(self, "performSelector:withObject:afterDelay:", "showRegisterWindowWithCountdown", 0LL, 5.0); } } - 通过上面伪代码发现:调用
-[XtraFinder registered]检测是否注册,如果没有注册就调用showRegisterWindowWithCountdown,而showRegisterWindowWithCountdown内部调用了showRegisterWindow:进行30s试用弹窗。 - 鼠标点击到
if ( !(unsigned __int8)-[XtraFinder registered](self, "registered") )尾部,按Tab键切换到汇编模式,根据上面的汇编知识可知:- 指令
test a1, a1运算结果为0的时候进行弹窗,不为0不弹窗。 - 调用方法
-[XtraFinder registered]的返回值存在了al中。 - 设置
al=1,那么test a1, a1就不会为0,不会弹窗 - 在
0000000000005F09下断点,改变a1的值为1,验证上面的猜测,
- 指令
- 下地址断点必须找到模块偏移量,使用Mach-O里面的地址加上模块偏移地址才能命中断点。因为XtraFinder是Finder启动后注入到里面的,所以并不知道XtraFinderPlugins模块是何时加进去的。这时需要在
checkRegistrationAndShowRegisterWindow头部设置断点,打印模块偏移地址,具体操作如下:- 使用
exit退出当前LLDB,重新附加成功后,执行(lldb) b checkRegistrationAndShowRegisterWindow - 按
c继续,命中后,查看偏移量(lldb) image list -o -f | grep XtraFinderPlugins [ 0] 0x0000000123469000 /Applications/XtraFinder.app/Contents/Resources/XtraFinderPlugins.bundle/Contents/MacOS/XtraFinderPlugins - 下地址断点
(lldb) br set -a 0x0000000123469000+0x0000000000005F09 Breakpoint 2: where = XtraFinderPlugins`-[XtraFinder checkRegistrationAndShowRegisterWindow] + 40, address = 0x000000012346ef09
- 使用
- 按
c继续,命中后更改al的值为1,按c继续,这个时候程序正常启动并且没有弹框XtraFinderPlugins`-[XtraFinder checkRegistrationAndShowRegisterWindow]: -> 0x12346ef09 <+40>: test al, al 0x12346ef0b <+42>: je 0x12346ef12 ; <+49> (lldb) register write al 1 (lldb) c Process 8851 resuming - 经过一段猛操作,得出结论修改
-[XtraFinder registered]返回值为1,就不会有弹窗
修改返回值
- 有很多方式可以修改,比如静态注入dylib、直接修改Mach-O汇编代码、注册机等,这里直接修改汇编代码。
安装keypatch
- IDA没有提供Hopper那样直接修改汇编代码的功能,但是有个keypatch插件可以做到。本人在安装插件过程中还是遇到了一些问题,在这里记录一下
- 基于Python编写,底层依赖keystone-engine,需要安装
sudo pip install keystone-engine - 下载
https://github.com/keystone-engine/keypatch完成后,将keypatch.py文件放到IDA的插件目录/Applications/IDA 7.0/ida.app/Contents/MacOS/plugins下,关闭IDA重新载入目标程序,这个时候点击要修改的行,右键就会出来Keypatch→Patcher选项 - 本人不能发现这个选项(IDA7.0,macOS11.2.3),进行了如下操作
- 通过
pip show keystone-engine查看keystone-engine安装路径,发现安装在/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages下面 - 进入上面的路径拷贝
keystone文件夹到/Applications/IDA 7.0/ida.app/Contents/MacOS/python下,关闭IDA重新载入目标程序 - 在需要修改的代码行右击,就会出来
Keypatch→Patcher选项
- 通过
修改汇编代码
- 找到
-[XtraFinder registered]返回值所在的行,切换到汇编模式。依次选择Keypatch→Patcher命令,在弹出的对话框中直接修改汇编代码为mov eax, 0x1,点击Patcher按钮进行确认。确认后,后面的几次Patch弹框提示全部取消,不要任何修改。
- 单击
Edit→Patch program→Apply patchs to input file,导出修改好的Mach-O文件。 - 使用修改过的
XtraFinderPlugins替换/Applications/XtraFinder.app/Contents/Resources/XtraFinderPlugins.bundle/Contents/MacOS/XtraFinderPlugins,记得提前备份一下。 - 点击
Restart XtraFinder后,发现XtraFinder不能启动了。这是因为程序经过修改后,原来的签名信息验证失败了,程序会错误退出,有两种方式解决- 进行重新签名
- 直接移除签名
- 简单起见,这里采用移除签名的方式,如下:
codesign --remove-signature XtraFinderPlugins codesign -d -vv XtraFinderPlugins XtraFinderPlugins: code object is not signed at all - 重新点击XtraFinder.app打开后,发现使用弹窗没有了,一切正常。


