by 大和尚
Disassemble Hopper是一款可以和IDA pro媲美的逆向工具,它可以用来反汇编、反编译和动态调试Mac OSX和Linux操作系统上X86、X64、ARM、ARM64等各种架构的应用程序。其界面如图1:
图1
图1. Hopper界面图示
其官网地址为:https://www.hopperapp.com/; 当前最新版本为: 4.7.7;购买价格为$99.00/年;
二。 注册流程未注册版本开启软件以后,自动弹出注册界面如下:
图2
图2:hopper注册窗口
在License File框内填入伪造的License文件路径,并点击“离线激活”按钮(Offline Activation)以后,转到第二步的验证界面如下;
图3
图3:离线验证界面
三。 界面分析通过逆向分析,以上两个窗口对应的Controller类,及图2窗口的Offline Activation按钮、图3窗口的OK按钮的responser回调函数分别如图4所示:
图4
图4. V层-C层对应关系图示
四。流程分析
那么我们从“Registration”窗口的“Offline Activation”按钮的Responser响应函数开始逆向。即-[RegistrationWindowController offlineActivationButtonClicked:]函数,其函数流程如下;
图5
图5.离线激活按钮响应函数流程图
其中1,5,6,7四个函数需要特别注意,
表格1 重点逆向函数1 | preCheckLicenseAndPresentError | License格式预检查,返回值需要为true。 |
5 | lv_getAssistanceCode() | 需要进一步逆向 |
6 | OfflineValidationWindowController | 该步骤弹出图3Offline Validation窗口,需进一步逆向[OfflineValidationWindowController okButtonClicked:] |
7 | checkSavedRegistrationAndDisplayUserInfo | 该函数检查保存到UserDefaults中的注册信息并显示。返回值需要为true。 |
对preCheckLicenseAndPresentError逆向,
图6
图6. preCheckLicenseAndPresentError函数代码片段图
由于涉及到的函数比较多,逻辑比较复杂;ida pro的截图太多应该放不开。本文中尽量用语言文字讲一下函数流程逻辑,感兴趣的朋友可以对应文字自己用ida pro逆向一下相应函数。
preCheckLicenseAndPresentError函数流程;
1. 首先判断license路径是否存在
2. 判断license内容是否为空
3. 将license文件以plist格式解析
4. 读取其中的Product字段
5. 判断license的Product字段是否为Hopper Disassembler v4
对lv_getAssistanceCode()逆向,该函数流程:
1. 调用sub_10008D34D获取当前电脑的Mac地址
2. 将License解析成Plist结构并获取Order字段
3. 将以上两步字符串通过CRC32、BASE64等截断拼接后返回一个字符
我们需要修改sub_10008D34D的返回值,使其返回一个固定的伪造mac地址(例如:11:22:33:44:55:66),以避免真实Mac地址上传到服务端而导致被封禁或者被溯源。
图7
图7. -[OfflineValidationWindowController okButtonClicked:]函数流程
该函数相对较简单,我们只需要进一步逆向lv_checkSavedSignature即可,并让该函数返回值为true。lv_checkSavedSignature逆向:
1. 从license中获取Order字段
2. 调用sub_10035E0D0检查Order字段的hash值是否与预留的92个Banned Hash相同。(该函数返回值修改为false。)
3. 调用sub_10008D34D获取mac地址(该函数返回值修改为固定伪造值)
4. 调用lv_checkSignature判断mac地址、Order字段以及在Ok页面输入的字符串(保存为SMS字段),是否匹配;(像lv_checkSignature这种函数,其函数体只是纯粹的数学计算、hash计算、密码学计算等,处理最为简单,整个函数体都不要,直接在函数开始插入mov eax, 0x1; ret;两条指令,就搞定了。)
因此我们需要保证或者修改lv_checkSignature返回值为true。当然修改返回值最为简单。
根据图5以及表格1的指导,下面分析[HopperAppDelegate -checkSavedRegistrationAndDisplayUserInfo]函数.
checkSavedRegistrationAndDisplayUserInfo逆向:
1.调用sub_100012E37函数(对license进一步检查并更新两个全局变量)
1). 100864DB1全局变量:0,1值保存license是否检查过了的标记。修改该值为1.
2). 调用sub_1000CA4DE函数(对license Signature、Email、Order、Expires字段检查)
a. 调用sub_10035E0D0判断Order是否Banned。该函数返回值修改为false。
b. 调用sub_10035E182判断Email是否Banned。该函数返回值修改为false。
c. 对Expires字段进行一系列计算判断是否过期。Expires的值伪造的大一些。
d. Signature判定是根据Order、Email、Expires的值进行变换、MD5等操作后比较。比较复杂,不过我们直接修改sub_1000CA4DE返回值为true即可。
3). 调用sub_100012726(获取license的类型)
a. 获取License的Type字段,判断是否Named或者Computer
b. 返回值1:表示Named;2:表示Computer;0:表示当前License无效。因此我们修改该函数返回值为1.
4).10068FAF0全局变量:0表示license合法,1表示license非法。因此该变量的语义应该是:license是否非法标记。修改该值为0.
5). 修改函数返回值为1(true).
2. 调用sub_100012D05(获取license类型)
1). 该函数直接调用并返回sub_100012726的结果。
3. 调用sub_100012c05(获取License中的注册名)
1).判断全局变量10068FAF0(license是否非法全局变量)的值是否为1,如果是则返回demo
2). 调用sub_10001260E获取License中的Name字段。伪造license文件中,我们可以在Name字段放上喜欢的注册名。
4. 显示用户注册信息并返回。我们需要修改checkSavedRegistrationAndDisplayUserInfo函数的返回值为1.
至此,整个离线激活流程就分析完了。
五。破解方法总结
表2. 破解修改点
[RegistrationWindowController -preCheckLicenseAndPresentError] | 修改返回值为1 |
[HopperAppDelegate -checkSavedRegistrationAndDisplayUserInfo]
| 修改返回值为1 |
sub_100012D05 | 修改返回值为1 |
全局变量0x100864DB1 | 修改值为1 |
lv_checkSavedSignature
| 修改返回值为1 |
lv_checkSignature
| 修改返回值为1 |
sub_10008D34D | 修改返回值为固定伪造mac地址 |
sub_10035E0D0 | 修改返回值为0 |
sub_10035E182 | 修改返回值为0 |
sub_1000CA4DE | 修改返回值为1 |
sub_100012726 | 修改返回值为1 |
全局变量0x10068FAF0 | 修改值为0
|
根据以上的破解过程,我们伪造一个license文件内容如下:
无标号图:伪造License内容(输入原文页面会乱码)
六。破解验证
实施了表2的破解点以后,并使用伪造的license文件(文件名保存为cracking.hopperLicense, 后缀必须为hopperLicense否则无法选中)进行离线激活测试:
图8
图8. 离线验证随便输入验证码
图9
图9. 离线破解成功图
查看about:
图10
图10. 查看About界面license过期日期
七。结语
上周工作日中午牺牲午休时间、再加上周末抽时间搞了以上hopper的离线激活破解。离线破解流程到此看起来阶段性成功了,但真正加载文件逆向时,却时常莫名崩溃,看crash dump文件都是在一个同步锁release的位置。具体原因和解决方法还需要进一步的分析。所以现在来看hopper破解了又没完全破解,希望有兴趣的兄弟把它搞定并分享出来。谢谢。