PE 在免杀中的应用
一次性进群,长期免费索取教程,没有付费教程。
教程列表见微信公众号底部菜单
进微信群回复公众号:微信群;QQ群:16004488
微信公众号:计算机与网络安全
ID:Computer-network
将PE文件结构知识与免杀思路结合起来,对PE文件建立起更加形象化的认识,以便更加透彻地理解PE文件与免杀之间的关系。
一、PE文件与免杀思路
一般情况下,基于PE文件结构知识的免杀技术主要用于对抗启发式扫描,这主要通过修改PE文件中的一些关键点来达到欺骗反病毒软件的目的。
1、移动PE文件头位置免杀
移动PE文件头是一个比较古老的免杀技巧,其原理是利用各种软件判断PE文件有效性的Bug。我们都知道PE文件使用一个SizeOfOptionalHeader字段来描述扩展头的大小,微软设计这个字段的初衷是为了方便以后的扩展,但是后来一些需要扩展的信息都被组织到了第15个数据目录所指向的位置中,因此这个字段几乎在所有情况下都是一个恒定的值0xE0。一些粗心的程序员在解析PE文件头时并没有预先读取这个字段并确定其真实大小,而是直接使用0xE0对PE文件进行处理,这样做虽然在绝大多数情况下不会出现任何问题,不过要是遇到黑客们恶意修改的程序,那就会出现较为严重的错误了。
从免杀技术上来讲,一些黑客曾利用这个方法修改病毒木马,修改后的程序会被一些杀毒软件识别为非PE文件,也就不再对其进行下一步的查杀工作了,而修改后的程序却是可以正常运行的。
下面,就为大家介绍一下这种修改方法。
首先,为了能有一个比较“干净”的修改场所,需要将其PE头部中对我们无用的信息清理一下。这里使用到的工具是PeClean,只需要将WebHacking拖放到PeClean的界面就可完成清理了,如图1所示。
图1 清理PE文件中的多余信息
然后使用反汇编工具WinHex打开文件,通过与典型PE文件的对比可以看到,许多没用的信息现在已经清理干净了,这样对于下面的修改操作来说很有利。
下面需要确认一下这个文件的PE选项头的大小,沿着PE文件头开始标志"PE"往下找,第一个问号处所对应的十六进制信息就是PE选项头的大小了。其实这种查找方法有一定的局限性,也是一种非正统的方法,容易因PE文件结构的变化而误判其位置。除此之外我们也可以在关键字"PE"后的第17个字节处查找描述PE选项头大小的字段SizeOfOptionalHeader,如图2所示。
图2 描述PE选项头大小的SizeOfOptionalHeader字段
从图2中可知,字段SizeOfOptionalHeader的十六进制信息为E0,转换为十进制也就是224,由此也就说明PE选项头的大小为224字节。
在PE选项头大小的基础上,再加上PE头本身的大小(24字节),那么也就表示,我们需要从关键字"PE"开始向下复制248个字节,可以一边选取,一边注意WinHex右下角的“大小”信息,如图3所示。
图3 利用WinHEX的强大功能
接下来按快捷键Ctrl+X复制这段数据,然后在DOS Sub中随便选取一块空白位置,将所复制的内容剪贴到那里,并将残余的原头部信息填为0,这样就基本完成了整个PE文件头的移动工作,如图4所示。
图4 移动后的PE头部
在移动时我们要小心不要影响其他字段的偏移信息,否则会导致整个操作失败,并且因为移动了PE头,相应地指向PE头的字段e_lfanew也要修正。
通过对PE文件的学习我们知道,PE选项头后面的内容就是各个区段的头部信息,因此为了确保系统能正确地找到这些信息,还需要修改一下控制选项头大小的SizeOfOptionalHeader字段,以使得修改后的PE选项头后面仍是有用的区段描述信息,而非其他信息。
至此,已经成功地移动了PE头与选项头的位置,起到了免杀作用,这种方法对于目前的所有操作系统都有效,但是并不能排除更高版本的Windows操作系统会对此进行更加严格的审核。
2、导入表移动免杀
随着反病毒与免杀之间的“战争”越演越烈,反病毒软件特征码定位的位置也越来越刁钻,甚至连导入表都已经被其所使用。下面讲的就是早年颇为流行的针对导入表查杀的免杀方法。
本案例的操作目的就是将位于模块USER32.DLL中的导入表MessageBoxA换一个地方,以达到免杀效果。首先使用LordPE载入目标文件,单击“目录”按钮,在弹出的新窗口中单击“导入表:”后的"……"按钮,然后在弹出的新窗口中选择USER32.DLL,并在下面找到导入表函数MessageBoxA,最后单击右键选择“编辑”选项,这样就可以看到此导出函数的所有信息了,如图5所示。
图5 导入表中MessageBoxA导出项的相关信息
导入表中INT里的IMAGE_THUNK_DATA结构记录了相关导出函数的序号及名称偏移地址,LordPE中的ThunkValue指的便是这个值,因此我们要将其(0x00023730)记录下来备用。接下来使用WinHex打开目标文件,按快捷键Alt+G调出“转到偏移地址”窗口,填入我们刚才记下的地址00023730,然后单击“确定”进行查找,找到的结果如图6所示。
图6 保存导出函数MessageBoxA的IMAGE_IMPORT_BY_NAME结构
到这里,可以将MessageBoxA使用00进行填充掉,并将其移动到其他位置了,本例中将其移动到了0x00022B7E处,如图7所示。在移动时一定要注意导入表的大小写,如果大小写搞错了就会直接导致修改的失败。
图7 移动后的MessageBoxA
最后,返回LordPE中,重新加载修改后的文件,右击编辑导入表MessageBoxA,将ThunkValue后的偏移地址改为我们更改之后的0x00022B7E,即完成了所有操作。
3、导出表移动免杀
在一般情况下,鲜有需要通过移动导出表进行免杀的。但是导出表作为PE文件中较为复杂的一个结构,还是很有必要讲解一下的。
通过《PE 文件格式详解(上)》中的图2可以很容易地总结出以下规律:
导出表必须有序号,且序号是导出函数与导出函数名建立关联的唯一途径。
字段AddressOfNames里保存有导出函数名的指针数组。
通过以上规律我们不难得出一个简单的结论,若需要移动某个导出函数名,只要修改导出函数名指针数组中的某一项即可,而且这样做不会破坏导出函数与导出函数名之间的对应关系。
这样一来要实现免杀就很简单了。下面还是以MessageBoxA函数为例,先用LordPE载入PE文件user32.dll后单击“目录”按钮,在弹出的新窗口中单击“导出表:”后的"……"按钮,在弹出的新窗口中找到MessageBoxA函数后单击鼠标右键,并选择“编辑”选项。在弹出的如图8所示的窗口中可看到此函数的名称RAV(相对虚拟地址)为0x0001E2AF。
图8 MessageBoxA函数的导出信息
根据函数名的RAV地址可得出其在文件中的偏移为0x0001D6AF,使用WinHex找到这个地址后将其用0x00填充,并将字符串MessageBoxA移动到一个新的位置上,然后将这个新位置的偏移转换为RAV填充回去,即可完成导出表的简单移动,也就实现了免杀。
二、PE文件与反启发式扫描
黑客们经常通过对PE文件进行精准的调校来躲过一种甚至数种反病毒引擎的查杀,这在一定程度上是有效的。虽然针对PE文件的调整不仅仅局限在针对启发式扫描上,但是在大多数情况下都是如此。下面讲解可能触发启发式扫描的一些特征。
1、最后一个区段为代码段
正常程序的代码段通常位于首位,后面的区段在绝大多数情况下都是没有可执行权限的。但有些程序的最后一个区段为代码段,造成最后一个区段为代码段的原因有很多,但是几乎都是由免杀行为或加壳行为所导致的。例如黑客们在修改特征码时,为了方便起见往往会将含有特征码的代码块变形并移动到非代码区段中,有的甚至干脆在后面加一个可执行的区段用以保存类似的数据。
如果最后一个区段为代码段将会引发一个问题—异常的入口点,造成这种情况一般是因为黑客们使用了TLS(线程本地存储区)技术而随意设置的入口点地址,另一个重要的原因就是因为免杀行为导致入口点被定位在了非正常的代码段上,因此异常的入口点地址通常会引起启发式扫描引擎的注意。
如果要避免遗留下类似的启发式特征,最为保险的方法就是增加原代码段的大小,然后将所有的免杀工作限定在正常的代码段中。
2、可疑的区段头部属性
可疑的区段头部属性分为很多种类型,最为典型的就是代码段具有可写属性,数据段具有可执行属性。
一个正常的可执行程序不会出现多个具有可执行属性的区段,更不会让代码段具有可写属性。但是一些恶意程序如果要完成某些特定的恶意行为就会制造出这些特征来,例如蠕虫在感染一个文件时只有三种方案,一是增加一个新的可执行区段,另一种是在现有的代码段中插入恶意代码,最后一种方法就是将恶意代码分别穿插到不同的区段中,并修改相应区段的属性。
随着蠕虫变种的诞生,某些蠕虫在每运行一次后都会产生新的变体,而这就要求这些蠕虫需要修改代码段的属性为可写状态,才能每次将其中的恶意代码替换为新的变体。
其实产生这些启发式特征的主要原因还是恶意软件作者本身的处理手法不够精明,如果稍微动一下脑子就可以避免出现这些特征。
3、可疑的PE选项头的有效尺寸值
这一项启发式特征是在我们上面所讲的“移动PE文件头免杀”的技巧被广泛应用时才流行起来的,在这之前这个启发式特征只用于那些试图修改选项头大小而隐藏更多敏感数据的恶意程序。
4、可疑的代码节名称
《PE 文件格式详解(上)》中详细地向大家介绍了微软对于区段名与对应功能的约定,虽然这些约定并不是每个编译器厂商都乐于遵守的,但是至少不至于太乱,就目前来看这样做仍然是比较有“章法”的。基于此,一个类似于.A1Pass的区段名肯定是会引起注意的。
5、多个PE头部
这是一个权值非常高的启发式特征,造成这个特征的原因有很多,例如可执行文件中含有需要释放的DLL或SYS,抑或是一个正常的文件被捆绑了木马等。
一般情况下只需要将其中包含的可执行文件加密一下即可避免出现这个特征。
6、导入表项存在可疑导入
导入表项的可疑导入所指的范围较广,一般情况下有以下3种:
导入表本身可疑,例如一些初级黑客伪造的无效导入表。
导入表的导入方式可疑,例如来自kernel32.dll中基于序号的可疑导入表项,或者使用偏移调用API。
导入表所导入的函数可疑,例如导入了可用于完成某个特定恶意行为的API序列或程序内部包含有相关API函数的名称。
对于这种启发式特征,黑客一般会使用自己实现的GetProcAddress函数,以便用散列值寻找并调用相关敏感API。
三、一个稍显复杂的例子—隐藏导入表
隐藏导入表的方法实在是太多了,无论是简单的异或加密、导入表单项移除,还是稍显复杂的重构导入表、利用HOOK方式打乱其调用等,都可以达到隐藏导入表的目的。只要您了解了导入表的结构,那么肯定可以想出更多好的处理方法。
下面讲的就是导入表单项移除中的一种(目的是移除程序中的RegisterClassExW这个函数)。单项移除导入表的方法还有很多,但是在此不再做讨论,有兴趣的朋友可以自己挖掘其他实现可能。
1、操作原理与先决条件
隐藏导入表方法的原理非常简单,就是先手工将指定导入项的IAT(Import Address Table)删除掉,这样就相当于删除了与之对应的API,所有工具将查看不到此导入项。然后再给此文件打一个补丁,令其在启动初期可以将正确的值填充到IAT处,这样就可保证在程序运行后仍可以正确调用此导入函数。
但是这种方法也有很多条件限制,首先,根据PE文件格式的定义可知,导入表的OriginalFirstThunk字段(保存INT的表)是一个以0x00000000结尾的32位数组,因此如果我们将某一个INT填充为0x00000000删掉后,会导致在此INT项后面所有由此DLL导入的函数失效。这是一个无法避免的问题,想要解决此问题,只有老老实实地将所有受影响导入函数处理一遍。
其次,在修复IAT时需要用到LoadLibrary与GetProcAddress这两个API函数,因此要求目标程序本身的导入表中必须已经导入了这两个API。
2、修改PE文件
第一步就是修改PE文件以达到隐藏指定API的目的。首先用LoadPE打开目标程序,并查看此程序的导入表,找到RegisterClassExW这个导入项,并勾选“始终查看FirstThunk(V)”选项,记下其IAT的RVA"0x000020E8",如图9所示。
图9 找到导入函数
然后选中导入项RegisterClassExW单击右键并选择编辑,将“Thunk数值”一栏填充为“00000000”,如图10所示。在单击“确定”按钮并保存后,此时导入项RegisterClassExW就已经被删除了。
图10 删除导入项RegisterClassExW
至此,已经完成了一个指定导入项的删除工作,为了使后面的补丁正常运行,还要修改一下此IAT所在区段的属性。因为就一般情况下来讲,IAT所在区段的属性都是不可写的,所以为了避免对其进行修改操作时触发访问异常,应该将其属性修改为“可写”,如图11所示。
图11 将IAT所在区段的属性加上可写属性
完成以上的修改并保存后,将修改完的程序重命名为“阶段产物.exe”。
3、构造我们的反汇编代码
完成了对PE文件的修改后,就可以通过向其增加补丁代码,来使程序在运行初期自动修补被删掉的导入项RegisterClassExW了。
在修改之前,首先需要使用几个关键数据,即导入项RegisterClassExW的VA地址、入口点地址与一块连续0x00空间的起始地址,它们分别如下。
导入项的VA地址:0x004020E8(此地址由其RVA地址计算所得)
入口点的VA地址:0x004015F7
连续0x00空间的VA地址:0x00401BDD(此地址可随意选择)
然后,把后面代码需要的字符串安排到这段连续0x00空间的某个地方(将其安排到了0x00401BB5与0x00401BCB处)。要注意的是,字符串USER32.DLL是UNICODE字符集(二进制编辑的快捷键为CTRL+E),如图12所示。
图12 字符串的VA
接下来就是将以下代码汇编到0x00401BDD处。
结果如图13所示。
图13 将代码汇编到指定程序中
最后将其保存,使用LoadPE将其修改为补丁代码的起始地址0x00401BDD,至此便完成了隐藏指定导入项RegisterClassExW的所有工作。
以上主要讲解了一些与PE文件格式有关的反启发式扫描免杀技巧,并介绍了部分基于PE文件结构的启发式特征。
微信公众号:计算机与网络安全
ID:Computer-network