CRC32:
CRC的全称是循环冗余校验,作用是为了检测数据的完整性。
CRC32的检测原理:
程序被编译后,代码段是固定的,因为已经被写死了。
我们在调试程序的时候,打断点或者修改代码都会影响CRC32的值,这个时候只需要检测CRC32的某一时刻值和最初的CRC32值是否一致就可以判断代码是否被修改了。
如果装了好压这个软件可以直接通过右键查看到CRC32的值:
这里我找了一个简单的CRC32的代码来测试一下:
利用CRC32检测自己程序是否被修改:
通过对代码段进行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检测
因为CRC的原理是要扫描你的整个代码段,那么可以通过CE工具来通过代码段的地址,查看是谁访问了该代码段,由此来找到CRC检测的函数,从而进行魔改通过CRC检测。要想逆一个东西,知道这个东西的原理再来逆会非常方便。
小结