本篇用作技术性研究,一个简单的算法。
设想一个问题,一个程序如果它没有漏洞如何hook(攻击)它呢?答案很简单,人为制造漏洞。比如常用的指令性的更改(je指令更改为jne等属此类)。但是这样就会造成了程序(exe或者dll)里二进制的不一致性,防范这种hook方法,就是检验程序二进制的完整性,CRC算法就是这种校验的体现。
我们可以把程序进行如下CRC算法Result
DWORD CRC32(BYTE* ptr, DWORD Size)
{
DWORD crcTable[256], crcTmp1;
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;
}
DWORD crcTmp2 = 0xFFFFFFFF;
while (Size--)
{
crcTmp2 = ((crcTmp2 >> 8) & 0x00FFFFFF) ^ crcTable[(crcTmp2 ^ (*ptr)) & 0xFF];
ptr++;
}
return (crcTmp2 ^ 0xFFFFFFFF);
}
ptr参数是二进制文件需要crc校验的起始地址,Size则是校验的二进制字节数。如果想要校验一个exe或者DLL的Result数值,那么我们可以如下:
GetOpenFileName(&ofn);
hFile = CreateFile(
szFileName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
fileSize = GetFileSize(hFile, &szTemp);
pDosHeader = (PIMAGE_DOS_HEADER)pBuffer;
pNtHeader = (PIMAGE_NT_HEADERS64)((DWORD)pDosHeader + pDosHeader->e_lfanew);
fileSize = fileSize - DWORD(pDosHeader->e_lfanew);
szCRC32 = CRC32((BYTE*)(pBuffer + pDosHeader->e_lfanew), fileSize);
DWORD Writeadd = DWORD(pDosHeader->e_lfanew - 4);
SetFilePointer(hFile, Writeadd, NULL, FILE_BEGIN);
if (!WriteFile(hFile, &szCRC32, 4, &szTemp, NULL))
{
CloseHandle(hFile);
}
当程序开始运行的时候,我们可以校验ptr到size中间的crc result,进行一个判断:
pBuffer = new TCHAR [fileSize];
ReadFile(hFile,pBuffer, fileSize, &NumberOfBytesRW, NULL);
CloseHandle(hFile);
pDosHeader=(PIMAGE_DOS_HEADER)pBuffer;
pNtHeader=(PIMAGE_NT_HEADERS32)((DWORD)pDosHeader+pDosHeader->e_lfanew);
fileSize=fileSize-DWORD(pDosHeader->e_lfanew);
if (CRC32((BYTE*)(pBuffer+pDosHeader->e_lfanew),fileSize) == OriginalCRC32 )
return TRUE;
else
return FALSE;
如果返回false则表示,exe或者dll更改了,此时我们就可以退出程序,不作任何运行,以防止被认为破解。
以上是校验的过程,主要的原理即把二进制进行计算,然后得出的结果进行保存,运行的时候再次进行计算,比较两次计算结果。代码取自《加密与解密》一书。整体来说,这种保护性较弱,但可以作为一种手段升级下。