PE文件就是我们常说的EXE可执行文件,针对文件特征的识别可以清晰的知道该程序是使用何种编程语言实现的,前提是要有特征库,PE特征识别有多种形式,第一种是静态识别,此方法就是只针对磁盘中文件的特征码字段进行检测来判断编译器版本,此种方式优点是快,缺点是识别不准确,第二种则是动态识别,当程序被装入内存解码后在尝试对其进行识别,此方法最为准确,我们经常使用的PEID查壳工具是基于静态检测的方法。

打开PE文件映射: 在读取PE结构之前,首先要做的就是打开PE文件到内存,这里打开文件我们使用了CreateFile()函数该函数可以打开文件并返回文件句柄,接着使用CreateFileMapping()函数创建文件的内存映像,最后使用MapViewOfFile()读取映射中的内存并返回一个句柄,后面的程序就可以通过该句柄操作打开后的文件了.

#include <stdio.h>
#include <Windows.h>
#include <ImageHlp.h> #pragma comment(lib,"Imagehlp.lib") // 读取PE结构的封装
HANDLE OpenPeFile(LPTSTR FileName)
{
HANDLE hFile, hMapFile, lpMapAddress = NULL;
DWORD dwFileSize = 0; // CreateFile 既可以创建文件,也可以打开文件,这里则是打开文件的含义
hFile = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return 0; // 获取到文件大小
dwFileSize = GetFileSize(hFile, NULL); // 创建文件的内存映像
// 此方法非常灵活,其不需要将程序完全读入内存中,节约空间。
hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
if (hMapFile == NULL)
return 0; // 读取映射中的内存并返回一个句柄
lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, dwFileSize);
if (lpMapAddress != NULL)
return lpMapAddress;
return 0;
} int main(int argc, char * argv[])
{
HANDLE lpMapAddress = NULL; lpMapAddress = OpenPeFile("c://lyshark.exe");
printf("打开文件句柄: %d \n", lpMapAddress); system("pause");
return 0;
}

判断是否为PE文件: 当文件已经打开后,接下来就要判断文件是否为有效的PE文件,这里我们首先将镜像转换为PIMAGE_DOS_HEADER格式并通过pDosHead->e_magic属性找到PIMAGE_NT_HEADERS结构,然后判断其是否符合PE文件规范即可,这里需要注意32位于64位PE结构所使用的的结构定义略有不同,代码中已经对其进行了区分.

#include <stdio.h>
#include <Windows.h>
#include <ImageHlp.h> #pragma comment(lib,"Imagehlp.lib") // 读取PE结构的封装
HANDLE OpenPeFile(LPTSTR FileName)
{
HANDLE hFile, hMapFile, lpMapAddress = NULL;
DWORD dwFileSize = 0; // CreateFile 既可以创建文件,也可以打开文件,这里则是打开文件的含义
hFile = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return 0; // 获取到文件大小
dwFileSize = GetFileSize(hFile, NULL); // 创建文件的内存映像
// 此方法非常灵活,其不需要将程序完全读入内存中,节约空间。
hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
if (hMapFile == NULL)
return 0; // 读取映射中的内存并返回一个句柄
lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, dwFileSize);
if (lpMapAddress != NULL)
return lpMapAddress;
return 0;
} // 判断是否为PE文件
BOOL IsPeFile(HANDLE ImageBase, BOOL Is64 = FALSE)
{
PIMAGE_DOS_HEADER pDosHead = NULL;
if (ImageBase == NULL)
return FALSE; // 将映射文件转为DOS结构,并判断开头是否为MZ
pDosHead = (PIMAGE_DOS_HEADER)ImageBase;
if (IMAGE_DOS_SIGNATURE != pDosHead->e_magic)
return FALSE; if (Is64 == TRUE)
{
// 根据 IMAGE_DOS_HEADER 的 e_lfanew 的值得到 64位 NT 头的位置
PIMAGE_NT_HEADERS64 pNtHead64 = NULL;
pNtHead64 = (PIMAGE_NT_HEADERS64)((DWORD64)pDosHead + pDosHead->e_lfanew);
if (pNtHead64->Signature != IMAGE_NT_SIGNATURE)
return FALSE;
}
else if (Is64 == FALSE)
{
// 根据 IMAGE_DOS_HEADER 的 e_lfanew 的值得到 32位 NT 头的位置
PIMAGE_NT_HEADERS pNtHead32 = NULL;
pNtHead32 = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
if (pNtHead32->Signature != IMAGE_NT_SIGNATURE)
return FALSE;
}
return TRUE;
} int main(int argc, char * argv[])
{
HANDLE lpMapAddress = NULL; // 打开文件拿到PE句柄
lpMapAddress = OpenPeFile("c://lyshark.exe"); // 判断是否为PE文件,这里定义的为真返回1,为假返回0
BOOL ret = IsPeFile(lpMapAddress, 0);
printf("是否为PE文件: %d \n", ret); system("pause");
return 0;
}

判断PE文件特征码: 判断程序使用了何种编译器编写,通常情况是要用文件的入口处代码和特征码进行匹配,通常情况下我们只需要匹配程序开头的前32个字节就差不多了,当然为了匹配精度更高,我们也可以对多个字段进行验证,这里就只写出大体轮廓吧.

#include <stdio.h>
#include <Windows.h>
#include <ImageHlp.h> #pragma comment(lib,"Imagehlp.lib") // 读取PE结构的封装
HANDLE OpenPeFile(LPTSTR FileName)
{
HANDLE hFile, hMapFile, lpMapAddress = NULL;
DWORD dwFileSize = 0; // CreateFile 既可以创建文件,也可以打开文件,这里则是打开文件的含义
hFile = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return 0; // 获取到文件大小
dwFileSize = GetFileSize(hFile, NULL); // 创建文件的内存映像
// 此方法非常灵活,其不需要将程序完全读入内存中,节约空间。
hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
if (hMapFile == NULL)
return 0; // 读取映射中的内存并返回一个句柄
lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, dwFileSize);
if (lpMapAddress != NULL)
return lpMapAddress;
return 0;
} // 判断是否为PE文件
BOOL IsPeFile(HANDLE ImageBase, BOOL Is64 = FALSE)
{
PIMAGE_DOS_HEADER pDosHead = NULL;
if (ImageBase == NULL)
return FALSE; // 将映射文件转为DOS结构,并判断开头是否为MZ
pDosHead = (PIMAGE_DOS_HEADER)ImageBase;
if (IMAGE_DOS_SIGNATURE != pDosHead->e_magic)
return FALSE; if (Is64 == TRUE)
{
// 根据 IMAGE_DOS_HEADER 的 e_lfanew 的值得到 64位 NT 头的位置
PIMAGE_NT_HEADERS64 pNtHead64 = NULL;
pNtHead64 = (PIMAGE_NT_HEADERS64)((DWORD64)pDosHead + pDosHead->e_lfanew);
if (pNtHead64->Signature != IMAGE_NT_SIGNATURE)
return FALSE;
}
else if (Is64 == FALSE)
{
// 根据 IMAGE_DOS_HEADER 的 e_lfanew 的值得到 32位 NT 头的位置
PIMAGE_NT_HEADERS pNtHead32 = NULL;
pNtHead32 = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
if (pNtHead32->Signature != IMAGE_NT_SIGNATURE)
return FALSE;
}
return TRUE;
} // 扫描特征码,对比
void GetPeSignature(LPCWSTR FilePath)
{
typedef struct _SIGN
{
char FileName[64]; // 存储文件名或特征描述
LONG FileOffset; // 存储检测文件偏移地址
BYTE VirusSign[32 + 1]; // 存储特征码大小32,其中的1是结束符.
}SIGN, *pSIGN; // 定义特征码与特征描述信息,你可以自己去提取一段特征码
SIGN Sign[2] = {
{
"Microsoft Visual C/C++ x86 (2013)",
0x8a0,
"\x55\x8B\xEC\x81\xEC\xC4\x00\x00\x00\x53\x56\x57\x8D\xBD\x3C\xFF" \
"\xFF\xFF\xB9\x31\x00\x00\x00\xB8\xCC\xCC\xCC\xCC\xF3\xAB\x8B\x45" \
},
{
"Microsoft Visual C/C++ x64 (2013)",
0x400,
"\xCC\xCC\xCC\xCC\xCC\xE9\x86\x02\x00\x00\xE9\x31\x05\x00\x00\xE9" \
"\x6C\x01\x00\x00\xE9\x57\x03\x00\x00\xE9\x22\x00\x00\x00\xCC\xCC" \
}
}; DWORD dwNum = 0;
BYTE buffer[32 + 1];
HANDLE hFile = NULL; // 获取到FilePath路径下文件的句柄信息
hFile = CreateFile(FilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); // 我们有两段待检测特征,这里循环两次从零开始
for (int x = 0; x <= 2; x++)
{
// 将待检测程序的文件指针指向特征码的偏移位置
SetFilePointer(hFile, Sign[x].FileOffset, NULL, FILE_BEGIN);
// 读取目标程序指定位置的特征码到内存中
ReadFile(hFile, buffer, sizeof(buffer), &dwNum, NULL);
// 对比内存中两个特征码是否相等
if (memcmp(Sign[x].VirusSign, buffer, 32) == 0)
{
printf("检测结果: %s \n", Sign[x].FileName);
}
}
CloseHandle(hFile);
} int main(int argc, char * argv[])
{
GetPeSignature(L"c://lyshark.exe");
system("pause");
return 0;
}

你需要自己提取不同编译器的特征字段,然后按照我写好的格式进行增加,例如我是用vs2013编译的,那么检测结果就可能会是vs2013,特征码的提取应尽量保证一致性。

C/C++ 实现PE文件特征码识别的更多相关文章

  1. 浅析MSIL中间语言——PE文件结构篇

    一.开篇 开篇我想讲一下于本文无关的话题,其实我很想美化一下自己博客园一直没时间弄,无意间找了博客园李宝亨的博客园里面有一篇分享自己主题的文章,我就将这个模板暂时用作我的blog主题,我要讲述一个关于 ...

  2. PE文件学习系列笔记四-C++实现PE文件的分析

    合肥程序员群:49313181.    合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q  Q:408365330     E-Mail:egojit@qq.com 综述: 首 ...

  3. PE文件学习系列三-PE头详解

    合肥程序员群:49313181.    合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q  Q:408365330     E-Mail:egojit@qq.com 最近比较忙 ...

  4. PE文件学习系列二 DOS头分析

    合肥程序员群:49313181.    合肥实名程序员群 :128131462 (不愿透露姓名和信息者勿加入)Q  Q:408365330     E-Mail:egojit@qq.com PE文件结 ...

  5. 手写PE文件(二)

    [文章标题]: 纯手工编写的PE可执行程序 [文章作者]: Kinney [作者邮箱]: mohen_ng@sina.cn [下载地址]: 自己搜索下载 [使用工具]: C32 [操作平台]: win ...

  6. PE 文件

    一.PE文件基本结构 上图便是PE文件的基本结构.(注意:DOS MZ Header和部分PE header的大小是不变的:DOS stub部分的大小是可变的.) 二.Section 详解 一个PE文 ...

  7. 深入学习PE文件(转)

    PE文件是Win32的原生文件格式.每一个Win32可执行文件都遵循PE文件格式.对PE文件格式的了解可以加深你对Win32系统的深入理解. 一. 基本结构. 上图便是PE文件的基本结构.(注意:DO ...

  8. 深入剖析PE文件

    不赖猴的笔记,转载请注明出处. 深入剖析PE文件 PE文件是Win32的原生文件格式.每一个Win32可执行文件都遵循PE文件格式.对PE文件格式的了解可以加深你对Win32系统的深入理解. 一.   ...

  9. 【黑客免杀攻防】读书笔记6 - PE文件知识在免杀中的应用

    0x1 PE文件与免杀思路 基于PE文件结构知识的免杀技术主要用于对抗启发式扫描. 通过修改PE文件中的一些关键点来达到欺骗反病毒软件的目的. 修改区段名 1.1 移动PE文件头位置免杀 工具:PeC ...

随机推荐

  1. Jdk1.8下的HashMap源码分析

    目录结构 一.面试常见问题 二.基本常量属性 三.构造方法 四.节点结构        4.1 Node类        4.2.TreeNode 五.put方法        5.1 key的has ...

  2. Ubuntu18.04安装Nautilus-actions自定义文件管理器鼠标右键列表

    sudo add-apt-repository ppa:daniel-marynicz/filemanager-actions #需要添加源 sudo apt-get install filemana ...

  3. 总结关于Ubuntu 安装 Docker 配置相关问题及解决方法

    总结关于Ubuntu 安装 Docker 配置相关问题及解决方法 Tomcat 示例 软件镜像(xx安装程序)----运行镜像----产生一个容器(正在运行的软件,运行的xx): 步骤: 1.搜索镜像 ...

  4. python3 - selenium常用的参数

    # 选用开发者模式,创建一个浏览器对象,可避免被检测到是selenium模拟浏览器 chrome_options = webdriver.ChromeOptions() chrome_options ...

  5. Ubuntu 18.04 安装 docker, 设置阿里云加速

    1.卸载旧的 docker 环境 sudo apt-get remove docker docker-engine docker.io containerd runc 2.设置仓库 更新索引 sudo ...

  6. 【MySQL】记一次线上重大事故:二狗子竟然把线上数据库删了!!

    写在前面 估计二狗子这几天是大姨夫来了,心情很郁闷,情绪也很低落,工作的时候也有点心不在焉.让他发个版本,结果,一行命令下去把线上的数据库删了!你没听错:是删掉了线上的数据库!运营那边顿时炸了锅:怎么 ...

  7. windows下RocketMQ的安装部署

    一.预备环境 1.系统 Windows 2. 环境 JDK1.8.Maven.Git 二. RocketMQ部署 1.下载 1.1地址:http://rocketmq.apache.org/relea ...

  8. NameNode和SecondaryNameNode(面试开发重点)

    NameNode和SecondaryNameNode(面试开发重点) 1 NN和2NN工作机制 思考:NameNode中的元数据是存储在哪里的? 首先,我们做个假设,如果存储在NameNode节点的磁 ...

  9. Vue管理系统前端系列三登录页和首页及`vuex`管理登录状态

    目录 登录页面设计 vuex 对应 用户模块 丰富界面 首页相关代码 登录页面设计 该节记录了登录界面的设计,以及 vuex 的简单实用,然后将首页简单搭建完成. 先看最终效果图 先在 views 文 ...

  10. 构造vlan报文

    #!/usr/bin/python from scapy.all import * packet = Ether(src='dc:99:14:01:a3:5e', dst='ff:ff:ff:ff:f ...