Anti-Cheat – 实现一种检查内存被修改的方法

这个Anti-Cheat方法为了解决什么问题?

一般来说,一款网页手机游戏纯靠客户端来解决反作弊( Anti-Cheat )几乎是不可能的。在这种情形下,页游手游客户端(后文均称游戏客户端)可以利用一些手段来增加作弊破解人员的成本,包括但不限于:代码加密,代码混淆,隐藏明文,内存混淆(加密)……

这个方法主要是避免检测出类似 CE工具 对客户端内存进行恶劣修改达到关键数据变更的行为,属于上面提到的内存混淆(加密)类。

本文建立在所有源码未被对方知晓的情况下进行操作……

内存混淆(加密)通用做法

游戏客户端将原始数据保留二份,一份原始数据,一份加密数据。调用set()方法时,原始数据赋原始值,加密数据采用加密方法存储数据(比较基础的方法是 数据异或 );调用get()方法时,对加密数据与原始数据进行校验,通过校验结果是否一致来判定内存是否被修改。

我们以整形为例,大致的实现:

class IntShadow {
public:
    int get() {
        if (origi != shadow ^ salt) {
            //这里处理内存被修改
        }
        return origi;
    }

    void set(int setInt) {
        salt  = rand();
        origi = setInt;
        shadow= setInt ^ salt;
    }
private:
    int origi;
    int shadow;
    int salt;
};

既然是造轮子,那么这个轮子的特点 – class BinarySep

1、使用简单

这个Anti-Cheat的方法使用方式类似于std::vector,例如:

BinarySep; a(1);
BinarySep; b(12345);
BinarySep;    c(false);

2、通用

支持所有的连续内存的基础类型包括不限于 int32_tdoubleboolstd::string(char *) ,其中string实现是将内容转化成为char *处理,也可以使用std::vector<BinarySep >进行嵌套使用。堆里的内容也可以转化成为char *处理。

3、混淆加密性较强

将内存分片化处理,按3264位机器内存对齐方式进行结构体随机数填充,来增加修改者寻找规律的复杂度。细节如下图:

在List上, 黄色蓝色字节处为原始数据字节,其他字节均用随机字符填充 。其中原始字节可以进行 异或混淆 或者 Base64 之类的方法混淆加密。

这里这样做的原因主要是 struct unit_list结构体, 在64位机器上若取3字节分片,那么sizeof(struct unit_list)==24,故我们将多余的5个字节当做padding填充随机字符。

需要注意的是,当分片大小取8时,padding[0]会导致部分编译器无法编译通过。

一些不足

占用空间大

初略计算一个占4字节的整形需要40字节的栈空间,48字节的堆空间(主要是链表)。由于链表使用的是linux内核的侵入式链表,它是双向的(这里优化点-可以改成单向,节约24字节的空间)。所以建议使用它对关键数据混淆加密,而非所有数据。

一些类型使用需小心

比较典型是string类型,使用了堆空间进行存储,从储存到获取是一个string->char*->string的过程。对于结构体类型可以直接混淆存储或者序列化成char *存储(但是结构体里指向堆内容指针无法直接处理)。

未支持所有运算符

目前只重载了”=”运算符,对于”!=”、”==”、”>”…之类的运算符可以调用get()方法获得基本类型后比较,比如:

BinarySep; a(1);
    BinarySep; b(2);

    if (a.get() > b.get()) {
        //...
    } else if (a.get() < 0) {
        //...
    }

本文对Anti-Cheat中内存混淆(加密)的源码实现未投入实际项目上线运行。

源码戳这里 -> Anti-Cheat BinarySep

(全文结束)

稿源:漫漫路 (源链) | 关于 | 阅读提示

本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 综合编程 » Anti-Cheat – 实现一种检查内存被修改的方法

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录