CRC校驗技術是用於檢測數據傳輸或存儲過程中是否出現了錯誤的一種方法,校驗算法可以通過計算應用與數據的循環冗餘校驗(CRC)檢驗值來檢測任何數據損壞。通過運用本校驗技術我們可以實現對特定內存區域以及磁盤文件進行完整性檢測,並以此來判定特定程序內存是否發生了變化,如果發生變化則拒絕執行,通過此種方法來保護內存或磁盤文件不會被非法篡改。總之,內存和磁盤中的校驗技術都是用於確保數據和程序的完整性和安全性的重要技術。
磁盤CRC(循環冗餘校驗)用於檢測磁盤數據的完整性,一般而言某些木馬專殺工具同樣會用到磁盤CRC特徵校驗技術,該技術的實現原理與內存驗證原理完全一致,針對磁盤的驗證同樣很簡單,但此處我們需要將計算到的CRC32
值存儲到PE文件自身中,通常我們可以存儲到PE文件的前一個DWORD
的位置上,程序運行後對比這個值,來判斷程序是否被打過補丁,如果打過直接結束掉。
// 檢查磁盤完整性
BOOL CalculateDiskCRC32()
{
char szFileName[MAX_PATH] = { 0 };
char *pBuffer;
DWORD pNumberOfBytesRead;
int FileSize = 0;
// 獲取自身文件,並打開文件
GetModuleFileName(0, szFileName, MAX_PATH);
HANDLE hFile = CreateFile(szFileName, GENERIC_READ, 1, 0, 3, FILE_ATTRIBUTE_NORMAL, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return TRUE;
}
// 取文件長度
FileSize = GetFileSize(hFile, 0);
pBuffer = new char[FileSize];
// 讀取文件到內存
ReadFile(hFile, pBuffer, FileSize, &pNumberOfBytesRead, 0);
CloseHandle(hFile);
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS32 pNtHeader = NULL;
pDosHeader = (PIMAGE_DOS_HEADER)pBuffer;
// 獲取到NT頭
pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew);
// 定位到PE文件頭前4字節處
DWORD OriginalCRC32 = *(DWORD *)((DWORD)pNtHeader - 4);
printf("[*] 讀出節表值 = %x \n", OriginalCRC32);
// 我們只需要計算PE結構的CRC32值,不需要計算DOS頭
FileSize = FileSize - DWORD(pDosHeader->e_lfanew);
DWORD CheckCRC32 = CRC32((BYTE*)(pBuffer + pDosHeader->e_lfanew), FileSize);
printf("[+] 計算CRC32 = %x \n", CheckCRC32);
if (CheckCRC32 == OriginalCRC32)
{
return FALSE;
}
else
{
return TRUE;
}
return TRUE;
}
int main(int argc, char* argv[])
{
BOOL ref = CalculateDiskCRC32();
if (ref == TRUE)
{
printf("[-] 程序已被修改 \n");
}
else
{
printf("[+] 程序正常 \n");
}
system("pause");
return 0;
}
首先讀者運行上述程序,則程序會輸出當前的CRC32值be63ac8b
我們記下這個HASH值,如下圖所示;
並將此值替換到如下圖中的黃色位置,當程序運行後會讀取該區域內的數據,並與動態計算的CRC32值進行計算,最終判斷是否被修改,如下圖所示;
通過CRC32數據對比並遍歷磁盤文件,我們可以實現一個簡單的特徵定位查殺程序,用於專門定位某些特殊的程序,如下是修改後的代碼片段;
// 計算文件CRC過程
BOOL CalcCRC32(char *FilePath)
{
// 打開文件
HANDLE hFile = CreateFile(FilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
return FALSE;
}
// 獲取文件大小
DWORD dwSize = GetFileSize(hFile, NULL);
if (dwSize == 0xFFFFFFFF)
{
return FALSE;
}
// 分配內存
BYTE *pFile = (BYTE*)malloc(dwSize);
if (pFile == NULL)
{
return FALSE;
}
// 讀取內存
DWORD dwNum = 0;
ReadFile(hFile, pFile, dwSize, &dwNum, NULL);
// 計算CRC32
DWORD dwCRC32 = CRC32(pFile, dwSize);
if (pFile != NULL)
{
free(pFile);
pFile = NULL;
}
CloseHandle(hFile);
return dwCRC32;
}
int main(int argc, char* argv[])
{
WIN32_FIND_DATA stFindFile;
HANDLE hFindFile;
char *szFilter = "*.exe"; // 篩選所有的.exe結尾的文件
char szFindFile[MAX_PATH]; // 保存欲檢測程序的路徑
char szSearch[MAX_PATH]; // 保存完整的篩選路徑
int ret = 0; // 搜索狀態返回值
lstrcpy(szFindFile, "D:\\"); // 搜索D盤目錄下的所有exe結尾的文件
lstrcpy(szSearch, "D:\\");
lstrcat(szSearch, szFilter);
DWORD dwTmpCRC32;
hFindFile = FindFirstFile(szSearch, &stFindFile);
if (hFindFile != INVALID_HANDLE_VALUE)
{
do
{
lstrcat(szFindFile, stFindFile.cFileName);
dwTmpCRC32 = CalcCRC32(szFindFile);
// 比較判斷
if (dwTmpCRC32 == 0xbe63ac8b)
{
printf("[*] CRC32 = %x 發現病毒 %s \n", dwTmpCRC32, szFindFile);
}
else
{
printf("[-] CRC32 = %x 正常程序 %s \n", dwTmpCRC32, szFindFile);
}
// 刪除程序名稱只保留"C:\"
szFindFile[3] = '\0';
ret = FindNextFile(hFindFile, &stFindFile);
} while (ret != 0);
}
FindClose(hFindFile);
system("pause");
return 0;
}
運行程序輸出效果如下圖所示;
本文作者: 王瑞
本文鏈接: https://www.lyshark.com/post/7a9a55f0.html
版權聲明: 本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!