C++ 實(shí)現(xiàn)PE文件特征碼識(shí)別的步驟
打開PE文件映射:
在讀取PE結(jié)構(gòu)之前,首先要做的就是打開PE文件到內(nèi)存,這里打開文件我們使用了CreateFile()函數(shù)該函數(shù)可以打開文件并返回文件句柄,接著使用CreateFileMapping()函數(shù)創(chuàng)建文件的內(nèi)存映像,最后使用MapViewOfFile()讀取映射中的內(nèi)存并返回一個(gè)句柄,后面的程序就可以通過該句柄操作打開后的文件了.
#include <stdio.h>
#include <Windows.h>
#include <ImageHlp.h>
#pragma comment(lib,"Imagehlp.lib")
// 讀取PE結(jié)構(gòu)的封裝
HANDLE OpenPeFile(LPTSTR FileName)
{
HANDLE hFile, hMapFile, lpMapAddress = NULL;
DWORD dwFileSize = 0;
// CreateFile 既可以創(chuàng)建文件,也可以打開文件,這里則是打開文件的含義
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);
// 創(chuàng)建文件的內(nèi)存映像
// 此方法非常靈活,其不需要將程序完全讀入內(nèi)存中,節(jié)約空間。
hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
if (hMapFile == NULL)
return 0;
// 讀取映射中的內(nèi)存并返回一個(gè)句柄
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文件:
當(dāng)文件已經(jīng)打開后,接下來就要判斷文件是否為有效的PE文件,這里我們首先將鏡像轉(zhuǎn)換為PIMAGE_DOS_HEADER格式并通過pDosHead->e_magic屬性找到PIMAGE_NT_HEADERS結(jié)構(gòu),然后判斷其是否符合PE文件規(guī)范即可,這里需要注意32位于64位PE結(jié)構(gòu)所使用的的結(jié)構(gòu)定義略有不同,代碼中已經(jīng)對(duì)其進(jìn)行了區(qū)分.
#include <stdio.h>
#include <Windows.h>
#include <ImageHlp.h>
#pragma comment(lib,"Imagehlp.lib")
// 讀取PE結(jié)構(gòu)的封裝
HANDLE OpenPeFile(LPTSTR FileName)
{
HANDLE hFile, hMapFile, lpMapAddress = NULL;
DWORD dwFileSize = 0;
// CreateFile 既可以創(chuàng)建文件,也可以打開文件,這里則是打開文件的含義
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);
// 創(chuàng)建文件的內(nèi)存映像
// 此方法非常靈活,其不需要將程序完全讀入內(nèi)存中,節(jié)約空間。
hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
if (hMapFile == NULL)
return 0;
// 讀取映射中的內(nèi)存并返回一個(gè)句柄
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;
// 將映射文件轉(zhuǎn)為DOS結(jié)構(gòu),并判斷開頭是否為MZ
pDosHead = (PIMAGE_DOS_HEADER)ImageBase;
if (IMAGE_DOS_SIGNATURE != pDosHead->e_magic)
return FALSE;
if (Is64 == TRUE)
{
// 根據(jù) 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)
{
// 根據(jù) 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文件特征碼:
判斷程序使用了何種編譯器編寫,通常情況是要用文件的入口處代碼和特征碼進(jìn)行匹配,通常情況下我們只需要匹配程序開頭的前32個(gè)字節(jié)就差不多了,當(dāng)然為了匹配精度更高,我們也可以對(duì)多個(gè)字段進(jìn)行驗(yàn)證,這里就只寫出大體輪廓吧.
#include <stdio.h>
#include <Windows.h>
#include <ImageHlp.h>
#pragma comment(lib,"Imagehlp.lib")
// 讀取PE結(jié)構(gòu)的封裝
HANDLE OpenPeFile(LPTSTR FileName)
{
HANDLE hFile, hMapFile, lpMapAddress = NULL;
DWORD dwFileSize = 0;
// CreateFile 既可以創(chuàng)建文件,也可以打開文件,這里則是打開文件的含義
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);
// 創(chuàng)建文件的內(nèi)存映像
// 此方法非常靈活,其不需要將程序完全讀入內(nèi)存中,節(jié)約空間。
hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
if (hMapFile == NULL)
return 0;
// 讀取映射中的內(nèi)存并返回一個(gè)句柄
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;
// 將映射文件轉(zhuǎn)為DOS結(jié)構(gòu),并判斷開頭是否為MZ
pDosHead = (PIMAGE_DOS_HEADER)ImageBase;
if (IMAGE_DOS_SIGNATURE != pDosHead->e_magic)
return FALSE;
if (Is64 == TRUE)
{
// 根據(jù) 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)
{
// 根據(jù) 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;
}
// 掃描特征碼,對(duì)比
void GetPeSignature(LPCWSTR FilePath)
{
typedef struct _SIGN
{
char FileName[64]; // 存儲(chǔ)文件名或特征描述
LONG FileOffset; // 存儲(chǔ)檢測(cè)文件偏移地址
BYTE VirusSign[32 + 1]; // 存儲(chǔ)特征碼大小32,其中的1是結(jié)束符.
}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);
// 我們有兩段待檢測(cè)特征,這里循環(huán)兩次從零開始
for (int x = 0; x <= 2; x++)
{
// 將待檢測(cè)程序的文件指針指向特征碼的偏移位置
SetFilePointer(hFile, Sign[x].FileOffset, NULL, FILE_BEGIN);
// 讀取目標(biāo)程序指定位置的特征碼到內(nèi)存中
ReadFile(hFile, buffer, sizeof(buffer), &dwNum, NULL);
// 對(duì)比內(nèi)存中兩個(gè)特征碼是否相等
if (memcmp(Sign[x].VirusSign, buffer, 32) == 0)
{
printf("檢測(cè)結(jié)果: %s \n", Sign[x].FileName);
}
}
CloseHandle(hFile);
}
int main(int argc, char * argv[])
{
GetPeSignature(L"c://lyshark.exe");
system("pause");
return 0;
}
你需要自己提取不同編譯器的特征字段,然后按照我寫好的格式進(jìn)行增加,例如我是用vs2013編譯的,那么檢測(cè)結(jié)果就可能會(huì)是vs2013,特征碼的提取應(yīng)盡量保證一致性。

文章出處:https://www.cnblogs.com/lyshark
以上就是C++ 實(shí)現(xiàn)PE文件特征碼識(shí)別的步驟的詳細(xì)內(nèi)容,更多關(guān)于C++ PE文件特征碼識(shí)別的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- C++ ReSharper2021激活碼永久有效
- C++中實(shí)現(xiàn)OpenCV圖像分割與分水嶺算法
- c++ 基于opencv 識(shí)別、定位二維碼
- C++ opencv實(shí)現(xiàn)車道線識(shí)別
- 基于OpenCV和C++ 實(shí)現(xiàn)圖片旋轉(zhuǎn)
- c++ 形狀類Shape(派生出圓類Circle和矩形類Rectangle)
- OpenCV實(shí)現(xiàn)車牌字符分割(C++)
- OpenCV實(shí)現(xiàn)車牌定位(C++)
- C++中typeid實(shí)現(xiàn)原理詳解
- Visual Studio Code運(yùn)行C++代碼時(shí)顯示CLOCKS_PER_SEC未定義的問題及解決方法
相關(guān)文章
詳解C++類的成員函數(shù)做友元產(chǎn)生的循環(huán)依賴問題
這篇文章主要為大家詳細(xì)介紹了C++類的成員函數(shù)做友元產(chǎn)生的循環(huán)依賴問題,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03
探討register關(guān)鍵字在c語言和c++中的差異
建議不要用register關(guān)鍵字定義全局變量,因?yàn)槿肿兞康纳芷谑菑膱?zhí)行程序開始,一直到程序結(jié)束才會(huì)終止,而register變量可能會(huì)存放在cpu的寄存器中,如果在程序的整個(gè)生命周期內(nèi)都占用著寄存器的話,這是個(gè)相當(dāng)不好的舉措2013-10-10
C++實(shí)現(xiàn)LeetCode(170.兩數(shù)之和之三 - 數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì))
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(170.兩數(shù)之和之三 - 數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)),本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
一文詳解C++中動(dòng)態(tài)內(nèi)存管理
這篇文章主要介紹了一文詳解C++中動(dòng)態(tài)內(nèi)存管理,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)孩子沒需要的朋友可以才可以參考一下2022-07-07
OpenCV實(shí)戰(zhàn)之基于Hu矩實(shí)現(xiàn)輪廓匹配
這篇文章主要介紹了利用C++ OpenCV實(shí)現(xiàn)基于Hu矩的輪廓匹配,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)OpenCV有一定的幫助,感興趣的可以學(xué)習(xí)一下2022-01-01

