CRC32:
CRC的全称是循环冗余校验,作用是为了检测数据的完整性。
程序被编译后,代码段是固定的,因为已经被写死了。
我们在调试程序的时候,打断点或者修改代码都会影响CRC32的值,这个时候只需要检测CRC32的某一时刻值和最初的CRC32值是否一致就可以判断代码是否被修改了。
如果装了好压这个软件可以直接通过右键查看到CRC32的值:
这里我找了一个简单的CRC32的代码来测试一下:
#include<iostream> uint32_t crc32_table[256]; int make_crc32_table() { uint32_t c; int i = 0; int bit = 0; for (i = 0; i < 256; i++) { c = (uint32_t)i; for (bit = 0; bit < 8; bit++) { if (c & 1) { c = (c >> 1) ^ (0xEDB88320); } else { c = c >> 1; } } crc32_table[i] = c; } return 1; } uint32_t make_crc(unsigned char* string, uint32_t size) { uint32_t crc= 0xFFFFFFFF; make_crc32_table(); while (size--) crc = (crc >> 8) ^ (crc32_table[(crc ^ *string++) & 0xff]); return crc; }
通过对代码段进行CRC32判断来处理。首先要拿到代码段,通过Windows 的PE文件的结构体 Dos头->NT头->(采用VS里的结构体)拿到区段头->然后通过区段头得到区段的信息。
char *buffer=(char*)GetModuleHandleA(0);//参数为0就获取当前进程的句柄 PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)buffer; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + buffer); PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader); for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections - 1; i++) { }
代码段顾名思义,就是执行代码的东西。在PE文件中的区段头里有一个字段可以判断属性:
typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; union { DWORD PhysicalAddress; DWORD VirtualSize; } Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics;//这个 } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER
Characteristics这个字段标识了是否是可执行。在CFF里面查看:
这里通过我的测试,在修改可读可写的属性时会修改最高字段的值,如果有就加2,然后别的就修改别的值,所以这里我们判断首位是不是6就可以判断是不是代码段了。
void Crc32Test() { char *buffer=(char*)GetModuleHandleA(0);//参数为0就获取当前进程的句柄 PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)buffer; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + buffer); PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader); for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections - 1; i++) { if (pSectionHeader->Characteristics / 0x10000000 == 6) { cout << pSectionHeader->Name << endl; auto CrcNum = make_crc((unsigned char*)(pSectionHeader->VirtualAddress + buffer), pSectionHeader->Misc.VirtualSize); cout << CrcNum << endl; } pSectionHeader++; } }
然后通过ollydbg随便打一个int3断点(因为int3断点会添加一个CC在里面所以肯定的改变了代码段的内容的)再运行来看CRC的值是否改变:
这里果然是改变了。
CRC是一个演算东西的内容,类似于一些hash运行把,就是把一坨东西来计算一个值出来,由于一个程序写好了之后代码段是不会改变的,所以我们可以把它用来计算代码段的内容,然后再程序里面时刻计算这个值有没有改变来判断是否被修改了代码。
完整代码:反调试--CRC检测 - Sna1lGo - 博客园 (cnblogs.com)