一、PE结构分析
要使用任何一个DLL所提供的函数,我们需要将它导入,也就是用到了导入表。然而对于那些提供了被导出的函数的DLL程序来说,他们必须使用导出表将函数导出,之后别的程序才可以使用。
以dos MZ header开始。
1、 DOS头结构
两个重要字段,e_magic如果标记不为MZ,windows装载器就不会执行它。(MZ是什么?该文件可以通过文件开头的ASCII字符串“MZ”(十六进制:4D 5A)来识别(“幻数”)。 “MZ”是Mark Zbikowski的缩写,Mark Zbikowski是MS-DOS的主要开发者之一。)e_lfanew,会指示装载器找windows下的执行体,描述后面PE头的字节偏移。
2、 PE头结构
(1) IMAGE_NT_HEADERS结构
Signature为dword类型,值为50h,45h,00h,00h(PE\0\0)为PE文件标记,识别给定文件是否为有效PE文件。
FileHeader结构域包含了关于PE
(2) IMAGE_FILE_HEADER结构
NumberOfSections字段,描述文件中节的数目。在编译器对源代码编译的时候,属性相同会放在一个连续的物理块区。
(3) IMAGE_OPTIONAL_HEADER32结构
ImageBase字段指出PE优先装载的虚拟地址VA(exe文件,装载在内存后,该值和文件中值是一致的,但dll文件未必一致)
AddressOfEntryPoint字段是准备运行的PE文件的相对入口地址,是相对va的偏移地址。
FileAlignment字段指定了文件中节对齐的长度单位(每节都为指定长度的倍数)
SectionAlignment字段指定节被装入内存的字节长度单位(同上)
3、以上分析可知判断一个文件是否为PE格式的可执行文件:
(1) 检验文件头部第一个子的值是否为MZ,如果是,则DOS有效。
(2) 用DOS头的字段e_lfanew来定位PE头。
(3) 比较PE头的第一个dword的值是否为4500000h(PE\0\0)。
如果是,那么粗略判断为一个有效PE文件
二、PE病毒api技术
1、 API
首先了解常用dll:
Kernel32.dll: 32位动态链接库文件,属于内核级文件。它控制着系统的内存管理、数据的输入输出操作与中断处理,包含内存管理、任务管理和文件操作等API函数。
Gui32.dll:图形设备接口。提供显示文本和图形等API函数,包括对窗口的建立、显示、事件处理和销毁。
User32,dll:用户接口服务,包含建立窗口和传送消息的API函数。
API函数的使用分为静态和动态两种。在源程序中调用API函数。两种皆可,未公开的API,无相关头文件,只能用动态方式。
2、 寻找API函数地址
PE病毒和普通PE一样需要调用API,但是PE病毒只有一个代码段,不存在普通PE程序中的导入表结构。所以如果想要调用API函数,必须先找出这些API在DLL中的地址。为了获得API函数地址,首先必须获得kernel32的基地址,获取之后,就可以在此模块中搜索出GetProcAddress和LoadLibrary的地址。一旦有了这两个函数的地址,就可以直接道道任何需要的API地址。
代码:[(13条消息) PE文件病毒实验(二)——实验报告_jmhIcoding-CSDN博客](https://blog.csdn.net/jmh1996/article/details/104592450)
首先需要进行重定位:
call A A : pop edi //可能会有一定偏移 sub edi,5 mov [edi-80], edi;//assign current address.
将当前内存地址保存到edi中,此时edi先低地址偏移80个单位就是数据区中addOfcurrent所在的内存单元。注意edi需要先减去5个字节,因为call和pop指令占据了5个字节。减去5个字节后,edi就是执行数据区getProcAddr的末尾。
接着获取KernalBase32的基地址:
mov eax, fs:[30h] mov eax, [eax + 0ch] mov eax, [eax + 1ch] mov eax, [eax] mov eax, [eax + 8h] mov [edi-8], eax;
这主要是通过PEB和TEB的结构来获取的,这种方法在Windows10,Windows7以及XP都可以正确的获取KernalBase32基地址。
接着搜索GetProcAddress函数的地址:
mov edi, eax mov eax, [edi + 3Ch] mov edx, [edi + eax + 78h] add edx, edi; edx = 引出表地址 mov ecx, [edx + 18h]; ecx = 输出函数的个数 mov ebx, [edx + 20h] add ebx, edi; ebx =函数名地址,AddressOfName search : dec ecx mov esi, [ebx + ecx * 4] add esi, edi; 依次找每个函数名称 ; GetProcAddress mov eax, 0x50746547 cmp[esi], eax; 'PteG' jne search mov eax, 0x41636f72 cmp[esi + 4], eax; 'Acor' jne search ; 如果是GetProcA,表示找到了 mov ebx, [edx + 24h] add ebx, edi; ebx = 序号数组地址, AddressOf mov cx, [ebx + ecx * 2]; ecx = 计算出的序号值 mov ebx, [edx + 1Ch] add ebx, edi; ebx=函数地址的起始位置,AddressOfFunction mov eax, [ebx + ecx * 4] add eax, edi; 利用序号值,得到出GetProcAddress的地址 sub eax, 0xb0 pop edi mov ebx, edi; mov [ebx-4], eax;//GetProcAddress的地址
这主要通过搜索,搜索得到“GetProcAddress”这个名字,然后通过这个名字根据导出表的结构由北桥查询得到GetProcAddress的地址,并将其保存在数据区的getProcAdd里面。
然后通过GetProcAddree获取LoadLibraryExa的地址:
ub ebx,28 push ebx add ebx,28 push [ebx-8]; call [ebx-4]; mov [ebx-12], eax;//LoadLibrary的地址 再通过LoadLibraryExa 载入msvcr120.dll这个动态链接库: push 0x00000010 push 0x00000000 sub ebx,76 push ebx add ebx,76 //push eax call [ebx-12] mov [ebx-88], eax;
三、病毒感染方式:
(1) 搜寻exe文件。
(2) 病毒修改exe文件
~分析搜寻到的exe文件是否为正常PE文件。
~分析该exe文件是否被感染(判断是否含有特征码)
~建立病毒节表结构,将病毒代码写到被感染文件最后。节名(一般为特征码),节的内存偏移地址VirtualAddress、节的属性Characteristics、节的文件偏移PointToRawData、节的大小VirtualSize。
·~修改PE头中节个数、代码段大小、内存中整个PE映像体大小、入口地址。
四、 病毒对PE修改的三种方式(添加节、扩展节、插入节)
1、 添加节的方式修改PE
所谓添加节就是在文件的最后建立一个新节,同时在节表结构的后面建立一个节表,用以描述该节。程序的入口地址被修改为指向最后含有病毒代码的节从程序整体观察被病毒感染之后的变化会发现:程序大小发生变化,并且程序入口地址发生变化。
病毒代码的执行过程为:获取kernel32.dll地址、获取GetProcAddress和LoadLibrary地址、获取要用到的函数的地址、感染exe文件、跳转到原来的程序并执行。
2、 加长最后一节修改PE
该方式将病毒附加在最后一页,同时修改最后一节的节表结构,修改程序入口地址,使指向最后一节的病毒代码。
病毒感染过程:以可读可写的方式打开文件——读文件入内存——分析是否为PE文件——是,则修正一些PE头部内容——修改最后节的节表结构——向内存文件最后节尾写入病毒代码——分析是否感染过——修改节名、入口地址、覆盖原文件、修改由病毒到原入口的跳转指令、考虑补全文件。
3、 插入节方式修改PE
这种方式不增加节的个数和文件长度,病毒搜寻到一个可执行文件后,分析每个节,查询节的空白空间是否可以容纳病毒代码,若可以则感染之。CIH病毒就是采用这种方式感染可执行文件的
感染过程:以可读可写方式打开文件——分配内存——读文件到内存——判断是否为PE文件——是,则遍历每个节——判断是否存在能容纳病毒的空白空间——是,则判断是否感染过——最后修改节名、PE头、节表结构、插入病毒。