gpt4 book ai didi

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

转载 作者:qq735679552 更新时间:2022-09-27 22:32:09 24 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章C++ 实现PE文件特征码识别的步骤由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

打开PE文件映射:

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#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结构所使用的的结构定义略有不同,代码中已经对其进行了区分. 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#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个字节就差不多了,当然为了匹配精度更高,我们也可以对多个字段进行验证,这里就只写出大体轮廓吧. 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#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++ 实现PE文件特征码识别的步骤

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

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

最后此篇关于C++ 实现PE文件特征码识别的步骤的文章就讲到这里了,如果你想了解更多关于C++ 实现PE文件特征码识别的步骤的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com