——————————————————————————————————————————————————————————————————
——————————————————————————————————————————————————————————————————
——————————————————————————————————————————————————————————————————
不得不说,随着时代的发展,游戏产业在近几年的互联网潮流中越来越扮演者重要的地位,与之而来的不仅有网络游戏公司的春天,还有游戏灰色产业的暗流涌动。在游戏产业的发展中,诞生了一大批所谓的“外x挂”开发人员,他们不断的利用游戏的漏洞,在违法牟利的同时,也促进了游戏安全行业的进步。
同时,在游戏安全的对抗中,诞生了以下几种技术以防止游戏作弊的发生:
⒈数据检测:对基础的游戏数据进行校验,例如坐标是否违规越界地图(坐标瞬移功能),人物短时间位移距离是否过大(人物加速功能)等等
⒉CRC检测:基于游戏程序代码的检验,例如将人物移动中判断障碍物的je条件跳转修改为jmp强制跳转(人物穿墙功能)等等
⒊封包检测:将游戏数据封包进行校验,防止利用封包漏洞实现违规操作,例如之前的穿X火线强登(可以登录任意账号)等等
⒋机器检测:现在鹅厂
安全组好像换人了 ,游戏机器码封的都挺狠,一封就十年,不过道高一尺,魔高一丈,目前依然不够完善,很多朋友还是可以Pass
⒌Call检测:非法调用Call导致校验值非法,例如攻击Call的严格校验(角色扮演游戏自动打怪脚本都是调用Call的)等等
⒍堆栈检测:该检测归于调用Call过程中产生的问题
⒎文件检测:对于游戏本地文件的检测,例如之前穿X火线几年前风靡一时的REZ文件(快刀秒杀,穿墙,遁地,飞天)等等
⒏模块检测:很多外x挂采用“注入”的形式,所以模块检测在游戏安全对抗中也扮演着极其重要的作用
⒐特征检测:这个主要检测典型的使用“易语言”开发的程序,或者部分外x挂市场比较大的毒瘤程序,或者菜单绘制(imgui绘制)等等
⒑调试检测:针对调试器和调试行为的检测,对Ollydbg,CheatEngine等调试器特征和调试行为的检测等。
⒒游戏保护:主要是利用R3各种反调试技术以及驱动层的HOOK等技术实现的游戏保护,例如鹅厂的TP等等
可能还有一些我暂时还没想到的,哥哥们可以在下方补充~
——————————————————————————————————————————————————————————————————
首先,我们今天要讲的是游戏的CRC检测,所以为了能让下面的内容让大家理解,我们先来准备一下CRC检测的基础知识吧:
⒈百度百科给我们CRC的解释
1 2 3 | CRC即循环冗余校验码(Cyclic Redundancy Check):是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。
来源:https:
|
1 2 3 | CRC即循环冗余校验码(Cyclic Redundancy Check):是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。
来源:https:
|
⒉维基百科给我们CRC的解释
1 2 3 | 循环冗余校验(英语:Cyclic redundancy check,通称“CRC”)是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种散列函数,主要用来检测或校验数据传输或者保存后可能出现的错误。生成的数字在传输或者存储之前计算出来并且附加到数据后面,然后接收方进行检验确定数据是否发生变化。一般来说,循环冗余校验的值都是32位的整数。由于本函数易于用二进制的计算机硬件使用、容易进行数学分析并且尤其善于检测传输通道干扰引起的错误,因此获得广泛应用。此方法是由W. Wesley Peterson于1961年发表[1]。
来源:https:
|
1 2 3 | 循环冗余校验(英语:Cyclic redundancy check,通称“CRC”)是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种散列函数,主要用来检测或校验数据传输或者保存后可能出现的错误。生成的数字在传输或者存储之前计算出来并且附加到数据后面,然后接收方进行检验确定数据是否发生变化。一般来说,循环冗余校验的值都是32位的整数。由于本函数易于用二进制的计算机硬件使用、容易进行数学分析并且尤其善于检测传输通道干扰引起的错误,因此获得广泛应用。此方法是由W. Wesley Peterson于1961年发表[1]。
来源:https:
|
我们总结一下上面的百科解释:CRC是一种校验算法并且该算法被广泛应用于文件,数据等的校验。
不过好像对于逆向来说还是有些模糊,那么现在就让我们打开看雪论坛发布的《加密与解密(第四版)》第279页,看一下其中的解释:
通过翻阅书籍,我们发现实际上CRC算法有很多种,而我们运用在软件加密中的CRC算法为CRC32算法。
哇,这本书,兄弟萌,你们看,这本书,简直就是逆向界的圣经啊,别犹豫了!买他!书本有价!知识无价!
哈哈,开个玩笑,在这里对所有参与《加密与解密》书籍工作的朋友致敬!
——————————————————————————————————————————————————————————————————
参考《加密与解密》书籍,我们便写如下代码,实现我们的CRC检测:
参考《加密与解密》书籍,我们便写如下代码,实现我们的CRC检测:
首先,为了方便我们进行学习,我们将CRC算法运用于自己身上,检验自身代码是否被修改,整体性代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | #include <Windows.h>
#include <stdio.h>
int crc = NULL;
int have_crc_table = NULL;
unsigned int crc32_table[256];
void Crc_Make_Table()
{
have_crc_table = 1;
for ( int i = 0; i < 256; i++)
{
crc = i;
for ( int j = 0; j < 8; j++)
{
if (crc & 1)
crc = (crc >> 1) ^ 0xEDB88320;
else
crc >>= 1;
}
crc32_table[i] = crc;
}
}
unsigned int Calc_Crc32(unsigned int crc, char *Data, int len)
{
crc = 0xFFFFFFFF;
if (!have_crc_table)
Crc_Make_Table();
for ( int i = 0; i < len; i++)
{
crc = (crc >> 8) ^ crc32_table[(crc ^ Data[i]) & 0xff];
}
return ~crc;
}
int main()
{
SetConsoleTitleA( "Crc检测过掉学习工具 Ver1.0 提供方:小迪Xiaodi" );
printf ( "\n\n" );
printf ( "使用CE工具->添加地址0x402000->查找访问并尝试过掉检测!\n\n" );
printf ( "如果修改主程序模块,将会提示 “CRC代码校验检测到您修改了代码!”:\n\n\n\n\n" );
unsigned int uMainMoudleSumA = Calc_Crc32(0, ( char *)0x400000, 0x1F000);
while (1)
{
unsigned int TmpCrcSum = Calc_Crc32(0, ( char *)0x400000, 0x1F000);
if (TmpCrcSum != uMainMoudleSumA)
{
MessageBoxA(NULL, "CRC代码校验检测到您修改了代码!" , "Caption" , MB_OK);
}
Sleep(2000);
}
getchar ();
return 0;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | #include <Windows.h>
#include <stdio.h>
int crc = NULL;
int have_crc_table = NULL;
unsigned int crc32_table[256];
void Crc_Make_Table()
{
have_crc_table = 1;
for ( int i = 0; i < 256; i++)
{
crc = i;
for ( int j = 0; j < 8; j++)
{
if (crc & 1)
crc = (crc >> 1) ^ 0xEDB88320;
else
crc >>= 1;
}
crc32_table[i] = crc;
}
}
unsigned int Calc_Crc32(unsigned int crc, char *Data, int len)
{
crc = 0xFFFFFFFF;
if (!have_crc_table)
Crc_Make_Table();
for ( int i = 0; i < len; i++)
{
crc = (crc >> 8) ^ crc32_table[(crc ^ Data[i]) & 0xff];
}
return ~crc;
}
int main()
{
SetConsoleTitleA( "Crc检测过掉学习工具 Ver1.0 提供方:小迪Xiaodi" );
printf ( "\n\n" );
printf ( "使用CE工具->添加地址0x402000->查找访问并尝试过掉检测!\n\n" );
printf ( "如果修改主程序模块,将会提示 “CRC代码校验检测到您修改了代码!”:\n\n\n\n\n" );
unsigned int uMainMoudleSumA = Calc_Crc32(0, ( char *)0x400000, 0x1F000);
while (1)
{
unsigned int TmpCrcSum = Calc_Crc32(0, ( char *)0x400000, 0x1F000);
if (TmpCrcSum != uMainMoudleSumA)
{
MessageBoxA(NULL, "CRC代码校验检测到您修改了代码!" , "Caption" , MB_OK);
}
Sleep(2000);
}
getchar ();
return 0;
}
|
最后,我们在生成文件的时候,要注意以下几个问题:
⒈静态编译和去除优化等的设置
⒉CRC校验函数传入参数的设置:
代码处:
1 2 | Calc_Crc32(0, ( char *)0x400000, 0x1F000);
|
1 2 | Calc_Crc32(0, ( char *)0x400000, 0x1F000);
|
在这里尤其要注意传入的第三个参数,他代表了一个校验的范围,那么这个位置,我们如何确定呢?
①:先编译生成文件,我生成的是Release版本:
②:用PE相关的工具确定程序主模块镜像的大小,在这里我使用的是PEID v0.95工具:
复制粘贴一下镜像大小,传入一下就可以了,这个参数不可以乱填,否则会造成数据溢出,导致程序崩溃。
——————————————————————————————————————————————————————————————————
⒈运行自己的程序并用CheatEngine添加地址:
——————————————————————————————————————————————————————————————————
⒈运行自己的程序并用CheatEngine添加地址:
⒉修改代码测试:
由于0x00402000处于代码段的位置,所以我们修改数值就相当于修改了代码,也就相应的触发了代码CRC的校验
⒊我们将末尾的数字“2”改为“3” ,直接触发了CRC检测:
——————————————————————————————————————————————————————————————————
①首先,在攻击前,我们要知道代码的CRC检测是针对代码段的
②代码段是用来执行的,正常情况下不会有其他数据访问代码段,被访问的大多是数据段,代码段被访问,很可疑就是CRC检测
③此处说的“访问”的概念,大家可以通过CheatEngine工具中的“找出是什么访问了这个地址”来理解
开干!
⒈针对0x402000这个地址,在CheatEngine工具中鼠标右键,查找访问,操作如下:
——————————————————————————————————————————————————————————————————
①首先,在攻击前,我们要知道代码的CRC检测是针对代码段的
②代码段是用来执行的,正常情况下不会有其他数据访问代码段,被访问的大多是数据段,代码段被访问,很可疑就是CRC检测
③此处说的“访问”的概念,大家可以通过CheatEngine工具中的“找出是什么访问了这个地址”来理解
开干!
⒈针对0x402000这个地址,在CheatEngine工具中鼠标右键,查找访问,操作如下:
2.检测出现了!
我们记录一下该处的访问代码:
1 | 0040103F - movsx ecx,byte ptr [eax]
|
1 | 0040103F - movsx ecx,byte ptr [eax]
|
因为Ollydbg的调试体验要更好一些,所以我们记录地址:0x40103F,并转到Ollydbg去分析:
Ollydbg舒服就舒服在还能把你的代码分析出来,太强了!
⒊下断走一遍流程
刚开始的时候,注意观察eax:
单步往下执行,下面会有个强制性的向上跳转:
继续执行走到初始位置:
总结:发现eax由0x400000变成了0x400001,也就是说,它在循环的递增检测所有范围内的内存代码数据
⒋返回上一层观查一下函数
通常,大家可能会CTRL+F9返回上一层,或者按如下图中返回:
这样做后就发现无论如何都无法返回,那应该怎样做呢?
很简单,我们可以从堆栈中返回,堆栈窗口有个神奇的功能就是返回数据:
对着“返回到”敲下回车键,抽个烟的时间:
奇迹竟然发生了:
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2019-8-13 12:12
被小迪xiaodi编辑
,原因: