C++ 使用CRC32檢測內存映像完整性的實現步驟
僅對.text代碼段進行校驗:
通常程序中至少包括了代碼段,數據段,而數據段中所存儲的數據是經常會發(fā)生變動的,例如我們的全局變量,靜態(tài)變量等都會默認存儲在數據段,而代碼段則不會發(fā)生變化,我們在檢驗時只需要注重.text內存段中的數據完整性即可,針對內存的校驗同樣可以抵御調試器的CC斷點,該斷點原理就是在下端處寫入int3指令,同樣可以檢測得到。
校驗思路如下
1.首先從內存得到PE的代碼節(jié)的RVA和節(jié)大小
2.根據得到的RVA和節(jié)大小計算出crc32或是RC4值
3.讀取自身保存的原始CRC32值,與校驗結果進行比較
1.先來實現第一步,讀取內存映像的起始地址與大小,我們可以這樣做。
#include <stdio.h> #include <windows.h> int main(int argc, char *argv[]) { PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNtHeader = NULL; PIMAGE_SECTION_HEADER pSecHeader = NULL; DWORD ImageBase; // 獲取基地址 ImageBase = (DWORD)GetModuleHandle(NULL); // 定位到PE頭結構 pDosHeader = (PIMAGE_DOS_HEADER)ImageBase; // 定位到NT頭 pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew); // 定位第一個區(qū)塊地址,因為默認的話第一個就是.text節(jié) pSecHeader = IMAGE_FIRST_SECTION(pNtHeader); // 取出節(jié)內偏移與節(jié)表長度 DWORD va_base = ImageBase + pSecHeader->VirtualAddress; // 定位代碼節(jié)va基地址 DWORD sec_len = pSecHeader->Misc.VirtualSize; // 獲取代碼節(jié)長度 printf("鏡像基址(.text): %x --> 鏡像大小: %x \n", va_base, sec_len); system("pause"); return 0; }
2.第二部就是計算校驗和,然后計算該節(jié)的CRC32值,并存入全局變量,也就是程序打開后自動初始化計算一次內存crc32值并放入全局變量中,然后開一個線程,每三秒檢測一次內存變化,如果變化則終止執(zhí)行或彈窗提示,你也可以提前計算處校驗和并寫入PE空缺位置。
#include <stdio.h> #include <windows.h> DWORD CRC32(BYTE* ptr, DWORD Size) { DWORD crcTable[256], crcTmp1; // 動態(tài)生成CRC-32表 for (int i = 0; i<256; i++) { crcTmp1 = i; for (int j = 8; j>0; j--) { if (crcTmp1 & 1) crcTmp1 = (crcTmp1 >> 1) ^ 0xEDB88320L; else crcTmp1 >>= 1; } crcTable[i] = crcTmp1; } // 計算CRC32值 DWORD crcTmp2 = 0xFFFFFFFF; while (Size--) { crcTmp2 = ((crcTmp2 >> 8) & 0x00FFFFFF) ^ crcTable[(crcTmp2 ^ (*ptr)) & 0xFF]; ptr++; } return (crcTmp2 ^ 0xFFFFFFFF); } // 檢查內存中CRC32特征值 DWORD CheckMemory() { PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNtHeader = NULL; PIMAGE_SECTION_HEADER pSecHeader = NULL; DWORD ImageBase; // 獲取基地址 ImageBase = (DWORD)GetModuleHandle(NULL); // 定位到PE頭結構 pDosHeader = (PIMAGE_DOS_HEADER)ImageBase; pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew); // 定位第一個區(qū)塊地址,因為默認的話第一個就是.text節(jié) pSecHeader = IMAGE_FIRST_SECTION(pNtHeader); DWORD va_base = ImageBase + pSecHeader->VirtualAddress; // 定位代碼節(jié)va基地址 DWORD sec_len = pSecHeader->Misc.VirtualSize; // 獲取代碼節(jié)長度 //printf("鏡像基址(.text): %x --> 鏡像大小: %x \n", va_base, sec_len); DWORD CheckCRC32 = CRC32((BYTE*)(va_base), sec_len); // printf(".text節(jié)CRC32 = %x \n", CheckCRC32); return CheckCRC32; } int main(int argc,char *argv[]) { // 用于保存初始化時 .text 節(jié)中的CRC32值 DWORD OriginalCRC32 = 0; // 初始化時,給全局變量賦值,記錄下初始的CRC32值 OriginalCRC32 = CheckMemory(); while (1) { Sleep(3000); DWORD NewCRC32 = CheckMemory(); if (OriginalCRC32 == NewCRC32) printf("程序沒有被打補丁. \n"); else printf("程序被打補丁 \n"); } system("pause"); return 0; }
上方代碼是保護了整個程序,在實際應用中,為了提高效率,有時我們只需要保護其中一個片段代碼就好,這樣可以提高效率,所有我們對上面代碼稍作修改即可實現針對特定片段的內存校驗。
#include <stdio.h> #include <windows.h> DWORD CRC32(BYTE* ptr, DWORD Size) { DWORD crcTable[256], crcTmp1; // 動態(tài)生成CRC-32表 for (int i = 0; i<256; i++) { crcTmp1 = i; for (int j = 8; j>0; j--) { if (crcTmp1 & 1) crcTmp1 = (crcTmp1 >> 1) ^ 0xEDB88320L; else crcTmp1 >>= 1; } crcTable[i] = crcTmp1; } // 計算CRC32值 DWORD crcTmp2 = 0xFFFFFFFF; while (Size--) { crcTmp2 = ((crcTmp2 >> 8) & 0x00FFFFFF) ^ crcTable[(crcTmp2 ^ (*ptr)) & 0xFF]; ptr++; } return (crcTmp2 ^ 0xFFFFFFFF); } // 檢查內存中CRC32特征值 DWORD CheckMemory(DWORD va_base, DWORD sec_len) { PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNtHeader = NULL; PIMAGE_SECTION_HEADER pSecHeader = NULL; DWORD ImageBase; ImageBase = (DWORD)GetModuleHandle(NULL); pDosHeader = (PIMAGE_DOS_HEADER)ImageBase; pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew); // 以下三條語句可用于確定位置 // pSecHeader = IMAGE_FIRST_SECTION(pNtHeader); // DWORD va_base1 = ImageBase + pSecHeader->VirtualAddress; // 定位代碼節(jié)va基地址 // DWORD sec_len1 = pSecHeader->Misc.VirtualSize; // 獲取代碼節(jié)長度 // printf("鏡像基址(.text): %x --> 鏡像大小: %x \n", va_base1, sec_len1); DWORD CheckCRC32 = CRC32((BYTE*)(va_base), sec_len); return CheckCRC32; } int main(int argc, char *argv[]) { // 用于保存初始化時 .text 節(jié)中的CRC32值 DWORD OriginalCRC32 = 0; DWORD begin_addr, end_addr, size; // 獲取到兩個位置的偏移地址 __asm mov begin_addr, offset begin; __asm mov end_addr, offset end; // 計算出 兩者內存差值 size = end_addr - begin_addr; // 校驗指定內存位置 OriginalCRC32 = CheckMemory(begin_addr, size); while (1) { begin: // 標記為需要保護的區(qū)域 printf("hello lyshark \n"); printf("hello lyshark \n"); printf("hello lyshark \n"); end: // 保護區(qū)域聲明結束 if (OriginalCRC32 == CheckMemory(begin_addr, size)) printf("此區(qū)域沒有被破解 \n"); else printf("此區(qū)域已被修改\n"); Sleep(3000); } system("pause"); return 0; }
通過使用磁盤校驗結合內存校驗兩種方式綜合保護,可以極大的提高軟件的安全性,繞過方式則是找到哪兒跟全局變量將其修正為正確的值即可,同樣的也可以更暴力一些直接將判斷條件改掉均可。
文章出處:https://www.cnblogs.com/lyshark
以上就是C++ 使用CRC32檢測內存映像完整性的實現步驟的詳細內容,更多關于C++用CRC32檢測內存映像完整性的資料請關注腳本之家其它相關文章!