代码设计的原则,代码保护之道——混淆的艺术

本文内容来自齐林在QCon上分享的主题《代码保护之道——混淆的艺术》,主要涉及黑产现状、通用防御架构、攻击流程、混淆理论。黑产现状,盈利手段

黑产的现状和常见的获利手段是什么?下面是三个数字:第一个数字是150万,这是2017网络安全生态峰会上评价的黑色产业从业人数。与这个数字相比,目前行业内排名靠前的公司,保安员工都不超过千人,这与黑产相比,实属罕见。第二个数字是1000亿元,是网络安全生态峰会上评估的黑产年产值。根据腾讯阿里两大巨头2017年的财报,两家公司净利润总和接近1000亿元,黑产年产值基本可以和这两大巨头并驾齐驱。第三个是20%,这是根据我们之前的经验评估的营销活动的资金损失率。比如你想做一个新注册的新活动,投入100万吸引用户,最后发现至少有20万进了黑产的口袋。

简单列举黑产的两种盈利场景。第一种是数据库碰撞,就是不断把一些从其他平台泄露的用户名和密码拿到其他平台去尝试。如果登录成功,账户里的资产信息会先被盗,然后被盗的账户会被用来做一些薅羊毛等相关的事情。二是垃圾注册。黑产必须有大量账号才能盈利。黑产会注册几千个小号,为后续活动做准备,这是万恶之源。但实际上,批量注册账号是一件非常昂贵的事情,需要大量的手机号、移动设备等等。工欲善其事,必先利其器,黑产深知这个道理。为了以最低的成本获得最大的利润,外挂应运而生。外挂具有批量化、虚拟化、自动化的能力,可以最大程度的满足黑产的需求。

为了抵御黑产,每个公司都会有自己专门的风控团队,利用各种技术手段来对抗。这是一个通用架构(见图1)。首先,客户端会对收集到的数据进行加密(包括安全签名等。)并请求它通过业务API接口传输到安全网关。网关会有一些实时的策略引擎进行风险控制,包括但不限于IP策略、环境设备、账户属性、行为序列和一些AI机器学习模型等。风控的结果会发送到后端的二次验证系统。如果发现前端请求不合法,会直接屏蔽请求或者弹出验证码等等。如果没有问题,真的会交给业务系统,然后业务系统会做一些相关的处理。同时每家还会有相应的离线策略引擎,通过旁路数据等进行无监督聚类甚至监督机器学习。生成更多的模型,辅助后端风控。

图1

接下来,重点介绍与客户保护相关的工作。整个风控系统是一个非常庞大的环节,仅仅对于客户端来说也是一个非常复杂的系统,包括采集什么样的风控数据,如何保证采集数据的安全性以及实时对抗等等。在移动时代,PC时代几乎所有的WEB服务都落地到了客户端。如果客户的风险控制做得不好,无异于门户大开,直接给黑产提供了攻击的入口。这里介绍一个基于客户的风险控制的例子。我们可以看到,攻击曲线完全降低到冰点。这里其实并没有什么复杂的机器学习方法,只是利用客户端本身混乱的逻辑来达到业务保护的结果。

防御方法论

第一点,世界上的武术都是速度快,不破的。攻防的本质其实是成本和收益的对抗。好的代码混淆会大大增加破解所需的时间和门槛。只要核心代码的更新速度比攻击者快,就能达到很好的防护效果。

第二点,重剑没有巅峰,巧到不行。攻防的核心是基于可信数据的对抗。谁对系统底层的理解更深入,谁就在整个攻防中掌握了主动权。同样,良好的代码混淆可以保证你的数据采集逻辑不被攻击者知道,可以大大提高后端风控数据的准确性和安全性。

第三点是把树叶藏在森林里。安全是商业的天然属性,商业是安全的最佳载体。只有与业务相结合的安全才是真正的安全,孤立的安全很容易被攻击者攻破。混淆业务代码和安全代码。攻击者要想破解,不仅要懂安全逻辑,还要懂整个业务逻辑。同时,生意上的麻烦也会对安全起到很好的预警作用。

黑客攻击过程

在介绍了防御方法论之后,在具体介绍代码混淆之前,先介绍一下黑客的攻击过程(见图2)。

图2

攻击的第一步是找到入口。一般有两种方式:静态分析和动态分析。静态分析是先检查。所以,elf和其他文件,并检查它们的导出表。如果可以如果找不到,请查看字符串表并在字符串中查找信息。你找到之后,也可以定位到相应的位置。如果没有更多的字符串表,就看导入表,因为导入表中可能会引入MD5等一些签名算法。如果没有导入表,则取决于代码逻辑。例如,AES算法有一个S盒。你可以直接在源代码里找到S盒的特征,然后知道它是AES算法。这是一个很好的线索,你可以顺着这个线索找出整体的逻辑。如果所有这些方法都失败了,还有一些技术,比如符号执行,可以用来辅助分析。

动态分析可以先使用常用的trace工具,如ltrace、strace等。分析函数的调用流。对整个流程有个大概的了解之后,就要定位关键的功能入口了。定位关键入口后,需要结合动态调试技术,使用GDB、LLDB等工具,通过单步调试等一些调试方法获取信息。除此之外,还有另一个利器——钩子,结合钩子技术打印出关键参数信息。同时,有些应用会采用一些客户端保护技术,比如防调试、防钩子等。这里需要结合静态分析手段来定位安全防护的入口和代码。如果关键函数入口难以定位,可以定位程序入口进行分析,通过调试程序入口模型定位关键信息。简而言之,攻击过程就是结合以上思路,利用一些安全工具或脚本进行综合分析,最终完成整个应用的反向破解。

常见混淆理论分析

接下来介绍一些常用的混淆理论,包括一些常用的混淆方法、混淆方式及其对应的效果。许多现代代码混淆是基于科尔伯格理论,该理论将代码混淆分为四类:布局混淆、数据混淆、控制流混淆和预防混淆,如图3所示。

图3

下面将继续介绍每个混淆器的工作原理。

布局混乱有两个核心点:重命名标识符和删除信息。所谓的标识符重命名,比如把一个有意义的函数名改成一个无意义的函数名,隐藏函数名所传达的信息。虽然它没有本质上没有任何效果,因为逻辑没有改变了,它减少了信息量,可以提高攻击者的门槛分析。二是删除信息,包括一些调试信息、日志信息、格式化信息等等。

重命名标识符有几种方法。第一个是标识符交换,对于库a中的函数名,使用从库B中提取的符号信息,例如,一个加密函数被称为解密函数。第二种是哈希命名,直接把函数变成哈希的结果。第三种是混淆字典,指的是我们自己产生的一堆混淆信息,比如把0和O放在一起,把1、L放在一起,组合起来命名函数,会让攻击者更难读懂。

第二种是数据混淆,混淆应用程序中的常量,如字符串、整数或符号点。混淆方法有编码变换、变量升级、拆分、数据变函数、合并标量变量等。图4显示了一些数据混淆的例子。

图4

控制流混淆是一种核心的混淆方法,包括不透明谓词、执行混淆、聚集变换等。其中,不透明谓词是控制流混淆的核心。例如,有一个函数可以执行到if分支或else分支。但是,我们可以通过一些转换使它转到if分支,而不是else分支。混淆包括虚假代码块、控制流扁平化、指令替换、控制流切割、VMProtect、代码并行化等。

第四类是防止混淆。顾名思义,我们知道必须用一些软件来逆向这个程序。我们可以对这个软件做些什么,比如插入华丽的指令破坏反编译程序等等。

最后,介绍了基于LLVM编译器的整体保护方案。因为LLVM的logo是一条龙,所以对LLVM的攻击叫与龙共舞,我们在LLVM的基础上实现了深度开发。如图5所示,LLVM框架的整体架构分为前端、IR和后端,整体流程与标准编译原理完全一致。前端主要有预编译、词法分析、语法分析等过程。最后生成中间矢量IR,IR层可以用各种方式优化。这里说的代码混淆主要是在IR层模型中实现的。除了代码混乱,IR本身也能解释执行和基于JIT的执行。同时,还可以通过内联汇编在IR中插入一系列flower指令。包括后端指令选择、有向无环图、指令调度、机器等。最后通过链接生成各种后端架构文件。LLVM对安全性很有帮助。首先,您可以在前端AST解析中做一些安全检查。IR可以是跨平台的代码混淆器,你甚至可以在后端实现自己的指令集,结合自实现的编译器和解释器,完成更健壮的虚拟机保护方案。

图5

文中提到的混淆主要是针对IR层的。它的优点是基本支持所有前端语言,同时混淆代码基本支持所有后端平台,实现一次代码几乎可以保护所有后端平台。同时,它是一种基于编译时的方案,因此具有很高的稳定性。LLIR是一个基于寄存器的SSA格式的强类型指令集,专为代码优化而设计。LLVM应用广泛,包括安全漏洞分析、安全保护、任意函数插装、任意代码替换、自动化测试、自动化分析等。很多自动化测试工具和安全工具基本上都可以通过这个框架来实现。llpass主要处理IR,其结构如图6所示。

图6

代码保护方案中借鉴了Obfuscator-LLVM项目,如控制流扁平化、伪控制流、指令替换等。O-LLLVM是基于编译器的代码保护方案的鼻祖,为后续开发者提供了很好的思路参考,但其开放功能极其有限,部分实现方案存在一些弱点和bug。因此,我们在它的基础上进行了深入的定制和开发,并结合混淆理论,实现了更强的代码保护技术。

基于IR的代码保护原理和思想,如字符串混淆、导入表隐藏、防调试、防篡改等。在分享中详细阐述。

图7

上图显示了一些实现的代码保护效果的例子。通过功能控制流程图可以看到,整体控制流程非常复杂,很多攻击者在这里都被吓退了。此外,控制流分段、花指令等技术的结合会直接摧毁反编译反编译效果等等。

综上所述,结合LLVM框架可以实现高强度的代码混淆和防护,进而帮助风控系统,有效提高整体业务安全性。