反调试——7——CRC检测

CRC32:

CRC的全称是循环冗余校验,作用是为了检测数据的完整性。

CRC32的检测原理:

程序被编译后,代码段是固定的,因为已经被写死了。

我们在调试程序的时候,打断点或者修改代码都会影响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检测自己程序是否被修改:

通过对代码段进行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检测。要想逆一个东西,知道这个东西的原理再来逆会非常方便。

小结

CRC有点类似于hash函数,就是把一坨东西来计算一个值出来,由于一个程序写好了之后代码段是不会改变的,所以我们可以把它用来计算代码段的内容,然后再程序里面时刻计算这个值有没有改变来判断是否被修改了代码。

  完整代码:反调试--CRC检测 - Sna1lGo - 博客园 (cnblogs.com)