Cherishao 发表于 2018-2-5 21:59

PE文件型病毒[含样本]

本帖最后由 Cherishao 于 2018-2-7 14:59 编辑

## 0x00 概述

之前分享了关于病毒的的一些小知识,有坛友对PE文件病毒比较感兴趣,于是,把之前学过的PE文件型病毒知识整理分享一下。

### 0x10 PE文件病毒

是Windows系统病毒之一,在安全模式下可以删除。

#### 0x11 病毒特点
该病毒是将可执行文件的代码中程序入口地址改为病毒的程序入口,这样就会导致用户在运行的时候执行病毒文件,这是黑客比较常用的方式。

### 0x20 如何判定PE文件
:如何判断一个文件是否为PE文件?

>1、检验文件头部第一个字的值是否等于MZ,如果是,则DOS头有效。
>2、用DOS头的字段e_lfanew来定位PE头。
>3、比较PE透的第一个双字的值是否等于45500000H(PE\0\0)。

#### 0x21PE文件结构
**PE文件结构图如下:**
!(http://oy3mfxixl.bkt.clouddn.com/201802071440_62.png)
#### 0x22 判断是否为PE文件
汇编代码实现
```bash
#定义局部变量
IMAGE_DOS_HEADER  dos_header;
IMAGE_NT_HEADER  nt_header;
CFile fp;
fp.Read(&dos_header,sizeof(dos_header)); 
If(dos_header.e_magic!=0x5A4D) #用DOS头的字段e_lfanew来定位PE头。
{…..}
fp.Seek(dos_header.e_lfanew,CFile::begin);

if(nt_header.Signature!=0x00004550) #比较PE透的第一个双字的值是否等于45500000H(PE\0\0)。

```

### 0x30 PE病毒编写的关键
定位、获取API函数、搜索目标文件、感染、破坏。
#### 0x31 病毒编写的难点
病毒是在宿主的运行环境下运行,所以无法像在自己本身的运行环境下一样访问自己的静态(全局)变量的数据和直接调用系统API。

#### 0x32 病毒感染PE文件的基本方法
```bash
(1)判断目标文件开始的两个字节是否为“MZ”;
(2)判断PE文件标记“PE”;
(3)判断感染标记,如果已被感染过则跳过这个文件,否则继续;
(4)获得Directory(数据目录)的个数,每个数据目录信息占8个字节;
(5)得到节表起始位置:Directory的地址+数据目录占用的字节数=节表起始位置;
(6)得到目前最后节表的末尾偏移(紧接其后用于写入一个新的病毒节):
节表起始位置+节的个数×(每个节表占用的字节数28H)=目前最后节表的末尾偏移
(7)开始写入新的节表项
①写入节名(8字节);
②写入节的实际字节数(4字节);
③写入新节在内存中的开始偏移地址(4字节),同时可以计算出病毒入口位置:
上节在内存中的开始偏移地址+(上节大小/节对齐+1)×节对齐
④写入新节(即病毒节)在文件中对齐后的大小;
⑤写入新节在文件中的开始位置:
上节在文件中的开始位置+上节对齐后的大小(8) 修改映像文件头中的节表数目
(9)修改AddressOfEntryPoint(即程序入口点指向病毒入口位置),同时保存旧的AddressOfEntryPoint,以便返回HOST继续执行。
(10)更新SizeOfImage(内存中整个PE映像尺寸=原SizeOfImage+病毒节经过内存节对齐后的大小);
(11)写入感染标记(可以放在PE头中);
(12)写入病毒代码到新节指向的文件偏移中。
```

### 0x40 重定位
#### 0x41 病毒感染重定位原因?
病毒要用到变量,在病毒感染host程序后,由于它依附到host程序中的位置各不
相同,因此病毒随着host载入内存后,病毒中的各个变量在内存中的位置会随着host程序的位置而发生变
化,因此,病毒程序要能正常使用变量。必须采用重定位技术。

#### 0x42 重定位步骤
>1.call指令跳转到下一条指令,使下一条指令感染后在内存中的实际地址进栈。
>2.用`pop EXX,`指令取出栈顶内容,得到感染后下一条指令内存中的实际地址Base。
>3.varstart为感染前call指令的下一条指令地址,varlable为感染前变量地址,则感染后var实际地址为(Base-Offset varstart)(基
>地址)+Offset varlable

### 0x50 API函数获取
#### 0x51 为什么要获取API函数地址
Win32下的系统功能调用一般通过调用动态连接库中的API函数实现。
API函数调用的实质是找到函数地址,然后

```bash
call MessageBox(0,”123”,0,0);
Call 0x7c91001c;  (WIN XP SP3)`
```

### 0x60 病毒获取API函数地址
#### 0x61 静态方式
调用时,根据函数名查引入表,就可以获取该函数的地址。
#### 0x62 动态方式
使用函数LoadLibrary装载需要调用的函数所在的dll文件,获取模块句柄。然后调用GetProcAddress获取需要调用的函数地址。这种方式是在需要调用函数时才将函数所在的模块调入到内存中,同时也不需要编译器为函数在引入表中建立相应的项。
这里主要注意下面2个特殊的API
>LoadLibrary加载一个DLL,返回DLL地址
>GetProcAddress通过DLL地址和API函数名获得API函数的地址

#### 0x63 得到模块kernel32的地址方法
由于程序入口点是被kernel32某个函数调用的,所以这个调用函数肯定在kernel32的地址空间上。那么我们只要取得这个返回地址,就得到了一个kernel32空间中的一个地址。
通过这个地址,我们可以从高地址向低地址方向进行搜索,通过PE标志的判断,搜索到kernel32模块的基地址!
大致流程如下:
取栈顶值到寄存器A(KERNEL32中的一个地址)
A =  A 与 0FFFFF000h(分配粒度是1000h,基地址必然在xxxx000h处)
循环:
      如果 == IMAGE_DOS_SIGNATURE(判断DOS头标志)
      {B = A;B =B+e_lfanew;  指向PE标志
      如果 ==IMAGE_NT_SIGNATURE (判断“PE\0\0”标志)
              { 跳出循环;(找到,退出!)}
       A =  A - 01000h;
循环结束  
代码实现:

```bash
GetKBase:
    mov edi ,        ;取栈顶值,就是KERNEL32中的一个地址
    and edi,0FFFFF0000h    ;分配粒度是10000h,基地址必然在xxxx0000h处

   .while TRUE
      .if WORD ptr == IMAGE_DOS_SIGNATURE    ;判断DOS头标志
          mov esi,edi
          add esi,DWORD ptr               ;esi指向PE标志
         .if DWORD ptr ==IMAGE_NT_SIGNATURE   ;判断PE标志
               .break                                       ;若正确,则跳出循环
         .endif
      .endif

       sub edi, 010000h        
   .endw

mov hKernel32,edi           ;把找到的KERNEL32.DLL的基地址保存起来
```
在得到了Kernel32的模块地址以后,就可以搜索他的导出表得到GetProcAddress和LoadLibrary两个API函数的地址。
对这两个API函数的联合调用就可以得到WIN32 应用层上任何所需要的API函数地址了。


#### 0x64 函数名地址查找
通过函数名称查找函数名地址,具体如下:

1、定位到PE文件头。
2、从PE文件头中的可选文件头中取出数据目录表的第一个数据目录,得到导出表的地址。
3、从导出表的NumberOfNames字段得到以命名函数的总数,并以这个数字做微循环的次数来构造一个循环。
4、从AddressOfNames字段指向的函数名称地址表的第一项开始,在循环中将每一项定义的函数名与要查找的函数名比较,如果没有任何一个函数名符合,说明文件中没有指定名称的函数。
如果某一项定义的函数名与要查找的函数名符合,那么记住这个函数名在字符串地址表中的索引值(如x),然后在AddressOfNameOrdinals指向的数组中以同样的索引值x去找数组项中的值,假如该值为y。
5、以y值作为索引值,在AddressOfFunctions字段指向的函数入口地址表中获取的RVA就是函数的入口地址,当函数被装入内存后,这个RVA值加上模块实际装入的基址(ImageBase),就得到了函数真正的入口地址。

#### 0x65 导出表中获取API函数地址

```bash
exportD=hKernel32.OptionalHeader.DataDirectory.VirtualAddress + hKernel32;
int i,iA;
for (i=0;i<exportD.NumberOfNames;i++)
{
p=hKernel32+exportD.AddressOfNames; If (!strcmp(p,name)) break;
}
WORD *pw = (WORD*)(hKernel32 + exportD.AddressOfNamesOrdinals);
iA = pw;
DWORD *pA = (DWORD*)(hKernel32 + exportD.AddressOfFunctions);
DWORD address = pA;

```

### 0x70 目标文件搜索
PE病毒通常以PE文件格式的文件(如EXE、SCR、DLL等)作为感染目标。在对目标进行搜索时一般采用两个关键的API函数:
```
FindFirstFile
FindNextFile
```
其一般搜索“*.exe”、“*.scr”等文件进行感染。在算法上可以采用递归或者非递归算法对所有盘符进行搜索。

#### 0x71 文件感染

一个被病毒感染的HOST程序通常首先执行病毒代码,然后执行HOST程序的正常代码。这既保证病毒首先获得控制权,同时也不影响HOST程序的正常执行。
另外也可能在HOST程序执行的过程中调用病毒代码,例如病毒的EPO技术中就采用这种方式。 
### 0x80 病毒对PE文件的修改方式
主要有这三种方式:添加节;扩展节;插入节。


#### 0x81 添加节方式修改PE 
在文件的最后建立一个新节,在节表结构的后面建立一个节表,用以表述该节。入口地址修改为病毒所在节.

1、把病毒代码追加到文件尾部。

2、在节表中增加一个section header各项数据填写正确(VirtualSize,VirtualAddress,PointerToRawData…..)。

3、在FILEHEADER中修改节表项数目: +1。

4、重新计算SizeofHeaders,并替换原值。
5、重新计算SizeofImage,并替换原值。

6、记录未感染时的AOEP(入口地址),因为在病毒代码结束时要让宿主程序正常执行。所以要先记录AOEP,在病毒程序结束后JMP跳到宿主程序的AOEP。

7、修改OptionalHEADER中的AddressOfEntryPoint,让它指向新加节的入口代码

添加节修改PE 结构图如下
![添加节修改PE](http://oy3mfxixl.bkt.clouddn.com/201802071443_649.png)

#### 0x82 插入节方式修改PE
这种方式不增加节的个数和文件长度,病毒搜寻到一个可执行文件后,分析每个节,查询节的空白空间是否可以容纳病毒代码,若可以,则感染之。CIH病毒就是采用这种方法感染可执行文件的。
SizeOfRawData - VirtualSize


插入节方式修改PE结构图如下
![插入节修改PE](http://oy3mfxixl.bkt.clouddn.com/201802071445_178.png)


#### 0x83 扩展节修改PE
先把病毒代码追加到最后一个节的尾部。修改节表中最后一项section header并增加 SizeOfRawData 的大小和内存布局大小。

扩展节修改PE结构图如下:
![扩展节修改PE](http://oy3mfxixl.bkt.clouddn.com/201802071444_38.png) 

### 0x90 Show me the code

最后分享2个实验,感兴趣的坛友可以动手实践下
(1)打造最小的PE文件。
(2)动态获取API函数地址及观察被PE文件型病毒感染的文件
(3)建议在吾爱虚拟机中实验。
链接:https://pan.baidu.com/s/1sna9Wdv 密码:ilgd 
![](http://oy3mfxixl.bkt.clouddn.com/201802061323_382.png)
压缩包提取密码:52pojie

Cherishao 发表于 2018-2-6 21:19

mq5123 发表于 2018-2-6 17:24
其实大多数蠕虫病毒就是这个原理(比如srv),简而言之就是把病毒数据写入到可执行文件,通过修改程序入口 ...

是有这个,不过,蠕虫病毒的攻击方式比较多样化,大多是利用操作系统和应用程序的漏洞主动进行攻击(RPC溢出漏洞、LASS溢出漏洞)

mq5123 发表于 2018-2-6 17:24

其实大多数蠕虫病毒就是这个原理(比如srv),简而言之就是把病毒数据写入到可执行文件,通过修改程序入口(OEP)使程序先执行病毒代码后再执行原可执行程序

shaokui123 发表于 2018-2-6 01:00

感谢分享,楼主排版不错

gongyong728125 发表于 2018-2-6 08:11

谢谢楼主分享,学习学习!

guoxiaofei029 发表于 2018-2-6 08:20

不动编程的路过

wj158 发表于 2018-2-6 08:52

这介绍的通俗易懂,感谢分享!

dapeng2113 发表于 2018-2-6 11:08

看不懂。。。但是很牛逼的感觉。。。

壹天 发表于 2018-2-6 11:30

看了一半,然后就没有看下去的欲望了

Cherishao 发表于 2018-2-6 13:12

dapeng2113 发表于 2018-2-6 11:08
看不懂。。。但是很牛逼的感觉。。。

感兴趣的话,可以了解清楚PE结构的东西,再来看一遍。

小黑霸天 发表于 2018-2-6 14:10

学习一下 日常防毒。。。

liujiajia123 发表于 2018-2-6 15:21

分析的可以,谢谢分享。
页: [1] 2 3
查看完整版本: PE文件型病毒[含样本]