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 格式详解与试验相关推荐

  1. PE文件结构详解(四)PE导入表

    PE文件结构详解(二)可执行文件头的最后展示了一个数组,PE文件结构详解(三)PE导出表中解释了其中第一项的格式,本篇文章来揭示这个数组中的第二项:IMAGE_DIRECTORY_ENTRY_IMPO ...

  2. PE文件结构详解(二)可执行文件头

    by evil.eagle 转载请注明出处. http://blog.csdn.net/evileagle/article/details/11903197 在PE文件结构详解(一)基本概念里,解释了 ...

  3. 【转】PE文件结构详解--(完整版)

    (一)基本概念 PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任 ...

  4. PE文件结构详解 --(完整版)

    From:https://blog.csdn.net/adam001521/article/details/84658708 PE结构详解:https://www.cnblogs.com/zheh/p ...

  5. PE文件结构详解(一)基本概念

    (一)基本概念 PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任 ...

  6. 【破解教程】PE文件格式详解(上)

    PE文件格式详解(上) 摘要 Windows NT 3.1引入了一种名为PE文件格式的新可执行文件格式.PE文件格式的规范包含在了MSDN的CD中(Specs and Strategy, Specif ...

  7. PE文件详解(教程1-7)

    PE文件详解(教程1-7) ========================================= PE教程1: PE文件格式一览 PE 的意思就是 Portable Executable ...

  8. Java字节码(.class文件)格式详解(一)

    原文链接:http://www.blogjava.net/DLevin/archive/2011/09/05/358033.html 小介:去年在读<深入解析JVM>的时候写的,记得当时还 ...

  9. php serialize取值,PHP 序列化(serialize)格式详解

    PHP 序列化(serialize)格式详解(转) 1.前言 PHP (从 PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize.unserialize.不过在 PH ...

最新文章

  1. 监听UIWebView点击视频播放的事件
  2. Python基础总结(1)
  3. 苹果电脑+VR头显不久将成为现实,macOS新系统正式上线
  4. Zookeeper基于Java 访问
  5. 一起谈.NET技术,WPF 基础到企业应用系列5——WPF千年轮回2
  6. php输出excel表格乱码和第一个0不显示的解决方法(详细)
  7. CAN总线技术 | 数据链路层04 - CAN节点状态与错误处理机制
  8. php怎么将网页变成图片格式,php如何实现图片格式转换
  9. ORACLE中exists与in的区别
  10. 「06」回归的诱惑:一文读懂线性回归(Python实战篇)
  11. c语言库函数手册pdf百度云,C语言库函数手册.pdf
  12. Hadoop安装教程(单机/伪分布式配置)
  13. JavaScript 动态生成表格 及删除表格
  14. 使用html2canvas实现超出浏览器部分截图
  15. Java基础知识(十) 多线程
  16. 基于CNN卷积神经网络实现手势识别
  17. 写点看Harvard CS50 公开课的感受
  18. 74LS85 比較器 【数字电路】
  19. 讲解关于编写跨平台Java程序时的注意事项 选择自 tiewen 的 Blog
  20. MeeGo镜像下载地址汇总

热门文章

  1. 论文阅读(13) 水母游泳过程中的神经机械波共振(2021)
  2. linux内核结构介绍及驱动引入
  3. layui 表格内容写temple函数_templet渲染layui表格数据的三种方式
  4. 编译报错unable to initialize decompress status for section .debug_info
  5. 企业运维之kubernetes监控
  6. 必修三计算机选修三知识点总结,高二必修三物理知识点总结
  7. win10+ubuntu18.04安装+sougou输入法
  8. imx6 android4.4 Recovery添加从U盘升级功能
  9. 设值单元格字体和背景色的颜色-实例
  10. 转:微信、米聊用户数量猛增 短信业务受冲击