一直听说CRC可以校验代码是否被修改,最近研究了一下。
CRC的优点是代码量小,容易理解,在动态校验上应用比力广泛。
代码量确实是小,没弊端,但是看网上的资料我理解起来,还真有点费劲,下面详细讲一下。
1.CRC算法原理
数据发送过程:
多项式转化为二进制数,这个2进制数作为除数。
CRC校验码的位数=上面计算除数的位数-1
校验码的位数是多少,就把必要校验的数据左移多少位,得到的就是被除数
被除数 模二除 除数 = 商+余数
余数就是我们必要的CRC校验码
数据接收过程:0
多项式转化为二进制数,这个2进制数作为除数。
接收到的数据和CRC码拼接起来,作为被除数
除数确定了,被除数也确定了,接下来再次利用“模2除法”校验
结果为0,则接受的数据正确,结果不为0接收的数据不正确
还是不懂,很好,再翻译一遍
多项式就是一个指定的数值,用我们必要校验的数据模二除这个多项式的数值,得到的余数就是CRC校验码。
这样好理解很多了吧,模二除法想相识的话可以网上搜一下,大家动动手,我就懒一下了。2.CRC算法实现
首老师成CRC校验码,我们这里按字节计算CRC,不考虑网上的按位计算(都64位体系了,不差一张表的内存)
先写个函数天生一张字节CRC校验码的表,由于每个字节从00-FF有256个组合,所以每个字节有256种差别的校验码。
[C] 纯文本查看 复制代码VOID GenerateByteCrc(){ unsigned int crc = 0; int i=0,j=0; for (j = 0; j < 256; j++) //一个byte有256种差别的值,计算所有可能值的crc码 { crc = j; for (i = 0; i < 8; i = i++) //这个for循环天生crc码 { if (crc & 1) crc = (crc >> 1) ^ 0xEDB88320; //根据多项式天生的除数 else crc >>= 1; } crc_byte[j] = crc; //对应整型数值的crc码保存在该数组中 } crc_byte_being = 1; //通过这个值判断是否天生了这个表}
有了校验表,就可以对数据进行校验,再写一个校验的函数:
[C] 纯文本查看 复制代码DWORD GenerateDataCrc(char* data, int len) //data是校验数据的起始地址,len是校验数据的长度{ unsigned int crc = 0xFFFFFFFF; unsigned int i; for (i = 0; i < len; i++) { crc = crc_byte[(crc ^ data) & 0xFF] ^ (crc >> 8); } crc = ~crc; printf("当前代码段数据CRC校验码为0x%x ", crc); return crc;}
起始看懂原理,再看这份实现的代码也有点晦涩,代码是鉴戒加密与解密书中简化的代码,假如纯按照原理来实现的话,代码还有有点繁琐的。
至此功能基本实现,写一个程序利用CRC校验自身是否被修改。
[C] 纯文本查看 复制代码int main(){ char* data=0x00401000; //这里是该程序代码段的起始地址 int len = 0x0e6c; //这里是该程序代码段的长度 if (!crc_byte_being) GenerateByteCrc(); DWORD OriginalCrcCode = GenerateDataCrc(data, len); while (1) { DWORD CurrentCrcCode = GenerateDataCrc(data, len); if (OriginalCrcCode != CurrentCrcCode) { printf("\n------程序已经被修改,准备退出-----\n"); getchar(); return 0; } printf("程序正常运行\n"); Sleep(2000); } return 0;}
看一下代码段的虚拟地址偏移和大小,虚拟地址偏移加上映像基址就是data的值,大小就是len的值。
3.过掉CRC检测
利用OD打开程序,F9跑起来:
在OD中看一下这个地址,CE也可以看,不外用OD更直观。
这段代码就是我们计算CRC的代码,esi中保存了末了计算出的CRC码,传给eax作为函数的返回值。
很快遇到一个跳转, 这个就很明显了,nop掉就可以了,当然绕过的方法还有很多,就不一一列举了。
4.CRC校验改进
颠末上述过程,发现CRC检测程序很容易被发现,被发现就会被干掉。
假如创建一个线程专门用来CRC检测呢,通过内存访问断点还是会被定位到检测代码。
假如双层CRC嵌套检测呢,两处代码还是会访问被检测的地址,所以还是会被定位。
假如通过另一个进程来检测被保护进程呢?果断写份代码试一试。
[C] 纯文本查看 复制代码int main(){SIZE_T* Real_len;char* process_name = "CRC-verify.exe";char* buff; VirtualAlloc(&buff, 0x0e6c, MEM_RESERVE, PAGE_READWRITE);int Pid = ProcesstoPid(process_name);printf("%d\n", Pid);HANDLE hprocess = OpenProcess(PROCESS_ALL_ACCESS, NULL, (DWORD)Pid);if (!hprocess) //进程被od打开时,这里OpenProcess会返回0{printf("进程打开失败");return 0;}ReadProcessMemory(hprocess, 0x00401000, &buff, 0x0e6c, &Real_len);if (!crc_byte_being)GenerateByteCrc();DWORD OriginalCrcCode = GenerateDataCrc(&buff, 0x0e6c); while (1){ReadProcessMemory(hprocess, 0x00401000, &buff, 0x0e6c, &Real_len);DWORD CurrentCrcCode = GenerateDataCrc(&buff, 0x0e6c);if (OriginalCrcCode != CurrentCrcCode){printf("\n------程序已经被修改,准备退出-----\n");return 0;}printf("程序正常运行\n");Sleep(2000);}CloseHandle(hprocess);return 0;}
先运行前面校验自身的程序,再运行后面这个跨进程校验的程序,然后利用CE附加被保护的程序,再次查找一下是什么访问了内存地址。
这个程序也发现代码段被修改了,阐明思路没问题。
这样我们可以采用自身CRC校验全部代码,通过保护进程来校验CRC校验的代码的方案来达到检测的目标。
当然这种方式通过遍历进程句柄表查找哪些进程打开了被保护进程,也可以过掉,但是相比于CRC自校验来说,门槛一下就提高了。
本日就写到这里吧,新人第一次发帖,有不足的地方还望大佬们指正。
参考资料:
《加密与解密第四版》 段刚
网络游戏安全之实战某游戏厂商FPS游戏CRC检测的对抗与防护 https://bbs.pediy.com/thread-253552.htm
怎样通俗的理解CRC校验并用C语言实现 https://zhuanlan.zhihu.com/p/77408094
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |