Dash_crack -- OSX M1(Apple Sillcon) 逆向工程实践
坚决抵制盗版行为,本文仅作 OSX 软件逆向分析及 OSX 软件安全相关的研究目的。
背景
最近在 osx
上找文档工具,想起之前在 linux
上用过一款名为 Zeal
的开源软件,顺藤摸瓜下载了 Dash
,使用体验还不错。
工作环境
- macOS Big Sur 11.5.2 (M1)
- Dash 6.0.7 (980)
- IDA Pro (7.6)
定位激活函数
打开软件尝试注册,发现扩展名未知。根据提示说明可以双击证书文件激活,故可以根据
info.plist
的文件关联信息获取到证书的后缀名为dash-license
.构建一空白文件,再次尝试,提示
The license file does not seem to be valid.
在应用目录下找到
Dash
二进制文件,拖入IDA Pro
,查找错误提示。
- 通过查找交叉引用,定位到成员函数
-[DHInApp processLicenseFile:]
- 速览目标函数,其内部的字符串信息表明该函数与激活存在直接关系。
由于 Dash
是一个通用二进制文件(即包含 arm
,x86_64
)。 使用 x86_64
的反编译结果通常优于 arm
的结果。 hopper
分析同理。
如非必要,在静态分析阶段应优先考虑分析 x86_64
格式。
逐层分析
通过 IDA pro
的反编译,整理出如下伪代码,仅包含需要关注的部分。
1 | if DHInApp::displayString() == "com.barebones.textwrangler": |
以上是经过整理的结果。
以上需要重点关注的部分有两个,
DHInApp::displayString
CFDictionaryRef_sub_100058844
并不能简单的让 DHInApp::displayString
返回正确的字符串,而是需要它走到下面的更新校验信息部分。
图中的 verifier_and_loadDataFromXML
是 CFDictionaryRef_sub_100058844
在分析完功能后重命名的结果。
以下反编译结果均出自 IDA Pro
。
为了方便阅读,下文代码结果均经过重命名处理,并用 // ....
的形式省略无用代码。
深入 DHInApp::displayString
1 | id __cdecl -[DHInApp displayString](DHInApp *self, SEL a2) |
在细说上面的结论前,先观察函数的控制流图。
这是一张有意思的图,通常这种密集的线条极有可能是一个公共的错误处理过程,这里暂称为公共出口。
结合上面的代码,该函数 return
出口共有4个。
其中第一个是激活后一定被触发的,第二是是在激活前必然触发的(可以通过对初始值分析得到)。剩下正确的出口和错误的出口各一个。
LABEL_14
属于图中的公共出口,再结合反编译的上下文看,公共出口即为错误处理。也就是说,流程上不进入 goto LABEL_14
即可完成激活。
checkurl_and_anti_crack
是完成流程的关键函数。
又一次见到了 verifier_and_loadDataFromXML
,后面会对它一探究竟。
接着看一下另外一个感兴趣的点 base64_pubkey
,通过对 create_global_pubkey
函数的内容分析,基本确定该全局变量存放着公钥,内容是通过对字符串 0xAD97B0E07F8C747E2D2......
转换得到。
再见 verifier_and_loadDataFromXML
1 | CFDictionaryRef __fastcall verifier_and_loadDataFromXML(const __CFURL *a1) |
最终需要通过 VerifySignatureFromResourceData
返回字典给外部使用。
1 | CFDictionaryRef __fastcall VerifySignatureFromResourceData(const __CFData *a1) |
这里本质是一个签名验证过程,通过证书中的 Signature
及公钥验证证书本身的合法性。
RSA 签名验证
简单比划一下 RSA签名验证
过程。
1 | -> : 生成 |
上面代码中的 use_sha_construct_sourceData
就值得注意了。
1 | CFTypeRef __fastcall use_sha_construct_sourceData(CFDictionaryRef theDict) |
至此可以得到一个较为完整的算法过程
- 使用某字节转换算法将
base64_pubkey
转换,然后使用kSecBase64Encoding
编码拼接得到公钥 - 把排除
Signature
的字段的值合并为数组,在Sort
排序及其他处理后使用SHA1
取样作为待验证数据 - 通过
待验证数据
pubkey
Signature
完成校验
破解思路
- 公钥替换并实现注册机,需要反推
base64_pubkey
- 字节补丁,对部分流程绕过处理
虽然公钥替换可以相对避免暗桩等问题,但步骤较为麻烦。本文采用字节补丁的方式完成验证。
需要打补丁的有两处:
- 使
VerifySignatureFromResourceData
中的签名验证部分恒返回真 - 绕开
DHInApp displayString
的无效文件验证
同时根据需求字段构造文件构造一个假的 license.dash-license
。
1 |
|
Patch
切换至 arm
版本,定位到关键点,打上补丁。
1 | 000000010004f7c8 CMP X8, X0 -> CMP X8, X8 |
尝试激活,简单修改系统时间验证。
至此基本完成了Dash的破解思路验证。可能会有其他暗桩需要在实际使用中遇到后分析处理。
总结
本文主要是窥探一下 OSX
软件的常用验证方法及破解思路,欢迎讨论与共同学习。
Dash
是一款很不错的软件,建议有能力的朋友可以支持正版软件发展。