准备工作
工具下载
以下工具包括Folx都打包好上传网盘了,这里下载。
Folx
一个mac上挺好用的下载工具,本文逆向破解的对象。直接把Folx.app
拖到Applications
文件夹中即可。mySIMBL
扩展包(plug in
)管理软件,本文将要给Folx软件写扩展包达到破解的目的,因此使用mySIMBL进行扩展包管理,图形界面方便的删除、开启等功能。
直接把mySIMBL.app
拖到Applications
文件夹中即可。EasySIMBL-Bundle-Template
一个扩展包XCode模板,类似于开发iOS在XCode新建工程时选的Single View App
。
把EasySIMBL Bundle.xctemplate
文件夹拖到~/Library/Developer/Xcode/Templates/Project Templates
目录下即可,如图所示。
class-dump
iOS/Mac逆向必须介绍的软件,从可执行文件
中导出头文件,然而本文用不上,不做介绍。Hopper Disassembler
超级强大的反编译软件,不仅可以把机器码解析成汇编,还能解析出相似与Objc的伪代码。总之就是太强大了,强大到我们几乎连class-dump
都用不上了。本文提供了史蒂芬周的破解版本,把Hopper Disassembler v4.app
拖进HopperV4Patcher
即可破解,如图所示。
问题分析
Folx有个很方便的功能下载Youtube视频,只要把Youtube视频的URL输入就可以下载(而且速度还不错,暂时没验证需不需要科学上网)。然而这是个付费功能,只要点下OK按钮就会跳出讨钱页。
听说有小伙伴因为种种原因弄不到Youtube的URL?下面提供一个。
https://www.youtube.com/watch?v=QFH747sK200
逆向分析
打开Hopper,选择File-Read Executable to Disassemble
直接粗暴读取二进制可执行文件来破解,如图所示。
选择Applications文件夹中的Folx,右键显示包内容
,并找到可执行文件,类似于win平台的.exe
文件,在这里:
等Hopper缓过神来,界面就差不多长这样。由于个人使用习惯,隐藏了底部和右边栏,可以在右上角的三个按钮选择是否开启。
左边栏是导航,可以看到使用Objc的方框语法列出了许多方法,还要啥class-dump?
中间区域是汉莫拉比法典,记载了一些上古语言,讲述了一些你不需要了解的事,直接无视就行。
OK我们的目的是要破解YouTube下载功能,那么把Youtube
当做关键词来搜索应该不会错,所以在左边栏的搜索框中输入youtube
,可以得到许多和Youtube相关的类和方法:
我们来猜一下这都是些什么东西。(十秒逆向九秒猜)
- FolxYouTubeHelper:看上去没有什么特别的东西,应该不是我们要找的。
- AppDelegate:看上去也只是一个干杂货的,负责弹框之类,应该也不是我们要找的。
- SDUrl:看上去像是个下载辅助的东西,应该还不是我们要找的。
- FolxNewEditTask:看上去是负责新建下载任务相关的类,估计会包含验证之类的东西,我们可以尝试从这个入手。
在搜索栏中查找FolxNewEditTask
,可以得到一整列表,这东西有点肥,看上去方法很多,其实基本每个@Property
都有get
和set
两个方法。
此处无图
在FolxNewEditTask
的方法中看到一个done:
方法,应该是新建任务完成的方法,也就是OK按钮
的点击方法。选中该方法,一探究竟。
在顶部栏点击伪代码按钮可以把上古语言翻译成类似于Objc的代码,极大提高可阅读性。代码中还保留了许多汇编的特征,例如rbx
、r14
之类的寄存器,可以简单理解为变量。如果你认可我葬爱家族,那么这样的代码对你来说应该没有任何阅读压力。
得到[FolxNewEditTask purchased]的伪代码:
在方法的开头就是一个判断语句,由&&
区分出两个判断条件。
前一条件首先是一个Objc下消息机制的方法调用,由前一句r14 = @selector(addTaskType);
可以猜测应该是判断当前的下载类型,记得Folx提供了三个下载类型,而Youtube是第三个类型,就是==0x2
成立。
后一个条件的关键词在于purchased
,这就很明显了。purchase的消息对象是rbx,前两句rbx=self
可以知道FolxNewEditTask对象应该有个purchased方法,判断交没交保护费,如果交了就走else语句,没交的话就弹出交钱框,就是底下的@selector(showFreeYouTubeAlert:)
。
不过我们的重点在于判断交保护费的方法,在左侧栏搜索,果然FolxNewEditTask是有这个方法,而且还有
setPurchased方法,可以推知
purchased`应该是一个成员属性。
这样我们就不用管它判断的机制是什么,只要这个方法返回YES
就对了,办法就是把FolxNewEditTask的purchased方法换成我们自己的purchased方法,这样不仅是YES,要返回大象都可以。
Hook
打开XCode,新建一个EasySIMBL模板工程
。
工程名无所谓,最重要的是Target App Bundle Id
,也就是我们要破解的App的Bundle ID
,在Folx.app的包内容中可以找到info.plist
文件,然后找到Folx的Bundle ID。
我一般用如下结构存放文件,其中Sources
组下存放原App中类的头文件,而HookClasses
组下存放我们自制的对应类。
首先在Sources下创建FolxNewEditTask.h
文件,然后把里面的代码都删了,填入如下内容。
@interface FolxNewEditTask
- (BOOL)purchased;
@end
虽然我们创建了FolxNewEditTask.h
,假装我们知道FolxNewEditTask的一切,其实我们只需要知道FolxNewEditTask有purchased这么一个方法,但这就够了,因为我们只需要一个flag。当然正经点的做法是使用class-dump
导出.h
头文件来使用,学习class-dump。
接着在HookClasses
中创建FolxNewEditTask+Hook的.h和.m文件。在FolxNewEditTask+Hook.h
文件中填入代码如下:
#import
#import "FolxNewEditTask.h"
@interface NSObject(FolxNewEditTaskHook)
+ (void)hookFolxNewEditTask;
@end
在FolxNewEditTask+Hook.m
文件中填入如下代码:
#import "FolxNewEditTask+Hook.h"
@implementation NSObject(FolxNewEditTaskHook)
+ (void)hookFolxNewEditTask {
NSError *error;
[self jr_swizzleMethod:@selector(purchased)
withMethod:@selector(banana_purchased)
error:&error];
if (error) {
NSLog(@"+++++hookFolxNewEditTask error: %@", error);
}
}
- (BOOL)banana_purchased {
return YES;
}
@end
-
banana_purchased
方法是我们自制的保护费管理员,不管什么情况下都会返回YES。 -
hookFolxNewEditTask
中我们调用jr_swizzleMethod:
方法把FolxNewEditTask原本的purchased方法和我们自制的purchased方法交换。
接着就是要在Folx程序启动的时候调用偷天换日大法。找到Banana.m
或者你的项目名.m
,里面已经提供好了load
方法,搞Objc开发的都应该知道load
方法是在类载入内存的时候调用,也是偷换方法的最佳时机。
首先得导入我们的头文件:
#import "FolxNewEditTask+Hook.h"
其次在load
方法后加上一行来调用偷换方法。
[NSClassFromString(@"FolxNewEditTask") hookFolxNewEditTask];
用command
+B
来编译一下,显示build succeeded
这样扩展就已经安装好了。我们可以打开mySIMBL
来管理扩展。
因为在扩展的方法中我们打印了一些信息,因此可以打开Console.app
来查看,这个是系统自带程序,在Launchpad
里找找。在Consolo里搜索++++
来过滤其他不必要信息。
然后就是见证奇迹了,运行Folx吧。
在Console中接收到这么一条消息就表示扩展已经成功执行:
接下来去测试一下Youtube功能是不是能使用。
点击OK之后讨厌的讨钱窗口没有跳出来,而是直接开始下载了,那么就算破解成功了:
根据我的猜测也有可能因为某些科学原因没法下载,因为公司都是科学上网的,所以我才不会开4G去验证呢。
更暴力的方法
正如标题所说,Hopper玩转Mac逆向
,而没说Mac和XCode玩转Mac逆向。
Hopper掌握了机器码,就拥有了生杀大权,所谓汇编、伪代码都是解析给程序员看的,虽然说使用Hook方法替换原方法可以自定返回值,甚至可以返回大象!但是我们不需要大象,如果只是一个BOOL
的事,汇编的修改是不是就够了呢?
当然,首先我们在mySIMBL里把方才所写的Folx扩展禁用掉,然后把Folx关了,回到Hopper中。
依旧在左侧栏中搜索找到[FolxNewEditTask purchased]
方法,并选择汇编代码。
其实[FolxNewEditTask purchased]
的Objc代码只有一两行,对应的汇编代码其实也不长,就7行:
1. push rbp
2. mov rbp, rsp
3. mov rax, qword [objc_ivar_offset_FolxNewEditTask__purchased]
4. mov al, byte [rdi+rax]
5. movsx eax, al
6. pop rbp
7. ret
rax
寄存器是返回值寄存器,相当于被放在rax
里的值最后都会被return rax;
。
rax
在第3行出现过一次,mov指令可以理解为赋值语句,意义是将qword [objc_ivar_offset_FolxNewEditTask__purchased]
的值赋予rax
。
我们不用管qword [objc_ivar_offset_FolxNewEditTask__purchased]
是什么,我们要的结果是return YES
,也就是return 0x01
,只需要确保在ret指令的时候rax的值是0x01即可。
因此选中mov rax, qword [objc_ivar_offset_FolxNewEditTask__purchased]
一行,选择Modify
-Assemble Instruction
,输入如下指令:
mov rax, 0x01
回车
选择File
-Produce New Executable
来生成新的可执行文件Folx,替换掉原来的文件即可。
结尾
没有结尾。