[Detection] CRC check memory patch

background

Usually the program includes at least code segment and data segment, and the data stored in the data segment is often changed. For example, our global variables, static variables, etc. will be stored in the data segment by default, while the code segment will not occur. Change, we only need .textto pay attention to the integrity of the data in the memory segment when verifying. The verification of the memory can also resist the CC breakpoint of the debugger. The principle of the breakpoint is to write an int3 instruction at the lower end, which can also be detected. get.
Insert picture description here

The verification ideas are as follows:
1. First obtain the RVA and section size of the code section of the PE from the memory
2. Calculate the crc32 or RC4 value according to the obtained RVA and section size
3. Read the original CRC32 value saved by itself, and the verification result Compare

Code:

#include <stdio.h>
#include <windows.h>

DWORD CRC32(BYTE* ptr, DWORD Size)
{
    
    
	DWORD crcTable[256], crcTmp1;

	// 动态生成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);

	// 定位第一个区块地址,因为默认的话第一个就是.text节
	pSecHeader = IMAGE_FIRST_SECTION(pNtHeader);
	DWORD va_base = ImageBase + pSecHeader->VirtualAddress;   // 定位代码节va基地址
	DWORD sec_len = pSecHeader->Misc.VirtualSize;             // 获取代码节长度
	//printf("镜像基址(.text): %x --> 镜像大小: %x \n", va_base, sec_len);

	DWORD CheckCRC32 = CRC32((BYTE*)(va_base), sec_len);
	// printf(".text节CRC32 = %x \n", CheckCRC32);
	return CheckCRC32;
}

int main(int argc,char *argv[])
{
    
    
	// 用于保存初始化时 .text 节中的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;
}

Insert picture description here

The above code protects the entire program. In actual applications, in order to improve efficiency, sometimes we only need to protect one of the fragments of code, so that efficiency can be improved. All we can modify the above code to achieve memory for specific fragments. check.

#include <stdio.h>
#include <windows.h>

DWORD CRC32(BYTE* ptr, DWORD Size)
{
    
    
	DWORD crcTable[256], crcTmp1;

	// 动态生成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;   // 定位代码节va基地址
	// DWORD sec_len1 = pSecHeader->Misc.VirtualSize;             // 获取代码节长度
	// 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 节中的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: // 标记为需要保护的区域
		printf("hello lyshark \n");
		printf("hello lyshark \n");
		printf("hello lyshark \n");
	end:   // 保护区域声明结束

		if (OriginalCRC32 == CheckMemory(begin_addr, size))
			printf("此区域没有被破解 \n");
		else
			printf("此区域已被修改\n");

		Sleep(3000);
	}
	system("pause");
	return 0;
}

Insert picture description here

By using disk verification combined with memory verification, the security of the software can be greatly improved. The bypass method is to find out where and global variables to correct them to the correct value. The same can also be changed. Some violence can directly change the judgment condition.

Guess you like

Origin blog.csdn.net/Simon798/article/details/110237292