使用动态库注入优雅破解 CrossOver
发布于
前言
上次我写过一篇叫做Hopper 之 CrossOver 19 破解的文章,那么今天,我再写一篇 CrossOver 19 的破文。上一篇文章里,我用了“直接修改二进制文件”的方法来破解应用,而这次,我则通过动态库注入的方法来破解 CrossOver 19——这是一个巧妙、新奇的方法,说白了就是 hook,你会喜欢上它的。我还要感谢@云在天大神在我的CleanMyMac X 破解贴里的指导,要不是她,可能我到现在还不知道什么是动态库注入。
前言的最后,我再说一下来意。我只是想教大家“破解方法”,而不是给大家“破解软件”。就拿例子说吧,我现在用的 Hopper Disassembler 可是正版的。因此,如果你喜欢 CrossOver 19,请购买正版。
准备
- 从CrossOver 官网上下载最新版的 CrossOver 19 。
- Hopper Disassembler v4 一枚。
- 与上篇文章不同的是,我们需要 Xcode(我的版本是11)。
分析软件
分析 UI
首先打开CrossOver,简单的看了一下,分析出几个信息:
-
打开软件后弹出了一个要钱弹窗,弹窗里提示了试用剩余天数,并提示你购买软件或者进行使用;
-
要钱弹窗中有两个需要关注的按钮,一个是
现在试用,另一个是使用购买信息解锁; -
按下
现在试用按钮,可以直接跳转到App里,开始14天试用; -
开始试用App后,在
CrossOver里仍然可以进行App激活操作。 -
CrossOver 没有附加反调试工具。
于是,我们就有如下的破解思路:
- Patch 剩余天数;
- Patch 使用购买信息解锁的验证流程。
本文中只讲述第一种方法。
使用 Hopper 分析
拖进 Hopper,先分析一波,看看有什么函数。本来想搜索UI中的字符串,字符串搜索无果后,根据“剩余天数”的英文“left”,搜索关键词:

我们发现,第一个函数-[CXApplication daysLeft],于是打开伪代码发现,这个函数返回的是一个int,极有可能是剩余的天数。我们可以尝试 Patch 这里,使得该函数永远返回十六进制0x8ef8(等同于 36600 天)。
按照上一篇文章的破解方法,我们只需要:
mov rax, 0x8ef8
ret
然后试用天数就永远是 36600 天了。但是别忘了,我们今天是要讲如何使用动态库注入来 hook 函数的,接下来,咱们就开始 hook。
构建动态库
动态库注入原理
在macOS下的应用程序,绝大多数都会用Objective-C或Swift进行编译(Python这类的先不谈)。
如果程序使用了Objective-C编译,我们可以仗着Objective-C的动态消息传送机制与runtime,在程序运行时进行函数的调换。
然而我们 crakers 都面对的是已经编译好的程序,所以我们要把函数调换的过程写到一个动态库中,然后再用动态库注入工具(insert_dylib之类的)注入到原来的二进制文件中。
新建项目
首先,打开 Xcode(按一下这里你会神奇地发现 Xcode 打开了),新建项目,选择macOS > Library,新建动态库项目,如图:

动态库的名称可以随便起,此处叫做CrossOverPatch,Framework选择Cocoa,Type选择Dyamic(动态)。随便找个地方保存项目即可。
现在你会得到一个动态库项目。
编写动态库
首先打开工程文件里的CrossOverPatch.m文件,输入:
#import "CrossOverPatch.h"
#import <objc/runtime.h>
我们用两个#import语句,将CrossOverPatch.h文件与objc/runtime.h库引入。CrossOverPatch.h文件(头文件),是每个.m文件里必须引用的。而objc/runtime.h库就是大名鼎鼎的运行时类库。
之后我们在下面声明一个实现@implementation
@implementation CrossOverPatch
@end
接下来,我们在实现里定义一个 hook 方法:
- (int)daysLeftHook {
NSLog(@"====== METHOD PATCHING ======");
return 36600;
}
**我们在下面的步骤中要用这个方法来替换原函数daysLeft,实现 hook。**这里需要注意一点,我们定义的这个函数的返回值(int)和参数列表(无)需要与原函数相同。NSLog则是打印语句,方便我们后期检测程序。最后的return语句,让函数返回了36600这个值。你当然也可以把36600写作0x8ef8。
我们在实现里添加一个+ (void) load方法,如下:
+ (void)load {
NSLog(@"====== START DYLIB INJECT ======");
NSLog(@"====== GETTING METHOD ======");
Method origMethod = class_getInstanceMethod(NSClassFromString(@"CXApplication"), NSSelectorFromString(@"daysLeft"));
Method newMethod = class_getInstanceMethod([CrossOverPatch class], @selector(orphansArray:));
method_exchangeImplementations(origMethod, newMethod);
NSLog(@"====== METHOD SWIZZLED ======");
}
我们一行一行地讲解代码:
- 函数
load定义。load函数会在动态库被加载时触发; - 打印日志;
- 同上;
- 定义变量
origMethod,它的类型是Method,它的值是原程序的daysLeft方法。class_getInstanceMethod::函数可以获取一个类(第一个参数)的一个实例方法(第二个参数);NSSelectorFromString:可以通过一个字符串(第一个参数)来获取一个类;NSSelectorFromString可以通过一个字符串(第一个参数)获取一个Selector;
- 定义变量
newMethod,类型是Method,它的值是我们刚才自定义的daysLeftHook方法,我们要用这个方法来替换第4行的daysLeft方法。 - 将第四行和第五行定义的新、旧两个函数通过
method_exchangeImplementations::交换。这样的话,原程序的daysLeft就成了我们自定义的daysLeftHook,而我们自定义的daysLeftHook就成了原程序的daysLeft,也就意味着成功 hook 了。 - 打印日志。
CrossOverPatch.m写完了。别忘了打开CrossOverPatch.h文件,声明函数:
@interface CrossOverPatch : NSObject
+(void)load;
@end
按下Cmd + B编译一下,最终得到动态库文件:libCrossOverPatch.dylib!
注入动态库
离破解只差一步——注入。方法有许多:运行bash脚本、修改info.plist……不过我更倾向于使用insert_dylib工具注入。点击这个链接即可将insert_dylib项目克隆到Xcode,编译得到insert_dylib可执行文件。
- 把
insert_dylib、CrossOverPatch.dylib和 CrossOver 19 应用程序的原来的二进制文件的绝对路径记录下来。 - 打开终端,输入命令:
$ cd [insert_dylib的路径] $ ./insert_dylib [dylib的路径] [原来的二进制文件的路径]
回车后,如果出现LC_CODE_SIGNATURE load command found. Remove it? [y/n],那么就按下y,回车。
如果出现Added LC_LOAD_DYLIB to /Applications/CrossOver.app/Contents/MacOS/CrossOver_patched,代表注入成功。
打开访达,进入/Applications/CrossOver.app/Contents/MacOS/位置,你还会发现有两个文件:CrossOver和CrossOver_patched。其中CrossOver_patched就是已经被我们注入的文件。将原来的CrossOver二进制删除或更改名称,将CrossOver_patched更名为CrossOver。
改头换面
通过下图找到电子权利信息:

找到后,用文本编辑打开,在文件开头加上一句类似下面的话:
这个版本的CrossOver已经由xxx破解。你可以放心使用。注意,请勿广泛传播……
验证破解
双击CrossOver二进制文件,即可打开应用程序。可以看见试用天数已经破解:

电子权利信息也被修改:
与此同时,你的电脑应该会弹出终端窗口:
====== START DYLIB INJECT ======
====== GETTING METHOD ======
====== METHOD PATCHING ======
====== METHOD SWIZZLED ======