本文旨让网络安全专业人员更好地了解网络和系统中的潜在漏洞风险,提高其攻防技巧。接下来将向你展示如何只使用Ghidra SRE轻而易举地破解Sketch。为什么要选择Sketch作为破解对象呢?Sketch 是一款适用于所有设计师的矢量绘图应用,特点是容易理解,上手简单,对于有设计经验的设计师来说,入门门槛很低,但就是软件太贵。从2019年九月到2020年二月的半年中,Sketch 经历了高密度的更新节奏,从58版本一路更新到了62版本,一些已有功能得到了优化和补充,也有一些全新的大功能,本文我用的版本是Sketch v63.1(最新更新)。另外,我再简单介绍一下Ghidra SRE,Ghidra 是由 NSA 的研究理事会为 NSA 的网络安全任务开发的软件逆向工程(SRE)框架,它有助于分析恶意代码和病毒等恶意软件,并可以让网络安全专业人员更好地了解其网络和系统中的潜在漏洞。
破解之前的准备
我强烈建议你为SketchApp(以后简称为Sketch)可执行文件创建备份,该可执行文件的通常位置在/Applications/SketchApp/Contents/MacOS中。唯一要做的就是将Sketch复制到同一目录中,但使用不同的名称。
打开Ghidra并创建一个新项目,从上述视图中导入Sketch可执行文件。要导入,只需将可执行文件拖到Ghidra中的项目视图即可。具体过程,请参阅Ghidra文档或这段精彩视频,以了解有关Ghidra基础的更多信息。如果你需要停止并稍后继续工作,这将简化你的逆向工程。现在,双击Ghidra项目3的Sketch,然后让Ghidra完整分析该项目(可能需要几分钟)。
查找可用的试用版
有不同的方法可以找到试验实施的位置,第一个也是最重要的是使用XREF字符串剩余的试用天数,这在启动Sketch时会显示。另一种方法是在“符号树”窗口中搜索字符串BCLicenseManager。由于上述字符串直接引用方法numberOfDaysLeftInTrialMode,因此我们也可以在同一窗口中完全搜索该字符串。
当我们将Ghidra指向此函数时,我们可以看到下一个伪代码;它接受两个参数。有趣的参数是param_1。此参数的严格要求是参考使用哪种许可证。如果遵循参考,则Sketch中有两个选项可用:BCRegularLicense和BCCloudLicense。一种用于离线激活,另一种用于基于云的激活。因此,此BCLicenseManager类具有许可证选择器,该选择器返回一些许可证实例。
long_long numberOfDaysLeftInTrialMode(ID param_1,SEL param_2) { ... puVar1 = _objc_msgSendSuper2; uVar3 = (*(code *)_objc_msgSendSuper2)(param_1,"license"); uVar3 = _objc_retainBlock(uVar3); (*(code *)puVar1)(uVar3,"remainingTimeInterval"); uVar4 = (*(code *)puVar1)(&_OBJC_CLASS___NSDate,"dateWithTimeIntervalSinceNow:"); uVar4 = _objc_retainBlock(uVar4); (*(code *)_objc_retain)(uVar3); uVar3 = (*(code *)puVar1)(&_OBJC_CLASS___NSCalendar,"currentCalendar"); uVar3 = _objc_retainBlock(uVar3); uVar5 = (*(code *)puVar1)(&_OBJC_CLASS___NSDate,"date"); uVar5 = _objc_retainBlock(uVar5); uVar6 = (*(code *)puVar1)(uVar3,"components:fromDate:toDate:options:",0x10,uVar5,uVar4,0); uVar6 = _objc_retainBlock(uVar6); puVar2 = _objc_retain; (*(code *)_objc_retain)(uVar5); (*(code *)puVar2)(uVar3); lVar7 = (*(code *)puVar1)(uVar6,"day"); (*(code *)puVar2)(uVar6); (*(code *)puVar2)(uVar4); return lVar7; }
接下来,我们将调用函数remainingTimeInterval,然后再进行一次计算,该计算用于通过currentCalendar和dateWithTimeIntervalSinceNow使用剩余时间。如果我们搜索名为first (remainingTimeInterval)的方法,我们可以看到我们通过BCLicenseManager获得两个可能的许可证类引用是非常正确的。
我们将使用-in BCRegularLicense,因为我们不需要处理云保护和向/etc/hosts添加内容让我们看看里面是什么。我们在其中调用了一些有趣的函数:validityInterval,它基本上与endTime(当许可证到期时)和networkTime/currentTime的组合一起工作。我们还有通知impl的isValid方法,如果许可仍然可用。
double remainingTimeInterval(ID param_1,SEL param_2) { ... puVar2 = _objc_msgSendSuper2; uVar1 = (*(code *)_objc_msgSendSuper2)(param_1,"validityInterval"); uVar4 = _objc_retainBlock(uVar1); uVar1 = (*(code *)puVar2)(uVar4,"endDate"); uVar1 = _objc_retainBlock(uVar1); uVar6 = (*(code *)puVar2)(param_1,"networkTime"); uVar5 = _objc_retainBlock(uVar6); uVar6 = (*(code *)puVar2)(uVar5,"currentDate"); uVar6 = _objc_retainBlock(uVar6); (*(code *)puVar2)(uVar1,"timeIntervalSinceDate:",uVar6); puVar2 = _objc_retain; (*(code *)_objc_retain)(uVar6); (*(code *)puVar2)(uVar5); (*(code *)puVar2)(uVar1); (*(code *)puVar2)(uVar4); cVar3 = (*(code *)_objc_msgSendSuper2)(param_1,"isValid"); auVar7 = ZEXT816(0); if (cVar3 != '\0') { auVar7 = ZEXT816(extraout_XMM0_Qa); } auVar7 = maxsd(auVar7,ZEXT816(0)); return SUB168(auVar7,0); }
让我们看看在名为BCRegularLicense的此类中还有哪些其他方法可用。首先,过滤“符号树”窗口以反映名称,找到后,滚动到method_list_t,在其上单击鼠标右键并使用“显示引用为”。
如果在“程序集视图”窗口中向下滚动,就会发现isExpired列表,让我们看看里面是什么。
char isExpired(ID param_1,SEL param_2) { ... if (lVar4 == 0) { // expired bVar6 = true; } else { // yet to expired uVar3 = (*(code *)_objc_msgSendSuper2)(param_1,"networkTime"); uVar3 = _objc_retainBlock(uVar3); uVar5 = (*(code *)puVar1)(uVar3,"currentDate"); uVar5 = _objc_retainBlock(uVar5); cVar2 = (*(code *)puVar1)(lVar4,"containsDate:",uVar5); puVar1 = _objc_retain; bVar6 = cVar2 == '\0'; (*(code *)_objc_retain)(uVar5); (*(code *)puVar1)(uVar3); } (*(code *)_objc_retain)(lVar4); return (char)bVar6; }
我们基本上有一个简单的方法来检查试用是否到期。
破解过程
如果你用的是bVar6,它对于过期的许可证可能是true,对于未过期的许可证可能是false。在Ghidra列表(Dissasemble)中使用此方法,然后找到一个ASM函数,该函数将0x1(true)的值移动到地址0x1004a2a50的R12B(bVar6)。
... 1004a2a4b 41 ff d5 CALL R13=>__stubs::_objc_release undefined _objc_release() 1004a2a4e eb 03 JMP LAB_1004a2a53 LAB_1004a2a50 XREF[1]: 1004a29ec(j) 1004a2a50 41 b4 01 MOV R12B,0x1
要修补可执行文件,请右键单击该地址上的指令,然后单击“修补指令”,或者你可以选择地址并按键盘快捷键Shift + Command + G。修补此指令以始终返回0x0(false),这意味着试用将永远不会过期。具体请参见下图以获取修补的ASM指令。
在地址0x1004a2a4e处,我们看到了初始JMP指令,该指令会跳转(转到)检查过程。我们需要修补此指令以跳转到0x1004a2a50的补丁。最终的代码汇编如下所示。
1004a2a48 4c 89 ff MOV param_1,R15 1004a2a4b 41 ff d5 CALL R13=>__stubs::_objc_release undefined _objc_release() 1004a2a4e eb 00 JMP LAB_1004a2a50 Jump to return `false` instruction -+ LAB_1004a2a50 XREF[2]: ....................... | 1004a2a50 41 b4 00 MOV R12B,0x0 Always return `false` on isExpired __stubs::_objc_release] undefined _objc_release() 68 12 00
绕过Sketch代码签名
不过目前我们还是没有破解Sketch,Sketch试图化解我们的破解,比如它检查代码签名,这意味着如果代码签名无效,它将在运行时退出。由于我们修补了二进制文件,因此应用程序的签名将无效。但是与其他化解技术类似,该技术很容易解决。
在Ghidra项目中,进入Dissasemble视图中的0x1004a1724,你将看到此代码。
1004a1736 85 db TEST EBX,EBX 1004a1738 0f 85 58 JNZ LAB_1004a1896 01 00 00
在我的Twitter帖子上,我说明了如何找到此地址。
我通过在退出系统调用之前设置一个断点,然后在dissasembler中一步步找到这个地址的。如果你尝试打开签名错误的SketchApp,则系统会显示BAD_CODE_SIGNATURE代码错误。因此,我知道该错误是由于签名错误所致。然后,我接着检查是哪个指令引用了此调用。
无论如何,在地址0x1004a1738处是指令JNZ (Jump not equal),该指令调用代码签名方法并退出Sketch,只需将此跳转替换为下一个指令0x1004a173e即可。
1004a1736 85 db TEST EBX,EBX 1004a1738 0f 85 00 JNZ LAB_1004a173e 00 00 00 LAB_1004a173e XREF[1]: 1004a1738(j) 1004a173e 49 89 c7 MOV R15,RAX Jumps here 1004a1741 4d 89 f4 MOV R12,R14
同样,将地址0x1004a1879的JZ(Jump equal)编辑为指令JNZ。
LAB_1004a186b XREF[1]: 1004a180f(j) 1004a186b 4c 89 ff MOV RDI,R15 1004a186e 41 ff d6 CALL R14=>__stubs::_objc_release undefined _objc_release() 1004a1871 4c 89 ef MOV RDI,R13 1004a1874 41 ff d6 CALL R14=>__stubs::_objc_release undefined _objc_release() 1004a1877 84 db TEST BL,BL 1004a1879 74 1b JZ LAB_1004a1896
这两个函数都引用了FUN_1004a1896(当代码符号无效时退出),因此我们必须修复它们以绕过这个检查。另外,通过导出程序函数导出你在Ghidra中的二进制文件。
二进制代码签名
导出二进制文件后,你将在导出位置获得Sketch.bin。然后将此文件移动到/Applications目录中的SketchApp捆绑包。然后,重命名并chmod你的可执行文件。
# In /Applications/Sketch.App/Content/MacOS $ mv Sketch.bin Sketch $ chmod +x Sketch
接下来,对二进制文件执行代码签名。我必须先从应用程序签名中删除空的填充部分。
创建一个新证书
要创建证书,请在MacOS中打开“钥匙串访问”。然后转到菜单栏中的“钥匙串访问”,选择“证书助手”,然后单击“创建证书”。输入你的姓名,并确保选择正确的证书类型。
现在,对新的二进制文件进行代码签名。
# Sign the application $ codesign --deep --force -s "signature" /Applications/Sketch.app # If you get errors while codesigning, try this $ xattr -lr /Applications/Sketch.app # lists all empty attrs in app $ xattr -cr /Applications/Sketch.app # clear empty attrs in binary app
到此为止,破解成功。
如果需要让Sketch永远处于破解状态,则可以通过禁用SketchApp更新功能实现。
$ defaults write com.bohemiancoding.sketch3.plist SUEnableAutomaticChecks -bool false
本文翻译自: https://duraki.github.io/posts/o/20200214-sketch.app-patch-in-ghidra.html如若转载,请注明原文地址: