做设计的朋友注意了!看完此文,你将获得永久的Sketch破解版

本文旨让网络安全专业人员更好地了解网络和系统中的潜在漏洞风险,提高其攻防技巧。接下来将向你展示如何只使用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
我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章