## JS小游戏内存风险检测与辅助加固工具

《小游戏内存风险检测与辅助加固工具》是一个基于代码静态分析的内存篡改风险识别与辅助加固的工具，帮助开发者更全面地识别风险点，提升加固效率。由开发者指定加解密函数，对风险点的数值做内存隐藏，最终提升内存篡改挂成本。

本工具提供了两项功能：

- 检测提效：自动发现内存数值篡改风险点，呈现给使用者。
- 辅助加固：提供web交互，让开发者逐个确认风险点是否加固。

# 操作说明

### 运行环境

chrome 浏览器。

### 安装流程

请点击[下载链接](https://res.wx.qq.com/op_res/yNKiSEkxweQlbCXtK4_0lCFygeksU4jLkPf3H40GUbNaGRJKH0V11bBnsLtWjmJnbhZIF_oLQcTjlt3Przhvkg)，获得安装包，安装后即可

### 使用流程

![](https://res8.wxqcloud.qq.com.cn/wxdoc/95c355f4-09e3-4be9-934d-f7339ff011ab.png)

step1. 在浏览器中打开 index.html。

step2. 上传待保护的 js 文件，点击 “一键检测”。如果文件较大，耗时较多，耐心等待。 

step3. 工具会对所有内存篡改风险点打上 getProxied 标记（使用 getProxied 包裹起来）。默认会加固这个保护点，如果需要取消这个保护点，那么点击 “取消加固”；否则确认下一个。 

step4. 选择左侧，点击自定义加解密函数->启用函数编辑，开发者需实现数据加解密函数，否则代码将抛出异常，无法正常运行。

stpe5. 确认完毕后，点击 “下载参考代码”，若提示【你还未自定义加解密函数，是否确认下载】，则需要检查第四步是否有正确实现数据加解密函数。

### 加固验证

如果有条件，可以对加固效果做验证。在PC上推荐使用 [Cheat Engine](https://www.cheatengine.org/) 工具。验证方法：

step1: 确认游戏外显数值，如分数、金币等。

step2: 打开Cheat Engine，选择进程，切换到进程显示页面。

step3: 选择进程页面的 WeChatAppEx.exe，可能会有多个，需逐个测试确认选择的游戏进程。

step4: 搜索游戏外显数值的两倍（或原值），未加固的游戏通过多次搜索可以定位到，已加固的则无法搜索到。

![](https://res8.wxqcloud.qq.com.cn/wxdoc/34297b8a-4cb6-4277-b91c-0622a4975689.png)

![](https://res8.wxqcloud.qq.com.cn/wxdoc/fb0b8868-9cf7-426c-aa21-2c2a2ec76e55.png)

### 注意事项

- 为简化操作，开发者点击"取消加固"表示放弃某个风险点的加固，不点击表示确认加固。（全部不点击则表示对全部风险点做加固）
- 为安全考虑，开发者需要自行指定数值加解密函数（encode/decode），注意数值计算溢出与精度问题。
- 为安全考虑，最终生成代码，推荐使用[《游戏深度保护插件》](codeprotect)对代码保护，防止攻击者识别加解密函数做对抗。

# 常见问题排查

### 生成代码运行异常

若抛出异常："You must implement this function"，则提示开发者需自行实现 encode/decode 函数。

###  工具异常

以下偶发情况：

- 检测时间过长：往往是原代码体积较大引起，可以等待1分钟，如果不能解决问题，选择重新启动工具，或者点击[咨询小助手](https://work.weixin.qq.com/kfid/kfcca4feec277f91616)。
- 检测弹窗报错：往往是原代码语法问题，请根据提示信息去修复语法问题。

## unity小游戏内存加固工具

《unity小游戏内存加固工具》可以保护unity小游戏的关键数值，防止被外挂修改，提升游戏安全性。加固工具目前已集成至unity小游戏转换工具中，开发者按照文档中的步骤做代码微调，即可实现unity小游戏的内存加固。

# 接入步骤

按照如下步骤接入使用，接入后务必做好充分测试再上线。

### 引入命名空间

```c#
using WeChatWASM.GameProtect.Monitor;
```

### 基础用法

```c#
// 存储数据
WXDataPlugin.Instance.Set("coins", 1000);
WXDataPlugin.Instance.Set("playerName", "Player1");

// 读取数据
int coins = WXDataPlugin.Instance.Get<int>("coins");
string name = WXDataPlugin.Instance.Get<string>("playerName");
```

### 安全读取（推荐）

```c#
// 安全读取，避免异常
if (WXDataPlugin.Instance.TryGet<int>("coins", out int coins))
{
    Debug.Log($"金币: {coins}");
}
else
{
    Debug.LogWarning("读取金币失败，可能被篡改");
}
```

### 订阅篡改事件

```c#
using WeChatWASM.GameProtect.Monitor;

private void Start()
{
    // 订阅数据篡改事件
    WXDataPlugin.Instance.DataTampered += OnDataTampered;
}

private void OnDataTampered(object sender, WXDataProtectionEventArgs e)
{
    Debug.LogError($"检测到数据篡改！Key: {e.Key}");

    // 实现反作弊逻辑
    HandleTampering(e);
}
```

# API 参考

### 核心方法

```json
[
  {
    "方法": "Set(string key, object value)",
    "描述": "存储数据"
  },
  {
    "方法": "Get<T>(string key)",
    "描述": "获取指定类型的数据"
  },
  {
    "方法": "Get(string key)",
    "描述": "获取数据（返回object）"
  },
  {
    "方法": "TryGet<T>(string key, out T value)",
    "描述": "安全获取数据"
  },
  {
    "方法": "HasKey(string key)",
    "描述": "检查key是否存在"
  },
  {
    "方法": "Remove(string key)",
    "描述": "删除指定数据"
  },
  {
    "方法": "Clear()",
    "描述": "清空所有数据"
  }
]
```

### 事件

```json
[
  {
    "事件": "DataTampered",
    "描述": "数据被篡改时触发"
  }
]
```

### 事件参数 (WXDataProtectionEventArgs)

```json
[
  {
    "属性": "Key",
    "类型": "string",
    "描述": "被篡改的数据key"
  },
  {
    "属性": "ExpectedValue",
    "类型": "object",
    "描述": "期望的原始值"
  },
  {
    "属性": "ActualValue",
    "类型": "object",
    "描述": "实际检测到的值"
  },
  {
    "属性": "ValueType",
    "类型": "Type",
    "描述": "数据类型"
  },
  {
    "属性": "DetectedTime",
    "类型": "DateTime",
    "描述": "检测到篡改的时间"
  },
  {
    "属性": "TamperDescription",
    "类型": "string",
    "描述": "篡改描述信息"
  }
]
```

# 最佳实践

### 数据分类保护

```c#
// 重要货币数据
WXDataPlugin.Instance.Set("coins", playerCoins);
WXDataPlugin.Instance.Set("diamonds", playerDiamonds);

// 游戏进度数据
WXDataPlugin.Instance.Set("level", playerLevel);
WXDataPlugin.Instance.Set("experience", playerExp);

// 配置数据
WXDataPlugin.Instance.Set("settings", gameSettings);
```

### 封装常用操作

```c#
using WeChatWASM.GameProtect.Monitor;

public static class CurrencyManager
{
    public static void AddCoins(int amount)
    {
        int current = WXDataPlugin.Instance.Get<int>("coins");
        WXDataPlugin.Instance.Set("coins", current + amount);
    }

    public static bool SpendCoins(int amount)
    {
        int current = WXDataPlugin.Instance.Get<int>("coins");
        if (current >= amount)
        {
            WXDataPlugin.Instance.Set("coins", current - amount);
            return true;
        }
        return false;
    }
}
```

### 篡改应对策略

```c#
private void HandleDataTampering(WXDataProtectionEventArgs e)
{
    switch (e.Key)
    {
        case "coins":
        case "diamonds":
            // 货币被篡改：重置为0或从服务器同步
            ResetCurrency(e.Key);
            break;

        case "level":
            // 等级被篡改：从服务器重新获取
            SyncLevelFromServer();
            break;

        default:
            // 记录日志并上报
            LogTamperAttempt(e);
            break;
    }
}
```
