校驗和(Checksum)
PE的可選映像頭(IMAGE_OPTION_HEADER)里面,有一個Checksum字段,是該文件的校驗和,一般EXE文件可以使0,但一些重要的和系統DLL及驅動文件必須有一個校驗和.
Windows 提供了一個API函數MapFileAndCheckSum 測試文件的Checksum,它位於IMAGEHLP.DLL鏈接庫里,其原型:
ULONG MapFileAndCheckSum
{
LPSTR FileName, // 文件名
LPDWORD HeaderSum, // 指向PE文件頭的CheckSum
LPDWORD new_checksum // 指向新計算出的Checksum
}
程序一旦運行后,new_checksum 地址處將放當前的文件的校驗和,old_checksum地址指向PE文件的checksum字段
安全的方法是將此值放在注冊表里,需要時比較.
內存映像校驗
磁盤文件完整性校驗可以抵抗解密者直接修改文件,但對內存補丁卻沒有效果,必須對內存關鍵的代碼進行校驗.
1 對整個代碼進行校驗
每個程序至少有一個代碼區塊和數據區塊,數據區塊屬性可讀寫,程序運行時全局變量通常會放在這里,這些數據會動態變化,因此校驗這部分是沒什么意義,而代碼段只讀,存放的是程序代碼,在程序中數據數不會變的,因此用這部分進行內存校驗是可行的.
具體實現方法:
(1) 從內存中映像中得到PE相關數據,如代碼塊的RVA和內存大小
(2) 根據得到代碼區塊的RVA值和內存大小,計算出內存數據的CRC-32值
(3) 讀取自身文件先前存儲的CRC-32值(PE文件頭前一個字段),這個值是通過軟件寫進去的.
(4) 比較兩個CRC-32值.
這樣比較內存的代碼段校驗,只要內存的數據被修改,就能發現。
BOOL CodeSectionCRC32()
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_SECTION_HEADER pSection = NULL;
DWORD ImageBase,OriginalCRC32;
ImageBase = (DWORD)GetModuleHandle(NULL); // 取基址
pDosHeader = (PIMAGE_DOS_HEADER)ImageBase;
pNtHeader = (PIMAGE_NT_HEADER32)((DWORD)pDosHeader + pDosHeader -> e_lfanew);
// 定位到PE文件頭前4個字節值,並讀取存儲在這里的CRC -32值
OriginalCRC32 = *((DWORD*)(DWORD)pNtHeader - 4);
pSecHeader = IMAGE_FIRST_SECTION(pNtHeader); // 得到第一個區塊的地址
//假設第一個區塊就是代碼區塊
if(OriginalCRC32 == CRC32((BYTE*)ImageBase + pSecHeader -> VirtualAddress)
// 為了方便加殼
// 上一句也可為if(OriginalCRC32 == CRC32((BYTE *)0x401000, 0x36AE)
return TRUE;
else
return FALSE;
}
2 檢驗代碼片段
在實際過程中,有時只需對一小段代碼進行內存校驗,以防止調試工具INT3斷點
下面是一段匯編代碼
翻譯成VC
DWORD address1, address2,size;
_asm Mov address1, offset begindecryt;
_asm Mov address2,offset enddecrypt;
begindecryt : // 標記代碼的起始地址
MessageBox(NULL, _T(“Hello World”), _T(“OK”), MB_ICONEXCLAMATION);
enddecrypt: // 標記代碼的結束地址
size = address2 – address1;
if(CRC32(BYTE*)address1, size) == 0x78E888AE)
return TRUE;
else
return FALSE;
使 .text 區塊可寫
在Win32 平台上,文件編譯后,.text 區塊的屬性石只讀的,但是要是寫,必須通過PE工具修改.text區塊的屬性為E0000020h 表示可寫 可讀 可執行
void Docrypt(DWORD* pData, DWORD Size, DWRD value)
{
// 首先要做的是改變着一塊虛擬內存的內存保護狀態,以便可以自由存取代碼
MEMORY_BASIC_INFORMATION mbi_thunk;
// 查詢頁信息
VirtualQuery(pData, &mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION));
// 改寫頁保護屬性為讀寫
VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, PAGE_READWRITE, &mbi_thunk.Protect);
size = Size/0x4; // 對數據共需要異或的次數
While(Size --)
{
*pData = (*pData) ^ value;
pData ++;
}
// 恢復也的元保護屬性
DWORD dwOldProtect
VirtualProtect(mbi_thunk.BaseAddress,
mbi_thunk.RegionSize,
mbi_thunk.Protect,
&dwOldProtect);
}