一个shellcode样本(beacon.dll)逆向分析

首先:shellcode分为代码和数据两部分

代码部分:负责解密数据部分,并跳转到数据部分偏移8处开始执行。

数据部分:第一个4字节是首次异或加密的key;第二个4字节是实际数据部分长度(已异或加密);剩余部分是按每4字节异或加密实际数据部分(实际是一个dll文件)

代码如下:

sub_430070( )函数解密完数据部分后,发现是一个pe文件(实际是一个dll),存在内存中的pe文件(虽然在内存中,但是暂没有被pe加载器加载,不能称为镜像);

sub_430070( )函数使用jmp edx指令跳转到这个内存中pe文件的起始位置开始执行,就是0x00430095处代码,也就是从0x004300A4处执行。

这里有2个疑问:

1、存在内存中的pe文件,没有被加载,怎么可以从文件的第一个字节处运行,即使被加载也应该从入口点处运行?

2、shellcode被加载进内存的一段连续空间,shellcode并不知道这段空间的起始地址(例子中的地址是0x00430068),又是怎样jmp道0x004300A4开始执行的?

先解决第二个:

call指令一般是调用一个函数,这个函数内部一般都有ret指令,特例就是有call没有ret,而是有pop。

call指令:把call指令下一条指令地址入栈

ret指令:把上面入栈的指令出栈到eip中

把上面代码做一个精简:

sub_430070:

         pop esi             // esi值应该是0x0043009C

         add esi,4       // esi值应该是0x004300A0

         add esi,4       // esi值应该是0x004300A4

         push esi

         pop edx           // edx值应该是0x004300A4

         jmp edx

call sub_430070

这个疑问解决了,再解决第一个疑问:

先不论文件是否被加载就从文件第一个字节(非入口点)开始运行,把解密出来的文件从内存保存到硬盘中,使用ida以二进制形式打开和winhex以16进制打开如下图:

很明显了,文件的DOS头是一段代码:使用call $+5(E8  00000000)确定在虚拟内存中的地址在根据相对偏移地址0x8072调用text节区中的ReflectiveLoader(x)函数,后续call eax,call ebx完成整个样本的运行。

从shellcode到在内存中解密pe文件,再从内存pe文件执行自身中代码(主要是调用上面说的三个函数)完成样本实际运行。

那么内存中的pe文件中3个函数代码又是如何运行的:

ReflectiveLoader(x)函数(由ida自动识别出来的函数名,暂时不清楚ida判断的依据,按Ctrl+E也会有这个函数)完成类似pe加载器的功能,如下:

1、使用call $+5指令获取自身在内存中的地址,再向上查找"MZ"、"PE"字符确定pe文件在内存中的起始地址,此外作者还认为dos头和dos存根的和介于0x40到0x400之间,如图:

2、通过fs:30获取PEB地址,再获取PEB_LDR_DATA地址,再获取LDR_DATA_TABLE_ENTRY地址,根据其中其中的BaseName判断是否是kernel32.dll模块,"kernel32.dll"字符串不直接出现,以简单算法加密作对比,如下图:

3、根据第2步找到的kernel32.dll模块,从LDR_DATA_TABLE_ENTRY中取出DllBase,根据EAT规则遍历导出表,获取4个函数地址LoadLibraryA( )、GetProcAddress( )、VirtualAlloc( )、VirtualProctect( ),这4个函数字符串也不直接出现,使用的运算方法和kernel32.dll字符串一致(图太大,不方便贴,后面会有一个缩略图,标明每一块的功能)。

4、从NT头中获取SizeOfImage值,使用第3步的VirtualAlloc( )函数申请内存空间;从NT头中获取SizeOfHeaders,使用rep movsb完成内存文件到镜像文件数据拷贝;从File头中获取节区数量和SizeOfOptionalHeader大小,定位到节区表处,同样使用

rep movsb指令完成每个节区的拷贝。如图:

5、由第3步获取的LoadLibraryA( )、GetProcAddress( )函数,修复导入表。

6、重定位。

7、从NT头中找到入口点地址,调用入口点函数。其中一个参数是1(调试发现并不是该样本的恶意代码执行部分)。

缩略图如下:

ReflectiveLoader(x)函数执行完毕,完成文件到镜像加载,并返回了入口点。

call eax    传入的参数之一是4,调试发现样本使用WinINet API从服务器获取数据执行恶意行为,由于服务器没有响应,一直在循环,最后调用exit( )退出进程。

call ebx    传入的参数之一是5,

猜你喜欢

转载自blog.csdn.net/singleyellow/article/details/86775810