欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]64位CreateProcess逆向:(三)PE格式的解析与效验

[原创]64位CreateProcess逆向:(三)PE格式的解析与效验

MiVerifyImageHeader是一个较小的函数。不过,恰恰由于它的较简单,是一个很好的示范,教会我们怎么从逆向的角度获取新知:大部人学习PE文件格式,是根据公开的文档以及头文件winnt.h中所提供的相关结构体。然而,PE中的有些字段,可能只是微软预留的位置,在当下操作系统中并没有实际作用。而随着微软操作系统的更新,有些字段是否检验,在不同版本(如WIN XP与WIN 7)策略是不同的。文档不可能完全跟得上操作系统的变更,winnt.h中的结构体也没法告诉我们哪些字段有意义哪些是预留占位的。
    而逆向相关代码,可以得到关于PE校验的最可靠答案。
    如前所述,MiVerifyImageHeader用于完成PE文件头的解析及校验工作,MiVerifyImageHeader的函数原型如下:

NTSTATUS

MiVerifyImageHeader(

OUT IMAGE_INFO* pOutInfo,

IN PIMAGE_NT_HEADERS pNtHeader,

IN PIMAGE_DOS_HEADER pDosHeader,

IN ULONG ntHeaderSize

)


    第一个传出参数,指向一个IMAGE_INFO结构体,它在MiVerifyImageHeader校验完成后,把后续还会使用的PE头字段,保存起来并传出。它的格式在微软提供的头文件中是找不到的,不过借助于IDA及分析相关代码,我们可以还原得到它的结构:

    各字段的意义从字段名上很容易猜到,就不再多说明。
    MiVerifyImageHeader的三个传入参数也很容易理解:指向IMAGE_NT_HEADER的指针、指向IMAGE_DOS_HEADER的指针以及NT头的大小。
    其中pDosHeader与ntHeaderSize是对于NE格式(Win3.x 所引入的16位可执行文件格式,我们将不做讨论。
    整个MiVerifyImageHeader的流程如下:

    在函数的开始,会对第二个参数(pNtHeader)进行检验,若该参数所指向地址不是以4字节对齐的话,则直接返回错误码、结束流程:

    接着,会检查NT头在Signature字段(以上代码中的cmp ‘EP’),以检测是否是PE文件,如上所述,NE文件及非PE文件不在我们讨论范围。我们直接看流程为’PE’后所做的事情。
    首先,是对NtHeader中的FileHeader相关字段进行校验:

    不过,对FileHeader的校验,并不全在在MiVerifyImageHeader中完成,结合静态分析及文件修改、试错。我们得出了FileHeader的必检字段,大家可在附件的头文件中

    接着,会校验OptionalHeader中的Magic字段,它决定着之后对IMAGE_INFO的填充方式,字段值为0x20B时,对应着64位的格式,这里我们只讨论64位的格式的填充。

    对于IMAGE_INFO中非数据目录相关的字段,MiVerifyImageHeader简单地将NT头中对应的字段填充上去:

    而对于数据目录字段,则是受NumberOfRvaAndSizes决定的:
    因为我们知道,在Winnt.h的IMAGE_OPTIONAL_HEADER64结构体中,有一个数组成员,称为数据目录(DataDirectory)。

IMAGE_NUMBEROF_DIRECTORY_ENTIRES的值为16:

这个数据目录就是我们学习PE结构体据悉的各种“表”,如导入表、导出表等等。
各个表所对应的序号在winnt.h头中也查得到:

    但是,我们也知道,一个PE文件,并不一定要有这所有的表项才能正常运行。那哪些表项是必须的,哪些是可选的呢,以及操作系统怎么去判断一个PE文件里有几个表呢?
    事实上,从代码中可以看出,MiVerifyImageHeader通过判断NumberofRvaAndSizes这个字段,来判断某些可选项数据表项是否存在。比如,若NumberofRvaAndSizes的值大于6,则说明Debug Directory一定存在(它对应的宏IMAGE_DIRECTORY_ENTRY_DEBUG的值为6),因此,会置IMAGE_INFO的相关字段(isHaveEntryDebug)为true。
    IMAGE_INFO中的其它与数据目录有关的字段也是同样的道理,大家看附件中的IDA文件即可知,在此不再赘述。

    这些在IMAGE_INFO中的数据,在MiVerifyImageHeader调用返回后还会被系统用到。
    在填充完IMAGE_INFO结构体之后,函数返回前,MiVerifyImageHeader还会检查文件对齐值、内存对齐值、镜像大小等进行相应的检测:
    文件对齐值、内存对齐值相关检测:
    检测文件对齐值是否0x200的倍数,不是则检测是否和内存对齐值相等,不是0x200的倍数且和内存对齐值不相等则出错返回。
    检查内存对齐值和文件对齐值是否2的倍数,不是则出错返回。
    检查内存对齐值是否小于文件对齐值,小于则出错返回。
    文件映像大小检测:
    检查映像装入内存后的总大小,大于77000000h则出错返回,也就是说,一个PE文件的最大限制是1904MB,这与StackOverflow上一实际测试的哥们的结果是一致的:

    相关标志的检测:
    检查OptionalHeader.Magic和FileHeader.Machine值是否合法,这分为64位系统与32位系统两种情况。
    当64为系统时,要求OptionalHeader.Magic为020Bh,且FileHeader.Machine为0200h或者8664h。
    当32为系统时,OptionalHeader.Magic为010Bh,且FileHeader.Machine为014Ch。
    以上检测若有不通过的项,均返回STATUS_INVALID_IMAGE_FORMAT。

    PE结构体中需要校验字段
    要注意,PE格式的检查,并不全部在MiVerifyImageHeader函数中完成。我们通过分析系统代码及反复试错,确认了64位PE头结构中必需检验的字段。在此分享给大家,代码如下,大家也可在附件的头文件中查看。


    一并附上32位的相关结构体,大家在学习PE时,可以对照:



    应用--畸形PE
    研究PE文件的校验函数,得出其校验流程,是很有实践意义的。其最直接的例子,就是可以通过写出畸形PE来躲过反病毒分析系统的检测:只要分析系统的校验逻辑与Windows有一个地方不致,就可能被利用并躲过。
    附件中是我们所写的一个畸形PE,它只有0x118个字节,在Windows 7 系统上可正常运行,弹出一个对话框:
[ATTACH][/ATTACH]
    而我们将它上传到几个知名的文件行为分析系统进行测试:
    腾讯的哈勃系统,未能检测出程序行为:

    Anubis系统认定该文件不能正常执行

    金山的火眼系统认定不是一个有效的PE文件:

    这说明,我们所写的畸形PE,在物理机上能正常执行的前提下,成功躲避了以上各个自动检测系统。
IDB文件:
ntoskrnl.7z.001.rar
ntoskrnl.7z.002.rar
ntoskrnl.7z.003.rar
请将IDB文件的后缀名“.rar”删除后用7z解压

上传的附件 w_1.png
w_2.png
w_3.png
w_4.png
w_5.png
w_6.png
w_7.png
w_8.png
w_9.png
w_10.png
w_11.png
w_12.png
w_13.png
w_14.png
w_15.png
w_16.png
w_17.png
w_18.png
w_20.png
w_21.png
w_22.png
畸形PE.7z
64位CreateProcess逆向:(三)PE格式的解析与效验.pdf
64位CreateProcess逆向:(三)PE格式的解析与效验.doc
ntoskrnl.7z.001.rar
ntoskrnl.7z.002.rar
ntoskrnl.7z.003.rar
PE相关的结构体(32与64).h.txt

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功