逆向 | 白盒免杀
一次性进群,长期免费索取教程,没有付费教程。
教程列表见微信公众号底部菜单
进微信群回复公众号:微信群;QQ群:460500587
微信公众号:计算机与网络安全
ID:Computer-network
源码级免杀,在业内也被称为“白盒免杀”,与通过修改二进制代码的“黑盒免杀”相对应。随着免杀技术与反病毒技术的不断博弈,源码级免杀被提到得越来越多,特别是在2009年以后,各大远控软件的逐步开源更是起到了积极的推动作用。也是基于此,一大批不会编程的初学者被淘汰。
可以肯定的是,随着网络安全体系的不断完善,网络环境相对来说会越来越安全,黑客们进行免杀操作的入门门槛也将逐步提高。这对于我们来说是喜忧参半,喜的是能有效执行免杀操作的黑客将越来越少,忧的是一些技术较高的黑客制作的免杀木马将更难被反病毒软件所捕获。因此,在黑客圈子内部,被称为“白盒免杀”的源码级免杀将越来越被重视,并且最终会成为主流技术。
一、怎样定位产生特征的源代码
相对于行为特征来说,去除文件特征仅仅是免杀工作中的第一步。
对于源码级免杀来说,去除特征码是一件比较容易的事情,但是定位特征码却是一件比较麻烦的事情。
虽然我们平时接触到的木马绝大多数都是用C或C++编写的,但这毕竟不是全部,因此黑客们必须要想出一种能解决所有问题的“万金油”才行。
1、定位文件特征
很好地将源代码与最终生成的二进制代码进行完美对应是比较困难的,如果黑客们不能解决这个问题,源码级免杀也就无从谈起了。当然,这并不代表就没有办法。
在源码层级定位文件特征的第一步仍然是使用类似MyCCL的特征码定位工具,定位出木马的二进制特征码,第二步想办法找出这些二进制特征码究竟是哪一行源代码编译后生成的,最后再酌情修改这段源代码,以达到去除特征的目的。
而这里面最为关键的一步就是找出所生成的二进制代码与源代码的对应关系。
通常情况下,我们所面对的大多数木马都是由C或C++开发出来的,因此接下来将围绕着Visual Studio这一Windows系统下应用最广泛的C++开发工具来进行讲解。
对于使用Visual Studio开发的木马来说,定位文件特征码的方法有很多,网上流传最为广泛的方法就是设置工程的编译选项,进而通过生成的map文件判断某个偏移大致是哪几行源代码生成的。
其实这种方法有很多问题,首先,生成带有行信息的map文件功能在Visual Studio 2003版本以后已经被微软去除了,这就意味着这种方法并不支持Visual Studio 2008/2010,其次,这种方法也比较麻烦,我们完全可以使用Visual Studio自带的查看反汇编的功能进行这种对应工作,整个流程也非常简单。
首先,在Visual Studio中按F10键启动逐过程调试,程序会停在离入口点不远的地方,这样做的目的是避免木马的本体代码被执行。
然后,假设我们找到的特征码起始地址是0x004020A1,此时可以按快捷键Alt+8(或依次选择“调试”→“窗口”→“反汇编”)打开反汇编窗口,按快捷键Ctrl+G转到地址输入栏,之后输入特征码起始地址0x004020A1,并按Enter键,这样便找到了这个特征码所对应的源代码信息,如下所示:
以上是为了举例而编写的一个测试工程,很显然,在现实情况下仅打印一句"Hello World!"是不会被定位为木马的特征码的。
最后,随意选中其中的某行代码并单击右键,在弹出的菜单中选择“转到源代码”即可自动跳转到与之对应的源代码处了。
但是我们都清楚,并非所有的木马都是使用Visual Studio这个IDE开发的,那我们要怎样面对不同的情况呢?以经验来看,如果黑客使用的是其他语言的IDE,那么他理应对它所使用的环境比较熟悉,相信总有一种方法能很好地解决二进制代码与源代码的对应问题。
当然,如果碰到极特殊的情况,那就只能通过在源代码中大量地安插类似于MessageBox的这种无意义语句,来定位产生特征的源代码了。
这种方法与MyCCL的原理有些类似,基本的思路就是二分法查找,我们可以先在整个工程中安插几十个不等的MessageBox,并使用其参数进行编号(例如MessageBox(NULL,"001","",MB_OK);),然后查看定位出的特征码离哪个MessageBox最近,并在附近以更高的密度安插MessageBox,最终便能定位出产生特征码的源码位置。
以上是总结的一些小技巧,您可以发挥思维找到更适合自己的、更好的定位源码特征的技巧。
2、定位行为特征
想要快速定位出行为特征,就要先了解反病毒软件是通过什么检查方法来判定程序行为的。就目前来说,有以小红伞为代表的启发式检查方法和以卡巴斯基为代表的虚拟机检查方法。另外,还有国内比较常用的基于HOOK技术的沙盒检查方法。
在以上3种检查方法中,启发式检查应用的原理比较类似于特征码检查,所以,一般在程序真正运行之前就能判定出这是否是一个可疑程序。对于这种情况只能使用特征码定位的方法突破了。
而后两种方法则是基于程序行为来分析的,它们会通过收集程序运行的一些行为,来判定此程序是否可信。
以虚拟机检查为例,在用户双击运行程序时,这一操作会被反病毒软件所拦截,并在其设定好的虚拟机环境中运行这个程序。如果经检查没有问题,则对用户的双击执行请求放行,反之则给出警告。
换句话说,黑客们可以通过控制程序仅执行部分我们选择的代码,从而用排除法判定触发警告的操作究竟是哪些。
比如程序中有一步操作是远程注入代码并执行,如果你怀疑正是这个操作触发了警告,那么可以在执行这个操作之前插入一条MessageBox。我们都知道,MessageBox弹出对话框会使整个程序暂停,用户必须单击对话框上的某个按钮后程序才会继续执行相应分支的代码。但是在虚拟机中,显然不会有人单击那个对话框,因此这就起到了一个断点的作用,也就是说程序会在执行某些代码之前中断下来。
这样一来,我们就能很容易地判定程序的某一行为是否会触发警告了。
二、基于源码的特征修改
想要基于源码修改特征码,扎实的编程功底是必不可少的,仅靠学习一些技巧是很难将免杀技术研究长久地持续下去的。
其实就基于源码的特征修改来讲,更多的只能是消除程序的文件特征,但很难消除程序的行为特征,因为行为特征更多的是考验黑客们对操作系统的了解程度。下面讨论的内容以修改文件特征的免杀方法为主。
1、变换编译器与编译选项
变换编译器与编译选项是进行源码免杀时最为简单,也最为有效的方法。但其缺点也是非常明显的,因为编译器的种类非常有限,编译选项的组合也有限,这就导致用此方法生成免杀程序的次数也是非常有限的。
就编译器而言,经过测试,Visual C++6.0、Visual Studio 2005、Visual Studio 2010所生成的机器码有比较大的差异。
如果用GNU Compiler Collection(GCC)与微软的Visual Studio系列所生成的机器码进行对比差距肯定会更大,但是鉴于工程的不兼容与编程细节上的若干差异会导致跨越的成本较高,因此这种方式一般很少被考虑进来。
另一种方法是通过修改编译选项来达到免杀目的。下表是总结出的部分能导致程序生成二进制代码有所改变的编译选项。
通过在工程属性的命令行中添加或去除以上任意的一个变异选项,可使编译器生成的机器码产生较大的变化。
当然,随着编译器版本的发展,以后会有更多的编译选项会影响生成程序的大小及机器码的内容,因此如果想要找出足够多的组合,还需要大家不断地跟进试验。
2、添加垃圾代码
我们可以通过添加一些普通的垃圾代码来达到改变程序文件特征的目的。这里将讨论一下怎样正确添加垃圾代码。
首先,添加垃圾代码的目的是改变可执行文件的文件特征,这就要求黑客们所添加的垃圾代码最终一定要能驻留在生成的程序中,这点尤其需要注意。通过对软件逆向工程的学习,相信很多朋友已经明白编译器的强大之处了,如果黑客们添加的垃圾代码过于明显,且被判定其结果是完全无用的,那么在编译时就非常有可能会被完全优化掉。
其次,黑客们所添加的垃圾代码要尽可能地不对原程序产生太多逻辑、效率或用户交互方面的影响,否则会导致这些垃圾代码的使用范围大大缩减。
基于以上考虑,我们可以构造出如下垃圾代码:
这段代码的前两行代码只需执行一次,至于后面那个if语句,则可以当作垃圾代码随意安插在源文件的任意位置。
但是要怎样才能保证这个if语句不会被优化掉呢?首先,由于这个if语句的判定依据是一个变量,只有在运行后才能确定,因此编译器无法断定是否一定会执行到里面的MessageBox;其次,由于MessageBox会与用户产生交互,因此编译器不会将其判定为垃圾代码。
因为一年最多只有12个月,所以月份等于15的这个条件其实是永远也不会成立的,由此可见,这个MessageBox函数实际上是永远都不会被执行的。
到此为止,黑客们已经成功构造了一个可以随意安插在源文件任意位置的垃圾代码。当然,可以使用的垃圾代码也绝不仅此一种,这里只是描述了构造垃圾代码的思路及过程,更多更完善的方案还要大家自己去钻研、试验。
3、语法变换
使用等价语法替换原有语句也是干扰定位的方法之一,这种方法主要用于应对木马被提取出家族特征的情况。
对于语句的变换方法有很多,例如将一个循环体拆解为多个,或将switch-case语句打乱顺序后拆解为若干个if-else语句等。
这种变换方法虽然比较麻烦,但是随着黑客对于程序及语言本身了解的逐渐增多,其修改方式也将会变得多种多样,因此这是一种比较长效且能较彻底破坏特征码的免杀技术。
4、添加汇编花指令
在源码中添加汇编花指令主要有两个目的,第一当然是破坏特征并改变关键代码偏移,其次就是干扰其他人逆向分析我们的程序。
随意在程序中安插花指令的最便捷方法就是使用宏,我们可以先收集数十个健壮性比较强的花指令集合,然后将其集中在一个新的头文件中,并定义为宏。以下就是一个将花指令定义为宏的例子:
上面定义了一个宏SPAM_CODE_01,每当使用它时它就会在指定地点安插如下代码:
说得更简单点就是向任意引用SPAM_CODE_01这个宏的地方安插两条汇编指令。每行后边的"\"在C中的意思是续行,它的作用是将用续行符分割的代码视为一整行。
之所以要在每条汇编指令前都加上__asm关键字是因为在插入汇编代码时,编译器会以__asm关键字作为分隔符,因此如果将每条汇编指令前的__asm关键字去掉的话,那么展开后就会如下所示:
这样,编译器就会将pushad popad视为一条指令,这显然会引起编译错误。
上面之所以在汇编指令的外面又加了一层__asm关键字的括号声明,是为了告诉编译器汇编指令段在哪里结束,否则编译器可能会将后面的C++代码也当作汇编指令去解析。
另外,在选择花指令时要注意其健壮性及独特性,缺乏健壮性的花指令会导致你的程序经常出错甚至崩溃,而缺乏独创性的花指令本身就有可能包含病毒特征码。
微信公众号:计算机与网络安全
ID:Computer-network