打开PE文件映射:
在读取PE结构之前,首先要做的就是打开PE文件到内存,这里打开文件我们使用了CreateFile()函数该函数可以打开文件并返回文件句柄,接着使用CreateFileMapping()函数创建文件的内存映像,最后使用MapViewOfFile()读取映射中的内存并返回一个句柄,后面的程序就可以通过该句柄操作打开后的文件了.
#include#include #include #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#include #include #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#include #include #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文件特征码识别的资料请关注脚本之家其它相关文章!