前言先分析解密再进行剖析其PE结构(骚操作,这样分析病毒耗时间),本次编写的原因也是为了大家方便参阅。主要还是为了分析PE结构,让大家掌握PE结构。分析的病毒,主要分析待解密数据的来源,解密key的位置以及通过运算进行解密。
使用Total Commander打开恶意样本,进行初步样本分析无壳:查看导入导出表可以发现没有导出表,那么就可以判定这可能是一个exe程序切换成汇编的视图,可以看到第一个调用的是GetVersion,那么就可以知道这是一个VC6.0的程序。一些运算的操作。那么就可以判定这并不是恶意程序真正的本体。也可以通过lordPE工具查看是否有问题,看到RVA相差这么大,肯定加了混淆。在这个窗口单击编辑区段,标志代码段还拥有可写入的权限,那么就更确定了。
对比正常区段看看既然做了运算类似自己加了一层壳的操作,那么必然会进行解密操作,就会使用诸如VirtualAlloc这一类申请内存的操作。那么我们就可以通过对VirtualAlloc下软件的断点来进行内存空间的查看。这里就需要用到OD来进行动态的分析了。关于内存的一些函数(感觉大多数恶意样本的作者更钟情于VirtutalAlloc?):https://blog.csdn.net/lqk1985/article/details/4678044?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242在这之前,使用的是x64dbg来下断点的,但是,F9一运行,程序直接跑偏了(x64dbg默认不带反反调试的功能),那么就可知,程序可能有反调试和反虚拟机的功能,查找字符串时,应重点关注,并且运行完毕后,做了删除自身的操作。在VirtualAlloc处下断点,根据size参数猜测是否是为PE文件申请的空间,当VirtualAlloc第二次断下的时候,设置硬件写入断点。第一次断下:第二次断下:F8单步步过执行到调用此函数的位置,在申请出来1D30000的位置下word大小硬件写入断点。F9运行:第1次写入了E8,第2次写入了C7,那么就可知,每次写入的都是1byte第3次F9就再次访问到了这块内存空间,会发现第一个字节被修改了,从E8变成了4D。那么F8单步,可以看到12F9F6处有个向上跳转的红线,那么就可以确定这是个循环操作。F4让程序运行到12F9F8处可以看到,在内存1F10000处标准的PE文件格式,那么这就是恶意样本真正的本体。那么经过什么样的运算得到最终的值呢,接下来我们就溯源分析一下。重新运行程序,这里需要删除之前下的硬件写入断点,因为每次申请出来的空间都是不一样的,再次运行到申请的内存处观察。待解密的数据来源:可以看到的是1EB0000处的值被修改了,那么从12F99B处往上观察,得出:[eax+edi]=dl=[ecx]=[ebp+ecx4-0x54],eax+edi就代表申请的内存空间,最终找到变址寻址处[ebp+ecx4-0x54],ctrl+g输入ECX转到内存。可以发现在4020B0处的1byte确实是申请的空间放入的值。那么我们继续F9看看是否还是在此处取值,发现并不是28,由上图可知。那么再次查看ECX,发现在4D6A10处正是申请空间的第2字节处。在dl赋值向下的位置F8执行,可以看到红色框内,在ebp-0x8处取出了一个数赋值给ecx,把ebp+ecx4-0x54赋值给edx,并且edx自增1后,再把值还给ebp+ecx4-0x54,ecx的值也在自增,并且ecx自增1后把值还给了ebp-8处,可以从下图看到ecx的是2,刚好也在第二个地址处进行取数据,最后ecx和0x8进行比较,如果不满足条件,ecx置0,并且把值赋给ebp-8处。在这里为什么地址是需要乘4呢,因为指针大小为4,每次ecx自增1时,乘4就可以找到下一个存数据的位置,这叫变址寻址。查看EBP-0X54处,那么可以确定的是,一共有8处是取数据的地方。也可以确定的是在4020B0处和4D6A10处数据每次都自增1,并且后面还有6次取数据的操作。从12F9B8的位置处开始,ebx+0x10的值赋值给edx,edi自增1,并且存在循环,进行判断,如果edi大于edx那么就不会执行跳转。由下图可知,edx==DCA00那么就可以知道,整个程序的大小为DCA00。解密key的来源及运算:可以看到红色框的部分,cl把解密后的数据给了eax+edx,把一个临时变量ebx-0x10的值赋值给了ecx,edx自增,然后ecx和edx进行比较,然后向上跳转绿色框部分,那么就可得,ecx存储的是程序的大小DCA00,当edx小于DCA00时结束循环。从绿色框开始位置12F9D0,ebp-0x8把值给edi,然后从edi+ebx处取解密key赋值给cl,从申请的存放数据的地方和cl进行异或运算,cl再和dl进行异或运算,edi自增,并与0xF进行比较,再把edi还给ebp-0x8,如果条件不成立edi置0,那么就可以确定从edi+ebx处53FA20-53FA2F的数据和申请的存放数据的地方和cl进行异或运算后,再依次和0x0-0xF进行异或运算。最后得到最终的PE文件。解密key:
通过去除混淆,Dump出一个.mem文件,进行PE结构的分析手工解析DOS结构体、NT结构体,以及区段表结构体里面重要的成员,重要成员的分析是以lordPE工具为参照进行划分的,当然,分析也包括结构体里面的嵌套结构体。DOS结构体:DOS头结构体大小:0x40检测是否是PE文件,判断开始两个字节是否为IMAGE_DOS_SIGNATURE(0x5A4D)最后4个字节为NT头的文件偏移:0x108接下来看看NT头也叫PE头:在文件中NT头是从0x108开始的。可以看到NT头包含了两个结构体,分别是文件头和选项头由颜色可知,接下来重点分析这两个结构体的成员。NT头结构体大小:0xF8文件头:文件头结构体大小:0x14判定是什么程序:Machine: x86(0x014C) x64(0x8664)、SizeOfOptionalHeader扩展头(也叫选项头)大小: x86(0xE0) x64(0xF0)、Characteristics: 标志位 x86(0x0100) x64(0x0020)NumberOfSections值是0x5,那么就表示区段有5个,可以看到之前dump出来的区段数量就是5个。Characteristics其它值可通过微软的官方查看,我这里把它也拷贝下来了啰嗦一句文件头里面的值除了第一个成员和最后一个成员不可更改,其余的值其实是可以更改的,程序作者有时为了不让别人使用工具分析程序,如lordPE等工具查看其结构,会进行值的改变,破坏其值。让工具无法正确的解析。扩展头:判定是什么程序:Magic: x86(0x010B) x64(0x20B)代码区段的总大小:0x8FA00OEP的入口点RVA(相对虚拟地址):0x66C4B代码区段的起始RVA(相对虚拟地址):0x1000只读数据段的RVA(相对虚拟地址):0x91000程序的模块基址:0x400000内存中的区段对齐:0x1000文件中的区段对齐:0x200在内存中的程序总大小:0xE7A10DllCharacteristics特性:高位:0x81防止在已标记为数据存储区的内存区域中执行代码。 此功能也称为非执行和执行保护。低位0x40:DLL可移动的,表示是存在随机基址的,如果把值置为0则以默认基址0x400000为准。因为涉及了RVA这样的术语,那么在这里做一些术语的补充:FOA(offset):文件偏移地址,相对于文件起始的偏移RVA:相对虚拟地址,通常是相对于模块加载基址的偏移,少数情况下,相对的是其它VA:虚拟地址,在4GB虚拟空间中的绝对地址镜像(没跑):保存在磁盘中的可执行文件,如果对PE文件使用了类似CreateFile的函数那么就变成了映像(运行在内存中)了IMAGE_DATA_DIRECTORY DataDirectory[16]数据目录项:像这样的结构体只有15个,但是会有一个全0为结尾的8字节字符作为预留位置相应的表结构:在这里,我们要明白,使用010Editor打开PE文件时,我们所看到的值,是和运行在内存中的数据相同的,都是以补码的形式存在的,程序运行时,称之为映像文件。我们通过静态查看时,称之为镜像文件,想要定位对应的数据,是只能通过文件的方式进行定位,而不能通过内存的方式进行查看,文件对齐和内存对齐的值是不一样的,所以是需要进行值的转换的。我们现在还不能通过已知的信息在文件中进行位置的定位,也就是找到文件偏移,需要知道了区段的数据才能够进行计算,下面我们继续看区段表结构。 区段结构体:可以看到的是之前在文件头里面已知的数据FileHeader.NumberOfSections的值是5,那么就可得5*0x28+0x200=0x2C8,存在5个节大小一共是0xC8,区段到文件位置0x2C8结束。Misc.VirtualSize:区段在内存中对齐之前的大小VirtualAddress:区段的起始RVA,因为是第一次出现的所以说是起始RVA就相当于在内存中的相对偏移地址,之前有介绍那么就可以得到一个公式:RVA+Optional.ImageBase=VA(绝对偏移地址)这是在内存中可以找到的,当然,前提是没有随机基址。SizeOfRawData:在磁盘中文件对齐后的大小,加上FOA文件偏移后那么就可以定位下一个区段的位置。PointerToRawData:在磁盘中FOA(文件偏移),没有基址,所以是0x400那么在文件中就在这个位置是代码段的开始。Characteristics:区段的属性,分析病毒时可以根据标志观察是否加壳,如果加了壳,代码段就变成可读可写可执行了,正常的代码段只能是可读可执行,在这里是60000020,如果是加了可执行那么就是E0000020了。可以查看一下未脱壳的样本的标志。最后可以整理出来一个表:在这之前数据目录表还有文件偏移没有确定,现在已经可以通过已知信息找到5个表文件偏移位置了。这里会用到一个公式:数据目录表中的RVA-某个区段的RVA+文件偏移就可以得到一个FOA的值去定位在文件中的位置了。当然了需要确定是在哪个区段里面,就要确定数据目录表的值是在哪个区段的范围里面。例如求导入表的文件偏移:查看表可知,RVA=0xB9494,它是在.rdata只读数据段那么可得:0xB9494-0x91000+0x8FE00=0xB8294剩下的几个表都一样的计算方法,这里就不在演示了。那么就完成了下面表结构缺省的文件偏移。接下来会手工分析数据目录表中的导入表。导入表:INT和IAT在还是文件的时候,里面存储的东西是一样的,都是指向PIMAGE_IMPORT_BY_NAME的RVA中Hint导入序号或者Name函数名称。但是从字面理解IAT应该是存储地址的,为什么会存储名称。因为在程序没有运行的时候,无法得到此模块会加载到什么位置,也就无法得到函数地址是多少。当程序运行起来之后,系统会去将IAT填充上函数的地址。根据数据目录表可知,导入表大小是0x140,根据上述结构体大小可知是0x14,那么就知道了导入了多少个DLL程序,0x140/0x14=0x10,并且导入表会有一个以全0为结束的结构体标志,那么就可得0x10-0x1=0xF,那么就有15个导入的DLL文件。通过计算0xB8294+0x140=0xB83D4找到如下16进制段:Oh!红配绿,醒目, 要知道这里的值也只是RVA并不是真正的文件位置,还得进行转换,计算方式和之前一样,数据目录表中的RVA-某个区段的RVA+文件偏移。在这里可以通过计算找到Name成员值:0xB9DC6-0x91000+0x8FE00=0xB8BC6,可以看到的是一个关于网络的DLL
我们还得确认是序号导入还是函数名称导入。我们接下来看看两个结构体。在这个磁盘中起作用的只有只有后两个成员当 IMAGE_THUNK_DATA 值最高位是1表示是一个序号,序号导入起作用。内存地址最大表示2GB 7FFF FFFF进入了内核层地址超过了8最高位是0最后一个成员起作用最高位是否为1可以使用系统提供的如果是最后一个成员起作用,那么就指向如下结构体:Hint 字段表示函数的序号Name字段定义了导入函数的名称字符串,这是一个以 0 为结尾的字符串。那么现在就通过计算找到的值查看是否高位是INT计算后高位是否是以8开头的:INT:0xB9C60-0x91000+0x8FE00=0xB8A60定位到此文件偏移处:确定高位不是8,那么再次计算:Hint=0xB9D0C-0x91000+0x8FE00=0xB8B0C=0x009A紧跟着的则是导入函数名称:IAT:0x9168C-0x91000+0x8FE00=0x9048C值和INT是一样的:从查看INT和IAT的RVA,以全0结尾就代表了一个dll库的函数API的数量,那么就可以确定WININET.dll库函数的API会是0x9个并且第一个API函数是InternetOpenW。最后总结一下,名称表元素的个数和序号表元素个数是相同的,地址表中的元素可能会比序号表和名称表元素个数要多。地址表中多出来的,就是没有名称的函数,或者是无效的函数。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
SSH山水画 为什么这套工具好像是火绒的far呢
APT_华生 Total Commander吗,这是我学习姜烨老师的课程学到的,OD的话是外星人
option 什么工具?显示不是Hiew吗?