-
-
[分享]2021 SDC:《houdini编译机制探索》PPT分享
-
发表于: 2021-10-29 09:33 20223
-
尽管houdini技术有着得天独厚的优越性,可国内对此的相关研究还处于启蒙阶段,尤其是其核心的实时指令转换的资料基本没有。
该议题通过对houdini机制的深入分析,从Houdini的前生今世,到其内部逻辑原理,指令篡改,代码校验......能够尝试理清其编译的诸多实现细节。为我们在模拟器领域对代码执行层面构建更为深入有效的安全方案提供技术基础。
下面就让我们来回顾2021看雪第五届安全开发者峰会上《houdini编译机制探索》此议题的精彩内容。
张玉璞:腾讯IEG业务安全部手游对抗负责人,腾讯安全技术专家。
曾在盛大、淘宝等多家企业长期从事游戏开发及二进制安全相关工作。2012年加入腾讯业务安全中心,2013年开始负责手游客户端安全对抗的方案建设及团队管理工作,主导创建了手游客户端安全对抗团队及手游核心对抗方案。
以下为速记全文:
大家好,我是来自腾讯互动娱乐事业群的张玉璞,此次想和大家分享的主题就是《houdini编译机制的探索》。
01
个人及团队介绍
首先介绍一下我所在的团队和以及个人。
我们是腾讯互动娱乐事业群下面的业务安全部,我们主要的工作其实都是为了游戏的安全,通过我们建立的Anti Cheat Expert安全方案,为游戏在外挂对抗、内容安全、反打击、账号安全以及黑产管控等泛安全的领域实行全面的保驾护航。个人刚才我们的主持人也已经介绍了,所以我就不用再过多介绍了。
02
从一次模拟器上的外挂对抗谈起
回到我们的主题,不知道大家有多少接触过Houdini的转译系统,对于我们来说的话,其实我们也是大概从19年底20年初开始了解到这样的一个转译系统,并且开始对它做相关的一些研究的。
为什么会接触到这样的一个系统?因为我们是做游戏安全的,所以其实我们是从一个模拟器上面的外挂对抗开始接触到这样的一个系统的。在传统的模拟器的外挂上面,其实它的做法和移动端的外挂没有太大的区别,它还是修改的是安卓的游戏进程内的各种代码以及数据,但是在19年底我们发现了有一种新类型的外挂,这种外挂它修改的不是安卓进程空间内的,它修改的是PC进程的x86代码。
既然是改了PC进程,我们第一个想法就是说我们移动端的方案是不是搞不定了,所以我们会有这样的一个想法就是说我能不能够模拟器去使用PC端的方案呢?
如果我们只探讨,比如说我们可以接入方案之后的话,我们肯定是可以去这样去应用的,但是市面上的模拟器有很多,并不是所有的模拟器都会去接入你的 PC端的方案,我们怎么样去保证我们在各个模拟器上都能够对 x86代码进行校验?
03
Houdini的前世今生
结果我们后面发现,实际上经过Houdini的这套系统之后,x86的代码它同样会被映射进安卓的游戏进程内,所以我们其实是可以去由游戏进程类的移动端方案去感知PC进程生成之后的x86代码的一个正确性,这个就使得我们对于houdini的这套系统产生了一个比较浓厚的兴趣,因为这样我们可以用移动端的方案去校验PC进程内的x86代码,所以我们就对于 houdini的系统开始进行了一些初步的了解。
Houdini这样的一个转译系统其实是英特尔在为了他在手机上面去运用x86架构的cpu,他为了实现这种x86架构的CPU可以去比较好的支持原生的基于arm指令集编译的apk,所以建立了这样的一套转译系统。
但是这一套的转译系统其实不止应用于x86的架构下的手机,由于它的一个性能要相对于传统模拟执行的方式要快很多,所以现在各个主流的模拟器全部使用了这套houdini的转译系统,所以hold it的一个技术特点就是快,它不但通过git的方式,而且的话它整个生成的我们在后面会介绍的一些很特殊的变化性很强的这些实现机制,都是为了尽可能的在一次转移译中只生成必要的内容来保证这样的一个效率。
而同时houdini是一个目前英特尔并没有正式公开的技术,所以在网上也基本上我们也尝试着找过,基本上是没有任何相关的一些资料的。
所以我们对于houdini的研究只能够通过逆向,目前houdini已经有到了七点几的一个版本,我们在最开始遇到的时候还是在四点几的一个版本,但是通过我们从四点几的版本到七点几的版本的各个适配来看的话,它的整个的核心机制是完全一模一样的,所以后面的介绍主要是依据于houdini4的一个版本来做。
3
逆向分析实例
我们现在来开始去分析houdini的实现机制。在分析合理的实现机制之前,我们先看一下这是同一个函数两次编译之后的一个汇编代码的内容。可以看到虽然是同一个函数,但是这两个汇编代码长的也不是很像啊。
首先第一个我们会发现这两个函数的地址就已经不一样了,差得很远。
第二个不知道大家能不能仔细看到这个内容,在这个代码的主体的两个代码块,红框标识的两个代码块里面,这个代码的内容也不一样,而且这两个代码块的内容也没有再出现在彼此的这两次编译之后的代码里面,为什么这两个代码在第二次编译之后就没了?
第三个,如果我们再去看这个代码块里面的细节,我们会发现这些计寄存器变化,第一次使用的是ebx,第二次使用的是eax另外原先的JNZ在这里变成了JZ这个可以看到只是同一个函数的两次编译,就已经有了这么多的变化。
4
猜想
让我们不得不对Houdini的生成机制有一些技术的猜想,来辅助我们去分析Houdini他大概会怎么样去做。
首先就是这个地址是漂浮不定的,那么这个对于动态系统来说的话,我们觉得也是很好理解的,所以它很有可能是通过为了效率采用jit的一个生成机制,但是jit的生成机制,它就会要去解决一个问题,因为有的代码块先生成有的代码块后生成,那么如何去处理这些代码块之间的一个跳转的实现?
它是通过一个比如说都跳到一个公共的逻辑中,然后有公共的逻辑进行分发,还是说去进行彼此的一个直接跳转?如果是直接跳转的话,谁来把这个跳转去做一个修改?
第二,一个函数它编译的代码片段,有的时候有有的时候没有,那么就说明Houdini的这一套编译机制的话,它一定不是以函数为单位的,它应该是一个更小的呃逻辑区分来作为代码块的划分的,那么它划分的原则又是什么?我们必须要弄清楚。
第三,不断变化的代码细节,x86的寄存器器为什么会变化?代码块以什么样的形式去访问arm寄存器的内容,那么因为x86的寄存器肯定不会和arm的寄存器有映射了,因为它是在不断变换的,以及除了寄存器变化的信息,包括我们前面看到的跳转信息的变化之外,还有没有其他可能随机生成的一些内容?
最后除了前面这几个我们在前面的实例里面看到的内容之外,还有一个没看到的部分,那还没有生成的代码块去哪里了,没有生成的代码块,它又是以什么样的机制去进行生成的,以及生成之后,它是如何和这些已生成的代码块形成一个拼接的呢?这个就是我们对于Houdini的这些实现在最初产生的一些猜想。
分析目标与方法
而回到我们分析的目的来说,我们既然是做外挂对抗,那么我们的分析目的第一,我们要能够明确区分出,你的执行环境是在Houdini的执行环境之内,还是在手机或者说其他的一些执行环境之内。
第二,如果你是在houdini的执行环境之内,我们如何去校验这个代码的正确性?有了这样的一个分析目标,我们就有了一个实际的分析方向来对这个目标进行拆解。
第一,我们既然要分析和定力完成这个目标,我们首先要知道 houdini中的内存布局大概是一个什么样的情况。然后的话前面的代码块,我们知道它不是以函数为单位,那么它的划分规则我们要弄清楚,在弄清楚了代码块的划分规则之后,那么它一定会有一个管理的机制,管理的机制是什么?
以及代码块中它会有各种各样的跳转,而如果外挂插入了一个hook,或者他修改了一个跳转,我们怎么样去区分这个跳转是正常的还是异常的。我们得弄清楚它控制流的一个机制,最后我们要知道代码块在什么时候开始,以及代码块在什么时候结束,然后我们最终去理清代码,快速的了解各个指令它翻译的一些大概机制以及它的一些细节,通过这些内容的了解,我们才能够去达到我们的目标。
6
逆向思路
但是前面我们也说了,houdini其实是没有任何的技术资料在对外公开的,所以要想去分析这各个点,我们只能够通过逆向的方式来做,那么如何逆向就是一个问题。
我个人的经验而言,我觉得逆向很像一个拼图游戏,我们大家不知道在玩拼图的时候是不是都是按照这个思路来做的,就我个人而言的话,在这个拼图里面我会首先找到一些单个的拼图,这些拼图的特点是什么呢?有它的图形里面是可以根据图形信息完全很容易的定位到它在整个拼图的一个相对位置。
然后基于这些固定的拼图信息,我们再向外进行扩展,找到它相关联的更多的拼图,然后逐步的通过往外延伸的延展的方式来完成整个评图。而逆向我觉得和这个思路非常的相似。在整个立项和定义中,我们首先要找到什么东西是可以固定的,并且和丁力一定不会对它进行转移更改的,然后我们又可以重复利用。
对我们来说主要是在arm端的里面的一些信息,比如说 arm中的各个函数的首地址,函数的块中的跳转地址,调用代码的一些地址,以及函数中一些对于成员变量访问时候的这些偏移信息,这些无论houdini在怎么样去变换,这些内容他基本上都得保持一致,因为他最终他要去和arm端去做一个比对的翻译过程,所以这些内容一定是会固定下来的,并且houdini会去做应用的。
所以依据这些固定的内容,我们就可以去建立一个快速逆向的思路。首先我们可以dump得出整个进程空间内的内存,而然后dump它会为我们带来两个分析的一个好处,第一个因为houdini本身是一个动态执行的系统,所以它有很多内容可能每次都会在不断的变。通过dump一次,我们可以保证在这一次的dump里面,所有代码块之间的信息,代码块和代码块之间的管理的信息它一定是固定的。
另外通过多次的dump进行比对,我们可以找到houdini在执行中的一些共性。然后有了这个dump之后,我们就可以前面提到的这些函数信息,跳转信息,调用信息,以及通过偏移的信息所定位出来的x86的代码块的首地址呢,来对整个内存进行搜索,从而定位到它的对于代码块跳转的管理机制的一些数据结构。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课