记录一次OSX软件破解

这不是一篇教你破解苹果软件的简明教程,而是一篇斗智斗勇的曲折故事。请支持正版!

序章:惹错人了

听说精致的Mac和优雅的MarkDown很配。你知道Markdown也可以用来写作幻灯片吗?首先给大家推荐一个Mac平台上优雅的应用叫做DeckSet。售价29.99$,挺贵的,呵呵。

我真的是一个自由软件爱好者!我虽然穷我支持正版!但我同时是个愤青啊!AppStore慢也就算了,想注册个可以花钱的账号过程也太不友好了!关键是,安装完DeckSet你跟我说公式渲染要额外付费9.99$是几个意思?

而且我仔细观察软件的行为,发现它的公式解析使用的是MathJax——一款开源免费的程序。DeckSet从开源软件中免费获得了代码,转而将这些代码实现的功能通过收费的形式提供给消费者,太不人性了(不过这不构成侵权,因为MathJax遵循的Apache协议允许商业销售)。

如果你不付钱呢,它就给你这样弹警告:

给你这样(把你的公式给吞了,仔细看还有淡淡的影子):

不管怎么样,我想它是惹错人了。

利其器

我并不是一个职业黑客。我之前也没破解过软件。既然放了狠话,也只能硬着头皮上。看看我已经有啥技能和工具:掌握C语言,有过软件设计的些许经验。由于踩过无数坑,渐渐总结出了软件调试技巧。加上一些新手的运气和对奸商的憎恶——可以付之一搏了。

再看看手头工具有哪些呢?静态观察程序代码,那就得要反汇编器,我觉得otx就不错。动态观察程序运行,那就得要调试器,就用Xcode自带的gdb吧。编辑二进制文件,系统自带的vim就可以。都是一些没有图形界面的土装备,但够用就行,烦不了太多——我是新手我怕谁。

otx用homebrew就可以安装。前提是必须先安装好Xcode。

gdb据说是Xcode自带。不过之前我用homebrew大法安装了独立版本。受限于系统权限,gdb想调试一个程序并不容易。你得用证书把gdb签名,赋予它调试其它进程的权限。具体过程可以参考这个

了解你的敌人

为了破解一个软件,我们不得不先了解这个软件。为了了解我们的目标,我们得搜集它的各种信息。

搜集信息的第一个层面是你要知道敌人在哪?我是说你的知道软件的安装位置和目录结构。Mac系统上,软件被装在/Application/目录下。其中/Applications/XXX.app/Contents/MacOS/这个目录下就是程序的二进制文件——我们靶子!

搜集信息的第二个层面就是多用这个软件,对与一些提示要特别留意。比如第一幅图中那烦人的黄色警告条:”Buy formula support for $9.99”就变成了我的突破口。

首先我用下面命令对程序进行反汇编,汇编代码保存在deckset.asm文件里。

otx Applications/Deckset.app/Contents/MacOS/Deckset > deckset.asm

打开保存结果的文件,里面全是汇编代码和各种零零碎碎的信息。我是新手,我看不懂。我不管,先Ctrl+F查找”Buy formula support”。果然,这样这样函数,对某个值进行了判断,并跳转到输出”Buy formula support”。跟踪这一线索,不说能不能了解程序判断你是否是合法用户的机制,倒是眼前肯定闪过了不少能让你想入非非的词汇,比如:

  • initWithMASProduct(MAS是不是maths?Product!)
  • RecieptVerification(Verification?验证什么?)
  • systemMACAddress(是不是用了MAC地址进行验证啊?)
  • purchaseFormulas

这个时候一些软件设计的经验就派上用场啦!用猫的思维去思考的老鼠活得才自在,用老鼠思维去思考的猫才能抓到最多的老鼠。知道怎么拼软件才知道怎么拆软件。

这时google到了一个工具叫class-dump(听到dump就来劲!),可以从面相对象编程的OC二进制文件中提取类的信息。这就是搜集信息的第三个层面,程序代码中蕴含的信息。otx反汇编时其实已经包含零零碎碎的信息啦,只不过class-dump的信息是按面向对象的思想整理过的。

otx也好,class-dump也好,不管用什么工具,意识最重要。刚刚看过的那些想入非非的字符串的最后一条“purchaseFormulas”激发了我的灵感。我Ctrl+F用purchase一搜,得到了下图信息:左边是otx反汇编的代码,右边是class-dump的结果,可以验证这些信息两个工具都是可以提供的,只是class-dump更便于阅读):

看到这个函数名”purchasedAddOnWithIdentifier”,加上返回值类型是BOOL,以及一些想当然,可以说任务已经完成一大半了。明白了吗?这个函数很可能是用来验证用户是否购买过这个AddOn,如果买过就返回True,否则False。所以我们只要让这个函数一值返回True很可能就大功告成啦!

准备毒药

来,我们仔细看purchasedAddOnWithIdentifier代码!

你真的仔细看了?我开玩笑的:P 我自己都看不懂!不需要看懂,我就是想让这个函数一直返回1而已。大神可能已经在用大脑编译一段实现这样功能代码然后直接用机器码填上了——这我可做不到,但我机灵着呐:我用C语言写一段一直返回1的函数,然后编译这段C代码,接着从里面提取这段机器码。

好,现在就开始做!写一段熟悉的C语言代码:

#include <stdio.h>

int freeman()
{
    return 1;
}

int main()
{
    if (freeman())
        printf("hello");
    return 0;
}

编译完了在用otx反汇编,得到:

用图中高亮显示的8字节机器码替换掉purchasedAddOnWithIdentifier开头的代码,就可以让purchasedAddOnWithIdentifier函数永远返回1,也就是True啦。这8字节机器码就是我们的毒药,下面我们来看看怎么把毒药喂给程序。

下毒

明确任务,我们需要把图左边的8字节代码一一换成右边对应的代码。

但是还记得吗?我们上述操作都是建立在purchasedAddOnWithIdentifier是关键验证函数这个猜想上的,我可不想在可执行文件本身直接动手,而是选择在运行时内存镜像中进行动态hack。这里就用上了gdb和一些特殊的调试技巧。

gdb deskset
> b *0x00000001000c43b2   #purchasedAddOnWithIdentifier函数开始地址
> r

然后随便摆弄一下程序,直到其“卡”在断点上。谢天谢地,程序真的停下来了!然后开始用gdb的set命令修改0x1000c43b2~0x1000c43be范围内内存单元的值。set完了,再用disas 开始地址,+区间长度命令反汇编这一段内存区域,检查是否正确。从下图可以看到,我们的代码和C语言hello程序中的freeman函数反汇编的结果是一样的。

然后 continue 运行。成功了!

清爽的界面再没有警告!购买菜单也灰掉了。

最重要的是公式终于清楚地显示啦!

稳固胜利果实

试验成功了,我们的毒已经注入了程序的内存空间。但是这个破解可不是永久的,每次重新打开程序,就得重复上述步骤进行破解。现在我们要把胜利的果实固定住。这可以通过修改程序二进制文件进行。用到的工具就是vim。

利用vim打开二进制程序看起来不是一个好主意——一片乱码啊!不过在vim中敲:然后在下面输入%!xxd回车后看到了可是另一番景象。通过搜索匹配到我们的目标代码,然后逐一替换,保存即可。

具体操作时有一些问题,比如说目标代码不好定位。这里提供一个小的脚本工具:offset可以方便地把otx中的内存地址转换成二进制文件中的偏移。

替换完成后别急着wq保存退出。而是先%!xxd -r重新转码然后再保存退出。

这样,我们就可以优雅地运行程序了。

技术总结

对于看完故事还意犹未尽的技术人,我还准备了一份简单的路线:

  • 收集信息和探索:对二进制文件展开成人类可读的形式后,使用关键词进行搜索,定位阻碍我们流畅使用该应用的函数。这一步耗时最长,需要仔细摆弄软件获得灵感,并且直接决定能否成功。一般搜索关键词如 activate、purchase、register、trial 之类,也可以结合具体应用和想要的效果选取。例如对于弹窗的应用,不一定非要“破解”,只要能屏蔽广告就行,那么可以使用 dialog、window 之类的关键词搜索。这个阶段使用的工具主要目的是为了把 binary format 妆换成 human-readable format,工具例如:hopper、otx、class-dump 都可以满足,class-dump 信息清晰一下。
  • 如果上一步定位了目标函数,则需要弄清这个函数的输入输出,并撰写编译自己后面用来替换的函数。比如目标函数通过判断用户身份返回是否合法,合法true不合法false。那么我们就建立一个C文件写一个永远返回true的函数编译出二进制。如果一个目标是一个返回void的弹窗函数,那么我们就让这个函数调用后立马return,跳过后面花里胡哨的广告展示逻辑。这个阶段用到的工具是 C 语言编译器,如 gcc。
  • 找到目标函数的虚拟内存地址。工具: hopper 或 otx 都可以满足。
  • 通过上面的内存地址获取代码在二进制文件中的位置。工具: offset 脚本。
  • 使用自己的函数逻辑替换二进制文件 offset 处原逻辑。工具:vim && xxds
  • 测试是否成功。不成功重复上述步骤,或者google 高阶破解技巧,或者放弃。

请支持正版!

参考资料