Sandboxie是Windows上的虚拟环境,可以用来测试不受信任的软件,类似轻量级的虚拟机,用于隔离安装流氓软件也十分方便
Sandboxie最初是收费的商业软件,后来停止开发并开源,现在由github的开发者David Xanatos维护,添加了许多新功能,称为Sandboxie Plus,但这些新功能需要向作者赞助获取激活码才能使用
既然有源代码,不妨以此为例分析软件的注册机制,以及逆向思路
二、分析
首先下载Sandboxie Plus的安装包,安装后打开设置,看到以下捐赠界面:
1.png (71.62 KB, 下载次数: 0)
下载附件
2023-6-3 17:47 上传
先随便输入,弹出错误提示:
2.png (73.02 KB, 下载次数: 0)
下载附件
2023-6-3 17:47 上传
意思是格式不对,那么接下来就去源码中找到对应的部分。可以git clone到本地然后用Visual Studio打开,也可以用github自带的搜索,结果如图:
3.png (108.89 KB, 下载次数: 0)
下载附件
2023-6-3 17:47 上传
注意到location标签包含了调用处的文件和行号,于是找到对应位置:
4.png (114.71 KB, 下载次数: 0)
下载附件
2023-6-3 17:47 上传
分析ApplyCertificate函数的逻辑,参数Certificate是一个字节数组,首先判断许可证是否为空,然后通过GetArguments函数解析为指定格式。下面是两个判断,NAME和SIGNATURE不能为空,否则就弹出刚才的错误提示。如果格式正确,就把许可证写入文件。
继续看,发现下面调用了theAPI->ReloadCert()然后进行条件判断,意识到ApplyCertificate函数的作用只是对许可证的初步处理,验证逻辑并不在这里。
5.png (57.35 KB, 下载次数: 0)
下载附件
2023-6-3 17:47 上传
那么继续看theAPI是什么,然而文件中找不到定义,头文件也没有,最后发现在stdafx.h中(这一步用VS就方便的多)
6.png (80.14 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
原来它是CSbiePlusAPI*类型,但在这个类中没有ReloadCert()函数,于是到父类CSbieAPI中查找。在SbieAPI.cpp找到定义,只有一行,调用了ReloadConf(SBIE_CONF_FLAG_RELOAD_CERT),紧接着就是它的定义:
7.png (58.69 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
大概是对参数进行处理,然后调用IoControl,继续跟进:
8.png (19.53 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
发现处理后的参数最终传入Windows的系统函数NtDeviceIoControlFile,这个函数有些陌生,上网查找它的用法,其中API_SBIEDRV_CTLCODE是关键,这是设备IO控制代码,用于指定要执行的具体操作。于是全局搜索这个名称,最后在驱动中找到了处理位置:
9.png (69.11 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
继续往下看,这里首先把参数buf赋值到user_args,第一个元素就是函数的编号,Api_Functions是存放函数指针的数组,然后计算出下标,取出对应的函数指针。
10.png (37.05 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
继续搜索,发现Api_Functions由Api_SetFunction函数初始化:
11.png (40.52 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
寻找调用处,原来在另一个文件中:
12.png (33.35 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
这个API_RELOAD_CONF就是传给驱动的参数,它对应的函数指针是Conf_Api_Reload,发现它很长,但关键的地方在前几行:
13.png (30.92 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
看到调用了MyValidateCertificate(),最后返回status,这很可能就是验证函数。紧接着搜索它的定义,发现它又调用了KphValidateCertificate():
14.png (43.17 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
继续跟进,终于来到了真正的验证函数:
15.png (83.4 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
这个函数特别长,但是还得仔细看,因为它修改了一些全局变量,也就是有副作用,如果直接写return 0可能导致意外状况。看到它首先调用了MyInitHash,转到它的定义,分析一下,它的作用是初始化pHashObj的成员,其实都是指向算法函数的指针:
16.png (60.63 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
接着往下看,下一处验证在这里,功能是验证许可证有效期和类型,格式错误则验证失败:
17.png (57.03 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
至于许可证类型到底有哪些,别急,先往下分析。
18.png (26.21 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
这里又出现了两个函数MyFinishHash和KphVerifySignature,分别查看定义:
19.png (53.64 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
经过分析,它的作用是计算许可证的hash,如果成功则返回STATUS_SUCCESS。但它只是完成了计算,校验不在这里,不需要修改。
而KphVerifySignature就是真正的验证函数了,分析这段代码,发现它是个纯算法函数,仅仅判断hash是否有效,而不创建或者修改外部资源,因此可以放心修改,方法是在第一行直接返回STATUS_SUCCESS:
20.png (86.64 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
经过这么多判断之后,终于来到了Verify_CertInfo.valid = 1;这一行。经过以上分析,发现它就是除了返回值以外验证许可证的条件之一。如果只关注返回值,却没发现函数对外部变量的修改,验证依然会失败。在商业软件中,会用到更复杂的多个变量验证的方法,就是所谓的“暗桩”。
21.png (99.81 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
接下来是确定许可证的类型和有效期,代码逻辑很清晰,这时发现一个隐藏属性CONTRIBUTOR,能直接跳过有效期判断。去官网看了一下价格,相当于赞助1000欧获得的Huge Supporter Certificate,果然内鬼比土豪更可怕……
22.png (97.11 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
所以许可证的TYPE可以写CONTRIBUTOR或者PERSONAL-HUGE,DATE都可以省略了。
回顾一下,实际上修改的只有一个函数。因为重点是分析和理解验证的流程,如果要实现任意输入都能通过验证,需要在多处进行修改,不利于初学者理解。
接下来就要验证是否破解成功,这里有个简便的方法,因为代码库在github上,而且配置了CI/CD,因此只要fork原仓库,提交自己的修改,等待自动构建完成,下载生成的Artifacts就可以了。这样就无需在本机花费大量时间和空间安装Qt,VS和WDK。如果想在本机编译,装好环境后用VS打开工程编译即可,可以参考readme,这里不详细讲述了。
三、验证
前面说过,为了让修改尽可能简洁,并不是随便输入都能通过验证。根据前面的分析,构造出如下许可证:
NAME: 52pojie //任意
DATE: 01.04.2099 //非必需
TYPE: CONTRIBUTOR //或PERSONAL-HUGE
SOFTWARE: Sandboxie-Plus
UPDATEKEY: 123456789 //非必需
SIGNATURE: www.52pojie.cn //任意
最后还有一个问题,Windows内核驱动必须签名才能加载,签名是需要花钱从微软买的。这个问题确实没有什么好办法,所以可以猜到,作者为什么要把验证放到驱动层了吧。
一个临时方案是暂时关闭Windows的驱动签名验证,方法是以管理员身份打开命令行,输入bcdedit /set testsigning on,重启即可。但这带来一些问题,比如游戏反作弊系统检测到禁止驱动签名则不能启动,桌面右下角会有测试模式的水印,安全性下降等等。
先不管那么多,看看修改后的效果如何吧:
23.png (40.86 KB, 下载次数: 0)
下载附件
2023-6-3 17:48 上传
验证成功,所有功能都正常使用
备注一下,在verify.c中还发现两个名称相近的函数KphVerifyFile和KphVerifyCurrentProcess,逻辑与KphVerifySignature相似,暂时没发现具体作用,不过也进行了修改。许可证的SIGNATURE属性少于6个字符会验证失败,猜测与hash函数的计算方式有关,暂时没做进一步分析。
四、总结
这次分析的软件虽然验证方式简单,但从中能总结出常见的验证流程以及破解方法。通常,破解从验证提示处入手,一步步跟踪找到验证函数的位置,大体是这样的流程:
提示窗口 -> 验证函数1 -> ... -> 验证函数n -> 算法函数
而某个需要验证的功能函数是:
功能函数 -> 验证函数1 -> ... -> 验证函数n -> 算法函数
很多时候,输入许可证和调用功能,验证时中间过程是不一样的,但如果能找到并修改最底层的算法函数,那么无论验证的中间过程是什么样的,最后进行计算时总能得到我们想要的结果。就像能控制一加一等于几,那么再复杂的算法结果也能操纵,比起修改中间过程更加彻底。
假如不修改算法函数,那就必须逐一分析验证过程中调用链的每个函数。修改返回值有时候是不行的,因为它们常常有副作用,创建或修改了一些外部变量,然后在另一个隐蔽的地方进行验证导致失败。另外,输入许可证时和调用功能时的验证函数可能是不同的,但大多数时候默认两者相同或部分相同。而遇到防护严密或者验证过程不寻常的软件,这个假设就不成立了。
在实际应用中,分析反编译的代码比源代码困难的多,需要经验和技巧的积累。而Sandboxie Plus作为开源的付费软件,很适合新手用来学习和练手,了解常见验证机制的流程。
版权信息
本文涉及的源代码使用GPLv3授权,在以GPLv3发布的前提下可以任意修改,来自SandboxiePlus/LICENSE:
Sandboxie-Plus is made up of the following components, governed under various licenses:
* MiscHelpers a generic Qt based helper library, license under the LGPL.
* The Qt Framework which is license under the LGPL.
* SandMan the primary Sandboxie-Plus UI component, provided under a custom license.
* QSbieAPI a stand alone re implementation of sandboxie’s API using IPC mechanisms to. communicate with sandboxie’s core components, license under the LGPL.
* Sandboxie core components, licensed under the GPL v3.
* UglobalHotkey is an extension for Qt framework, which implements global hotkeys functionality and is in the Public Domain.
* QtSingleApp a Qt Solutions Component that provides support for applications that can be only started once per user, BSD licensed.
ALL THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.