文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

C++ 实现PE文件特征码识别的步骤

2024-04-02 19:55

关注

打开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,特征码的提取应尽量保证一致性。

文章出处:https://www.cnblogs.com/lyshark

以上就是C++ 实现PE文件特征码识别的步骤的详细内容,更多关于C++ PE文件特征码识别的资料请关注编程网其它相关文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯