PE 格式详解与试验
PE 格式详解与试验
- 可执行文件结构分析
- DOS头
- 文件头
- 可选头
- PE RVA 地址与文件地址转换
- 块表 Section Header
- 导入表 Data Directory
- 基址重定位 reloc
- C语言解析 PE 结构
- ELF 格式
- readelf 命令解析 ELF 格式
- 基于机器学习的恶意程序检测
- 通过PE文件可以直接获取到的特征
- 字节直方图
- 文本特征
- 从各个节分析出的特征
- 训练模型
可执行文件结构分析
可执行文件有:
- windows 的 PE,如 .exe、.dll、.ocx、.sys、.com。
- Linux 的 ELF,如 .out、.so。
PE 整体结构图:
具体结构:
比较重要的几个部分:DOS 头、文件头、可选头、数据目录、节头和节区。
- DOS头:32位签名以及魔数
0x00004550
- 文件头:说明文件在什么平台、分几个区段、链接的时间、是可执行文件还是 DLL
- 可选头:包含大量 PE 文件的重要信息
- 数据目录:包含许多指向各节数据的指针
- 节头和节区:PE文件的核心,病毒检测相关特征主要集中于此
DOS头
DOS头,是 PE 文件第一个结构,总共占 64 个字节。
第一个成员 e_magic
,叫魔数,占 2 个字节,固定俩个字符 MZ
的内存编码,这是发明人的首字母缩写。
- M 的内存编码:4D
- Z 的内存编码:5A
但在 x86 平台,是低位优先,看到的是 5A4D,而不是 4D5A。
第二个成员 e_cbip
,PE 文件最后那一页所包含的字节数。
第三个成员 e_cp
,PE 文件包含多少页。
第四个成员 e_crlc
,PE 文件有多少重地位个数。
第五个成员 e_cparhdr
,PE 文件有多少断头。
第六个成员 e_minalloc
,所需的最小附加段。
第七个成员 e_maxalloc
,所需的最大附加段。
第八个成员 e_ss
,栈底寄存器的初始值。
第九个成员 e_sp
,栈顶寄存器的初始值。
第十个成员 e_csum
,校验码,验证 PE 文件有木有被改。
第十一成员 e_ip
,指令寄存器的初始值。
第十二成员 e_cs
,段寄存器的初始值。
第十三成员 e_lfarlc
,重定向表的文件地址。
第十四成员 e_ovno
,覆盖号,早期内存小,运行大程序就需要这个。
第十五成员 e_res[4]
,保留字段。
第十六成员 e_oemid
,oem 的 id。
第十七成员 e_oeminfo
,oem 的信息。
第十八成员 e_res2[10]
,保留字段。
第十九成员 e_lfanew
,PE 头的起始地址 0x0000003C。
- 判断 PE 文件的有效性:
e_magic
(5A4D)、e_lfanew
( PE,0,0,(x00004550) )
解析 DOS Header:
int peDosHeaderAnalyze(char *file)
{if(file==NULL)return -1;FILE *fp = NULL;IMAGE_DOS_HEADER dosheader; // PE 结构体公开的unsigned long pesig;fp = fopen(file, "r+b"); // 打开文件if(fp == NULL)return -1;fread(&dosheader, sizeof(dosheader), 1, fp); // 读出 dos header fseek(fp, dosheader.e_lfanew,SEEK_SET); // 读出 dos header 最后一个成员 fread(&pesig, 4, 1, fp); // 读 4 个字节,PE 头签名 PE,0,0fclose(fp);printf("IMAGE_DOS_HEADER info:\n");printf("e_magic : %04x\n", dosheader.e_magic); // "MZ"-->"ZM":0x5A4Dprintf("e_cblp : %04x\n", dosheader.e_cblp);printf("e_cp : %04x\n", dosheader.e_cp);printf("e_crlc : %04x\n", dosheader.e_crlc);printf("e_cparhdr : %04x\n", dosheader.e_cparhdr);printf("e_minalloc: %04x\n", dosheader.e_minalloc);printf("e_maxalloc: %04x\n", dosheader.e_maxalloc);printf("e_ss : %04x\n", dosheader.e_ss);printf("e_sp : %04x\n", dosheader.e_sp);printf("e_csum : %04x\n", dosheader.e_csum);printf("e_ip : %04x\n", dosheader.e_ip);printf("e_cs : %04x\n", dosheader.e_cs);printf("e_lfarlc : %04x\n", dosheader.e_lfarlc);printf("e_ovno : %04x\n", dosheader.e_ovno);printf("e_res[0] : %04x\n", dosheader.e_res[0]);printf("e_oemid : %04x\n", dosheader.e_oemid);printf("e_oeminfo : %04x\n", dosheader.e_oeminfo);printf("res2[0] : %04x\n", dosheader.e_res2[0]);printf("lfanew : %08x\n", dosheader.e_lfanew);return 0;
}
判断 PE 有效性:
bool isPeValid(char *file) {if(file == NULL)return FALSE;FILE *fp = NULL;IMAGE_DOS_HEADER dosheader; // PE 结构体公开的unsigned long pesig;fp = fopen(file, "r+b"); // 打开文件if(fp == NULL)return false;fread(&dosheader, sizeof(dosheader), 1, fp); // 读出 dos header fseek(fp, dosheader.e_lfanew, SEEK_SET); // 读出 dos header 最后一个成员 e_lfanewfread(&pesig, 4, 1, fp); // 读 4 个字节,PE 头签名 PE,0,0fclose(fp);if((dosheader.e_magic ==IMAGE_DOS_SIGNATURE) &&(pesig == IMAGE_NT_SIGNATURE))//dos header e_magic和PE头部里的signature必须为固定值:MZ(0x5A4D )和PE00(0x00004550)return true;return false;
}
调用:
int main(int argc, char* argv[])
{char *pefile="D:\\mfpedemo_x64debug.exe";if(isPeValid(pefile))peDosHeaderAnalyze(pefile);elseprintf("not a valid pe file\n");return 0;
}
文件头
file_header 结构,三部分组成:
文件头用来说明该二进制文件将运行在何种机器之上、分几个区段、链接的时间、是可执行文件还是DLL等等。
typedef struct _IMAGE_NT_HEADERS64 {DWORD Signature;IMAGE_FILE_HEADER FileHeader;IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;typedef struct _IMAGE_NT_HEADERS {DWORD Signature;IMAGE_FILE_HEADER FileHeader;IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;#ifdef _WIN64
typedef IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS64 PIMAGE_NT_HEADERS;
#else
typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS;
#endif
IMAGE_FILE_HEADER 占 20 个字节:
typedef struct _IMAGE_FILE_HEADER {WORD Machine; // 运行平台WORD NumberOfSections; // 块数目DWORD TimeDataStamp; // 时间日期标记DWORD PointerToSymbolTable; // COFF 符号指针,这是程序调试信息DWORD NumberOfSymbols; // 符号数WORD SizeOfOptionalHeader; // 可选部首长度,是 IMAGE_FILE_HEADER 的长度WORD Characteristics; // 文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
Machine(机器码)指定运行平台:
#define IMAGE_FILE_MACHINE_UNKNOWN 0#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386.#define IMAGE_FILE_MACHINE_ALPHA 0x0184 // Alpha_AXP#define IMAGE_FILE_MACHINE_POWERPC 0x01F0 // IBM PowerPC Little-Endian#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64(K8)
Characteristics 文件属性取值,文件是否为可运行的状态,是否为DLL文件等信息:
#define IMAGE_FILE_RELOCS_STRIPPED 0x0001
// Relocation info stripped from file.#define IMAGE_FILE_ EXECUTABLE_IMAGE 0x0002
// File is executable. - 文件是否可以执行#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004
// Line nunbers stripped from file.#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008
// Local symbols stripped from file.#define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010
// Agressively trim working set#define IMAGE_FILE_LARGE_ADDRESS_AMARE 0x0020
// App can handle >2gb addresses#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080
// Bytes of machine word are reversed.#define IMAGE_FILE_32BIT_MACHINE 0x0100
// 32 bit word nachine.#define IMAGE_FILE_DEBUG_STRIPPED 0x0200
// Debugging info stripped .DBG file - 是否包含调试信息#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 #define IMAGE_FILE_NET_RUN _FROH _SWAP 0x0800
// If Image is on Net.#define IMAGE_FILE_SYSTEM 0x1000
// System File. - 是否是系统文件#define IMAGE_FILE_DLL 0x2000
// File is a DLL. - 是否是 DLL 文件#define IMAGE_FILE_UP_SYSTEM_CNLY 0x4000#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000
// Bytes of machine word are reversed.
从 PE 头进入 IMAGE_FILE_HEADER,只需要在成员 e_ifanew
+ 4 个字节即可。
可选头
可选头虽然叫作可选,但是却包含了大量PE文件的重要信息。
Optioal_header 分 32、64 位,俩者相差不大。
可以下载软件 CFF Explorer 直接查看。
其中,比较重要的几个字段:
- Magic,标记32位和64位可选头,为IMAGE_OPTIONAL_HEADER32时,magic码为10B,为IMAGE_OPTIONAL_HEADER64时,magic码为20B
- MajorLinkerVersion和MinorLinkerVersion,链接器的版本号
- SizeOfCode,代码段的长度
- MajorOperatingSystemVersion、MinorOperatingSystemVersion,所需操作系统的版本号
- MajorImageVersion、MinorImageVersion,映像的版本号
- MajorSubsystemVersion、MinorSubsystemVersion,所需子系统版本号
- SizeOfImage,映像的大小
- SizeOfHeaders,所有文件头的大小
- Subsystem,运行该PE文件所需的子系统。
PE RVA 地址与文件地址转换
这些节在内存中是按照 0x1000 对齐,
PE 首先加载到一个基地址:ImageBase(0x40000,也可能随机)
- VA:虚拟地址
- RVA:相对虚拟地址,RVA = VA - ImageBase
- VOffset:该节起始地址对于 ImageBase 的偏移量,如 .text 块(0x0401000) - 基地址(0x0400000)
- ROffset:该节起始地址对于文件起始位置的偏移量,如 .text 块(0x0401000) - 文件起始地址(0x0000000)
比如你获得了一条虚拟地址,这些条件都知道 ImageBase、VOffset、ROffset、VA,请算某一个虚拟地址 VA,对应的文件偏移地址:fRVA。
- fRVA - ROffset = RVA - VOffset
- fRVA = RVA - VOffset + ROffset
- RVA = VA - ImageBase
- fRVA = VA - ImageBase - VOffset + ROffset
上述等式原理:虚拟地址和文件地址对于字起始的偏移是固定的。
块表 Section Header
块表结构:
可以下载软件 CFF Explorer 直接查看。
导入表 Data Directory
typedef struct _IMAGE_DATA_DIRECTORY {DWORD VirtualAddress; // RVA 相对虚拟地址DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGEDATA_DIRECTORY;#define IMAGE_NUMBEROF_DIRECTOF_ENTRIES 16
Data Directory[16],那 16 个元素是那些:
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0
// Export Directory 导出表#define IMAGE_DIRECTORY_ENTRY_IMPORT 1
// Import Directory 导入表#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
// ResourcE_Directory 资源表#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
// Exception Directory 异常表#define IMAGE_DIRECTORY_ENTRY_SECURITY
// Security Directory 重定向表#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
// Base Relocation Table#define IMAGE_DIRECTORY_ENTRY_DEBUG 6
// Debug Directory// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7(x86 usage)#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7
// Architecture Specific Data#define IMAGE _DIRECTORY_ENTRY_GLOBALPTR 8
// RVA of GP#define IMAGE_DIRECTORY_ENTRY_TLS 9
// TLS Directory#define IMAGE_DIRECTORY_ENTRY LOAD_CONFIG 10
// Load Configuration Directory#define IMAGE_DIRECTORY_ENTRY BOUND_IMPORT 11
// Bound Import Directory in headers#define IMAGE_DIRECTORY_ENTRY_IAT 12
// Import Address Table#define IMAGE _DIRECTORY_ENTRY DELAY_IMPORT 13
// Delay Load Import Deseriptors#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14
// COM RuntimE_Descriptor
可以下载软件 CFF Explorer 直接查看。
基址重定位 reloc
EXE的默认加载地址是 0x400000,DLL的默认加载地址是 0x100000。
如果加载的基地址不是默认的地址(比如多个 DLL 加载),那么使用固定地址就需要重新定位。
这些代码地址就会记录在 reloc
表里。
C语言解析 PE 结构
#include <windows.h>
#include <stdio.h>
#pragma warning(disable:4996)bool isPeValid(char *file) {if(file==NULL)return FALSE;FILE *fp;IMAGE_DOS_HEADER dosheader;unsigned long pesig;fp = fopen(file,"r+b");if(fp == NULL)return FALSE;fread(&dosheader,sizeof(dosheader),1,fp);fseek(fp,dosheader.e_lfanew,SEEK_SET);fread(&pesig,4,1,fp);fclose(fp);if((dosheader.e_magic ==IMAGE_DOS_SIGNATURE) &&(pesig == IMAGE_NT_SIGNATURE)) // dos header e_magic和PE头部里的signature必须为固定值:MZ(0x5A4D )和PE00(0x00004550)return TRUE;return FALSE;
}int peDosHeaderAnalyze(char *file) {if(file==NULL)return -1;FILE *fp;IMAGE_DOS_HEADER dosheader;unsigned long pesig;fp = fopen(file,"r+b");if(fp == NULL)return -1;fread(&dosheader,sizeof(dosheader),1,fp);fseek(fp,dosheader.e_lfanew,SEEK_SET);fread(&pesig,4,1,fp);fclose(fp);printf("IMAGE_DOS_HEADER info:\n");printf("e_magic : %04x\n",dosheader.e_magic);//"MZ"-->"ZM":0x5A4Dprintf("e_cblp : %04x\n",dosheader.e_cblp);printf("e_cp : %04x\n",dosheader.e_cp);printf("e_crlc : %04x\n",dosheader.e_crlc);printf("e_cparhdr : %04x\n",dosheader.e_cparhdr);printf("e_minalloc: %04x\n",dosheader.e_minalloc);printf("e_maxalloc: %04x\n",dosheader.e_maxalloc);printf("e_ss : %04x\n",dosheader.e_ss);printf("e_sp : %04x\n",dosheader.e_sp);printf("e_csum : %04x\n",dosheader.e_csum);printf("e_ip : %04x\n",dosheader.e_ip);printf("e_cs : %04x\n",dosheader.e_cs);printf("e_lfarlc : %04x\n",dosheader.e_lfarlc);printf("e_ovno : %04x\n",dosheader.e_ovno);printf("e_res[0] : %04x\n",dosheader.e_res[0]);printf("e_oemid : %04x\n",dosheader.e_oemid);printf("e_oeminfo : %04x\n",dosheader.e_oeminfo);printf("res2[0] : %04x\n",dosheader.e_res2[0]);printf("lfanew : %08x\n",dosheader.e_lfanew);return 0;
}int peFileHeaderAnalyze(char *file) {if(file==NULL)return -1;FILE *fp;IMAGE_DOS_HEADER dosheader;IMAGE_FILE_HEADER fileheader;unsigned long pesig;fp = fopen(file,"r+b");if(fp == NULL)return -1;fread(&dosheader,sizeof(dosheader),1,fp);fseek(fp,dosheader.e_lfanew,SEEK_SET);fread(&pesig,4,1,fp);fread(&fileheader, sizeof(fileheader), 1, fp);fclose(fp);printf("IMAGE_FILE_HEADER info:\n");printf("Sinature\t\t: %08x\n",pesig);printf("Machine\t\t\t: %04x\n",fileheader.Machine);printf("NumberOfSections\t: %08x\n",fileheader.NumberOfSections);printf("TimeDateStamp\t\t: %08x\n",fileheader.TimeDateStamp);printf("PointerToSymbolTable\t: %08x\n",fileheader.PointerToSymbolTable);printf("NumberOfSymbols\t\t: %04x\n",fileheader.NumberOfSymbols);printf("SizeOfOptionalHeader\t: %04x\n",fileheader.SizeOfOptionalHeader);printf("Characteristics\t\t: %04x\n",fileheader.Characteristics);return 0;
}int peOptionalHeaderAnalyze(char *file) {if(file==NULL)return -1;FILE *fp;IMAGE_DOS_HEADER dosheader;IMAGE_FILE_HEADER fileheader;IMAGE_OPTIONAL_HEADER32 opthdr32;IMAGE_OPTIONAL_HEADER64 opthdr64;unsigned long pesig;fp = fopen(file,"r+b");if(fp == NULL)return -1;fread(&dosheader,sizeof(dosheader),1,fp);fseek(fp,dosheader.e_lfanew,SEEK_SET);fread(&pesig,4,1,fp);fread(&fileheader, sizeof(fileheader), 1, fp);if(fileheader.Machine==IMAGE_FILE_MACHINE_I386) {fread(&opthdr32, sizeof(opthdr32), 1, fp);printf("32bit pe format\n");printf("IMAGE_OPTIONAL_HEADER info:\n");printf("Magic\t\t\t\t: %04x\n",opthdr32.Magic);printf("ImageBase\t\t\t: %04x\n",opthdr32.ImageBase);printf("Subsystem\t\t\t: %08x\n",opthdr32.Subsystem);printf("AddressOfEntryPoint\t\t: %08x\n",opthdr32.AddressOfEntryPoint);printf("SizeOfCode\t\t\t: %08x\n",opthdr32.SizeOfCode);printf("SizeOfImage\t\t\t: %04x\n",opthdr32.SizeOfImage);printf("SizeOfHeaders\t\t\t: %04x\n",opthdr32.SizeOfHeaders);printf("SectionAlignment\t\t: %04x\n",opthdr32.SectionAlignment);printf("FileAlignment\t\t\t: %04x\n",opthdr32.FileAlignment);printf("\nDataDirectory info:\n");for(int i=0;i<IMAGE_NUMBEROF_DIRECTORY_ENTRIES;i++)printf("DataDirectory[%02x]:%08x\n",i,opthdr32.DataDirectory[i].VirtualAddress);} else if(fileheader.Machine==IMAGE_FILE_MACHINE_AMD64) {printf("64bit pe format\n");fread(&opthdr64, sizeof(opthdr64), 1, fp);printf("IMAGE_OPTIONAL_HEADER info:\n");printf("Magic\t\t\t\t: %04x\n",opthdr64.Magic);printf("ImageBase\t\t\t: %I64x\n",opthdr64.ImageBase);printf("Subsystem\t\t\t: %08x\n",opthdr64.Subsystem);printf("AddressOfEntryPoint\t\t: %08x\n",opthdr64.AddressOfEntryPoint);printf("SizeOfCode\t\t\t: %08x\n",opthdr64.SizeOfCode);printf("SizeOfImage\t\t\t: %04x\n",opthdr64.SizeOfImage);printf("SizeOfHeaders\t\t\t: %04x\n",opthdr64.SizeOfHeaders);printf("SectionAlignment\t\t: %04x\n",opthdr64.SectionAlignment);printf("FileAlignment\t\t\t: %04x\n",opthdr64.FileAlignment);printf("\nDataDirectory info:\n");for(int i=0;i<IMAGE_NUMBEROF_DIRECTORY_ENTRIES;i++)printf("DataDirectory[%02x]:%08x\n",i,opthdr64.DataDirectory[i].VirtualAddress);}fclose(fp);return 0;
}int peSectionHeaderAnalyze(char *file) {if(file==NULL)return -1;FILE *fp;IMAGE_DOS_HEADER dosheader;IMAGE_FILE_HEADER fileheader;IMAGE_SECTION_HEADER sechdr;unsigned long pesig;fp = fopen(file,"r+b");if(fp == NULL)return -1;fread(&dosheader,sizeof(dosheader),1,fp);fseek(fp,dosheader.e_lfanew,SEEK_SET);fread(&pesig,4,1,fp);fread(&fileheader, sizeof(fileheader), 1, fp);fseek(fp, dosheader.e_lfanew+4+sizeof(fileheader)+fileheader.SizeOfOptionalHeader,SEEK_SET);for(int i =0;i<fileheader.NumberOfSections;i++) {fread(&sechdr, sizeof(sechdr),1,fp);printf("SectionName\t\t:%s\n",sechdr.Name);printf("VirtualAddress\t\t:%08x\n",sechdr.VirtualAddress);printf("SizeofRawData\t\t:%08x\n", sechdr.SizeOfRawData);printf("PointerToRawData\t:%08x\n", sechdr.PointerToRawData);printf("Characteristics\t\t:%08x\n", sechdr.Characteristics);printf("\n");}fclose(fp);return 0;
}int main(int argc, char* argv[]) {char *pefile="D:\\mfpedemo_x64debug.exe";if(isPeValid(pefile)) {peDosHeaderAnalyze(pefile);printf("\n");peFileHeaderAnalyze(pefile);printf("\n");peOptionalHeaderAnalyze(pefile);printf("\n");peSectionHeaderAnalyze(pefile);} elseprintf("not a valid pe file\n");return 0;
}
ELF 格式
ELF 文件有俩种视图,一个是磁盘上视图,另一个是加载到内存中的运行视图。
ELF header:位于文件开始部分,位置固定,表明文件的组织情况。
Program header table:告诉系统如何来创建一个进程的内存映像。
section header table:位于 ELF 文件末尾,包含了描述节区的信息。
ELF header:
- e_ident[EI_NIDENT] 字段包含魔数、字节序、字长和版本,后面填充 0。
readelf 命令解析 ELF 格式
readelf 命令解析 ELF 格式:
- readelf -h demo.so:查看 .so 文件的头部信息
- readelf -S demo.so:查看 .so 文件的节头信息
- readelf -l demo.so:查看 .so 文件的程序段头信息
- readelf -a demo.so:查看 .so 文件的全部内容
基于机器学习的恶意程序检测
基于机器学习的恶意程序检测,主要分为两类:
- 一类是通过PE文件可以直接获取到的特征,比如字节直方图,字节熵直方图和字符串特征等;
- 另一类特征是需要解析PE文件结构,从各个节分析出的特征,比如节头特征,导入和导出表特征,文件头特征等。
通过PE文件可以直接获取到的特征
字节直方图
本质上 PE 文件也是二进制文件,可以当作一连串字节组成的文件。
我们可以定义一个长度为 256 维的向量,每个向量依次为 0x00,0x01 一直到 0xFF,分别代表 PE 文件中 0x00,0x01 一直到 0xFF 对应的个数。
假设 PE 文件对应的二进制流为:0x01 0x05 0x03 0x01。
经过统计,0x01 有两个,0x03 和 0x05 对应的各一个,假设直方图维度为8,所以对应的直方图为:[0,2,0,1,0,0,1,0,0]。
np.frombuffer(bytez, dtype=np.uint8)
# 处理PE文件时,假设PE文件以字节数组的形式保存在变量bytez中,将bytez转换成numPy的数组便于处理
h = np.bincount(np.frombuffer(bytez, dtype=np.uint8), minlength=256)
# 将bytez转换成字节直方图,numpy.bincount可以直接实现字节直方图,作用是以字节为单位统计个数
实际使用时,单纯统计直方图非常容易过拟合,因为字节直方图对于PE文件的二进制特征过于依赖,PE文件增加一个无意义的0字节都会改变直方图;另外PE文件中不同字节的数量可能差别很大,数量占优势的字节可能会大大弱化其他字节对结果的影响,所以需要对直方图进行标准化处理。
一种常见的处理方式是,增加一个维度的变量,用于统计PE文件的字节总数,同时原有直方图按照字节总数取平均值:
return np.concatenate([[h.sum()], # total size of the byte stream h.astype( self.dtype ).flatten() / h.sum(), # normalized the histogr
am])
文本特征
恶意文件通常在文本特征方面与正常文件有所区分,需要关注的文本特征:
- 可读字符串个数
- 平均可读字符串长度
- 可读字符直方图
- 可读字符信息熵
- C盘路径字符串个数
- 注册表字符串个数
- URL字符串个数
- MZ头的个数
# 可读字符串个数:由文本文件中常见的字母、数字和符号组成的字符串
self._allstrings = re.compile(b'[\x20-\x7f]{5,}') # 集中在ASCII码值在0x20~0x7F之间的字符,而且长度不小于5的字符串为可读字符串个数
allstrings = self._allstrings.findall(bytez)
len(allstrings) # 统计可读字符串的个数
# 平均可读字符串长度:在获取了全部可读字符串的基础上,计算其平均长度
string_lengths = [len(s) for s in allstrings]
avlength = sum(string_lengths) / len(string_lengths)
# 可读字符直方图:统计可读字符串的字符直方图,由于可读字符的个数为96个,所以我们定义一个长度为96的向量统计其直方图
as_shifted_string = [b - ord(b'\x20') for b in b''.join(allstrings)]
c = np.bincount(as_shifted_string, minlength=96)
p = c.astype(np.float32) / c.sum()
# 可读字符信息熵
wh = np.where(c)[0]
H = np.sum(-p[wh] * np.log2(p[wh]))
# C盘路径字符串个数
self._paths = re.compile(b'c:\\\\', re.IGNORECASE)
# 恶意程序通常对被感染系统的根目录有一定的文件操作行为,表现在可读字符串中,可能会包含硬编码的C盘路径
[len(self._paths.findall(bytez))]
# 注册表字符串个数
self._registry = re.compile(b'HKEY_')
# 恶意程序通常对被感染系统的注册表有一定的文件操作行为,表现在可读字符串中,可能会包含硬编码的注册表值
[len(self._registry.findall(bytez))]
# URL字符串个数
self._urls = re.compile(b'https?://', re.IGNORECASE)
# 恶意程序通常从指定URL下载资源,最典型的就是下载者病毒,表现在可读字符串中,可能会包含硬编码的URL
[len(self._urls.findall(bytez))]
# MZ头的个数
self._mz = re.compile(b'MZ')
[len(self._mz.findall(bytez))]
完整的的文本特征:
return np.concatenate([[[len(allstrings)]], [[avlength]], [p.tolist()], [[H]], [[len(self._paths.findall(bytez))]], [[len(self._urls.findall(bytez))]], [[len(self._registry.findall(bytez))]], [[len(self._mz.find all(bytez))]]], axis=-1).flatten().astype(self.dtype)
从各个节分析出的特征
节中包含事实上真正运行的代码,所以也是特征提取的一个重点。
训练模型
PE 格式详解与试验相关推荐
- PE文件结构详解(四)PE导入表
PE文件结构详解(二)可执行文件头的最后展示了一个数组,PE文件结构详解(三)PE导出表中解释了其中第一项的格式,本篇文章来揭示这个数组中的第二项:IMAGE_DIRECTORY_ENTRY_IMPO ...
- PE文件结构详解(二)可执行文件头
by evil.eagle 转载请注明出处. http://blog.csdn.net/evileagle/article/details/11903197 在PE文件结构详解(一)基本概念里,解释了 ...
- 【转】PE文件结构详解--(完整版)
(一)基本概念 PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任 ...
- PE文件结构详解 --(完整版)
From:https://blog.csdn.net/adam001521/article/details/84658708 PE结构详解:https://www.cnblogs.com/zheh/p ...
- PE文件结构详解(一)基本概念
(一)基本概念 PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任 ...
- 【破解教程】PE文件格式详解(上)
PE文件格式详解(上) 摘要 Windows NT 3.1引入了一种名为PE文件格式的新可执行文件格式.PE文件格式的规范包含在了MSDN的CD中(Specs and Strategy, Specif ...
- PE文件详解(教程1-7)
PE文件详解(教程1-7) ========================================= PE教程1: PE文件格式一览 PE 的意思就是 Portable Executable ...
- Java字节码(.class文件)格式详解(一)
原文链接:http://www.blogjava.net/DLevin/archive/2011/09/05/358033.html 小介:去年在读<深入解析JVM>的时候写的,记得当时还 ...
- php serialize取值,PHP 序列化(serialize)格式详解
PHP 序列化(serialize)格式详解(转) 1.前言 PHP (从 PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize.unserialize.不过在 PH ...
最新文章
- 监听UIWebView点击视频播放的事件
- Python基础总结(1)
- 苹果电脑+VR头显不久将成为现实,macOS新系统正式上线
- Zookeeper基于Java 访问
- 一起谈.NET技术,WPF 基础到企业应用系列5——WPF千年轮回2
- php输出excel表格乱码和第一个0不显示的解决方法(详细)
- CAN总线技术 | 数据链路层04 - CAN节点状态与错误处理机制
- php怎么将网页变成图片格式,php如何实现图片格式转换
- ORACLE中exists与in的区别
- 「06」回归的诱惑:一文读懂线性回归(Python实战篇)
- c语言库函数手册pdf百度云,C语言库函数手册.pdf
- Hadoop安装教程(单机/伪分布式配置)
- JavaScript 动态生成表格 及删除表格
- 使用html2canvas实现超出浏览器部分截图
- Java基础知识(十) 多线程
- 基于CNN卷积神经网络实现手势识别
- 写点看Harvard CS50 公开课的感受
- 74LS85 比較器 【数字电路】
- 讲解关于编写跨平台Java程序时的注意事项 选择自 tiewen 的 Blog
- MeeGo镜像下载地址汇总
热门文章
- 论文阅读(13) 水母游泳过程中的神经机械波共振(2021)
- linux内核结构介绍及驱动引入
- layui 表格内容写temple函数_templet渲染layui表格数据的三种方式
- 编译报错unable to initialize decompress status for section .debug_info
- 企业运维之kubernetes监控
- 必修三计算机选修三知识点总结,高二必修三物理知识点总结
- win10+ubuntu18.04安装+sougou输入法
- imx6 android4.4 Recovery添加从U盘升级功能
- 设值单元格字体和背景色的颜色-实例
- 转:微信、米聊用户数量猛增 短信业务受冲击