macOS逆向之跳过XtraFinder试用界面
说在前面
- 注意事项:此文仅限于技术交流,请不要做违法的事情。对于那些居心叵测的人根据此文造成违法的事情与本人无关。此文章不得转载!!!如果APP方需要删除,请发邮件
xxoo@hotmail.com
,谢谢。 - 开发环境: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
导出头文件之前,要执行如下命令,确认没有加壳,要不然导不出头文件。1
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等待应用程序启动1
2lldb -n Finder -w
(lldb) process attach --name "Finder" --waitfor - 点击
访达
->XtraFinder
->Tools
->Restart XtraFinder
进行重启,lldb 就会附加到进程上。1
2Executable module set to "/System/Library/CoreServices/Finder.app/Contents/MacOS/Finder".
Architecture set to: x86_64h-apple-macosx-. - 给
RegisterWindowController
的所有方法下断点,判断哪里进行它的显示1
br set -r '\[RegisterWindowController .*\]'
- 命中断点后,使用
bt
查看掉用堆栈,发现-[XtraFinder showRegisterWindow:]
决定了它的显示。1
2
3
4
5
6Target 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
一下,得到如下伪代码:1
2
3
4
5
6
7
8
9void __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,重新附加成功后,执行1
(lldb) b checkRegistrationAndShowRegisterWindow
- 按
c
继续,命中后,查看偏移量1
2(lldb) image list -o -f | grep XtraFinderPlugins
[ 0] 0x0000000123469000 /Applications/XtraFinder.app/Contents/Resources/XtraFinderPlugins.bundle/Contents/MacOS/XtraFinderPlugins - 下地址断点
1
2(lldb) br set -a 0x0000000123469000+0x0000000000005F09
Breakpoint 2: where = XtraFinderPlugins`-[XtraFinder checkRegistrationAndShowRegisterWindow] + 40, address = 0x000000012346ef09
- 使用
- 按
c
继续,命中后更改al的值为1,按c
继续,这个时候程序正常启动并且没有弹框1
2
3
4
5
6
7XtraFinderPlugins`-[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打开后,发现使用弹窗没有了,一切正常。