吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 10998|回复: 15
收起左侧

[调试逆向] CRC检测的实现与对抗

  [复制链接]
蓝蓝的小刺客 发表于 2020-4-6 19:08
一直听说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[i]) & 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的值。
图片1.png

程序正常执行如下:
图片2.png

3.过掉CRC检测
使用OD打开程序,F9跑起来:
图片3.png


我们可以在代码段随便修改一条指令,看到效果:程序已经检测到代码被修改。
图片4.png
图片5.png


好,现在我们试着过掉检测,因为CRC会不断的读取要验证的代码,所以我们可以使用CE查看是哪些代码在读取我们的程序。CE附加进程后,先手动添加一条代码段的地址,这里我就添加了代码段的起始地址,然后查看什么访问了该地址。
图片6.png


在OD中看一下这个地址,CE也可以看,不过用OD更直观。
这段代码就是我们计算CRC的代码,esi中保存了最后计算出的CRC码,传给eax作为函数的返回值。
图片7.png


在这个函数中下个软件断点,因为软件断点会修改当前地址的指令为int 3,所以程序正常执行肯定会退出,我们单步跟着程序走,找到判断的代码
图片8.png


很快遇到一个跳转, 这个就很明显了,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附加被保护的程序,再次查找一下是什么访问了内存地址。
图片9.png

发现只能看到自身校验的代码,随便修改一处代码段中的内容,再查看跨进程校验的程序。
图片10.png


这个程序也发现代码段被修改了,说明思路没问题。
这样我们可以采用自身CRC校验全部代码,通过保护进程来校验CRC校验的代码的方案来达到检测的目的。
当然这种方式通过遍历进程句柄表查找哪些进程打开了被保护进程,也可以过掉,但是相比于CRC自校验来说,门槛一下就提高了。
今天就写到这里吧,新人第一次发帖,有不足的地方还望大佬们指正。
参考资料:
《加密与解密第四版》   段刚
网络游戏安全之实战某游戏厂商FPS游戏CRC检测的对抗与防护   https://bbs.pediy.com/thread-253552.htm
如何通俗的理解CRC校验并用C语言实现   https://zhuanlan.zhihu.com/p/77408094

免费评分

参与人数 5威望 +1 吾爱币 +24 热心值 +5 收起 理由
大肥虫 + 1 + 1 学习了
小菜鸟一枚 + 1 + 1 拜读大佬的文章,学习一下!
窝窝窝窝窝窝你~ + 1 + 1 思路清晰
scsl + 1 + 1 我很赞同!
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

深空恐惧 发表于 2020-4-8 16:15
技术贴里的新人都这么厉害了吗,膜拜学习
llpgy521 发表于 2020-5-7 10:52
小菜鸟一枚 发表于 2020-7-9 12:28
z26351911 发表于 2020-7-30 15:54

学习了 感谢!
wwbtowwb 发表于 2020-8-1 19:13
谢谢楼主!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
xieyueyang 发表于 2020-8-4 14:41
本小白很惊叹
wwbtowwb 发表于 2020-8-23 19:16
我来学习啦  谢谢
语过添情jj 发表于 2020-9-25 14:47
我用VS2017运行这个CRC的代码,为啥提示有很多错误呢,是代码不全吗?
吾爱的破解 发表于 2020-9-27 13:43
授人鱼不如授人以渔
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-22 00:47

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表