消灭病毒游戏抓包破解

Flchy 于 2019-03-10 发布

最近无聊打发时间喜欢玩下消灭病毒,发现后期升级武器金币太高了升不动了。网上搜了下,似乎还没有破解教程,这里记录下我的修改过程,后端代码见后面

PS:2019/04/09 游戏升级,复制出来的ID加密了,工具已删除,不过实际使用的还是openid(老ID)

抓包

这里使用的是 Fiddler 4 抓包,电脑开热点,手机电脑处于同一个局域网下

先设置下抓 https 和局域网抓包

01

02

手机连上电脑开的 wifi

03

手机浏览器192.168.201.1:8888下载证书,下载完后再安装

手机 WIFI 设置代理

04

启动游戏打一关并退出,会看到有个 upload 的接口上传数据

05

06

同样的数据放到 postman 中返回{"data":{},"code":0}

修改金币和钻石后在 post 返回{"code":1000}

看到 sign 字段应该是对数据做了校验的,这里需要获取源码分析加密算法

小程序反编译

为了获取源码,需要得到小程序的源码包,并反编译获得源码

需要一部 root 权限的手机,同时清空微信数据,登陆我们的账号,只下载这一个小程序。

到达/data/data/com.tencent.mm/MicroMsg/[一长串字母]/appbrand/pkg/

看到有两个 wxapkg 文件和一个文件夹,和文件夹同名的 wxapkg 就是小程序的代码了,另一个 wxapkg 文件应该是小游戏的调试工具包

拿到 wxapkg 文件需要做反编译

这里提供了一下解包工具https://github.com/chenrensong/SS.UnWxapkg

下了 NodeJS 版本的解包失败,Java 版本的成功了,而且要比 NodeJS 版本的简单很多

Java 版本的解包工具,dest 目录有可执行 jar 包下载

# 要有Java环境
java -jar unweapp-0.1.jar "xxx.wxapkg"

最终得到如下代码

07

伪造数据包

再 game.js 文件中不难找到加密逻辑。就是对 json 排序后拼接,再 md5 加密

08

不难写出生成 sign 的代码,其中 appid 和 secret 是固定值

09

把抓包的数据放到 postman 中

修改 record 中的部分数据,生成 sign。同时到 postman 中对应修改下数据,同时修改 sign 为最新值

10

11

post 成功

重新打开游戏,发现数据已修改。如若失败是因为服务端数据还没下载到本地,本地的数据覆盖了服务端数据。最简单的办法是,当抓完包后,删除这个小程序,禁止它上传数据,当伪造数据 post 修改数据成功后,在下载下来,肯定是会先向服务端拿数据的

12

Java 版本的修改工具

主程序

public static void main(String[] args) throws Exception {
  RequestBuilder rb = Requests.post("https://wxwyjh.chiji-h5.com/api/archive/get");
  Map<String, String> header = new HashMap<String, String>();
  header.put("content-type", "application/x-www-form-urlencoded");
  header.put("User-Agent", "Mozilla/5.0 (Linux; Android 9; MIX 2S Build/PKQ1.180729.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MicroMessenger/7.0.3.1400(0x2700033B) Process/appbrand2 NetType/WIFI Language/zh_CN");
  header.put("Host", "wxwyjh.chiji-h5.com");
  rb.headers(header);

  Map<String, String> data = new HashMap<String, String>();
  data.put("plat", "wx");
  data.put("time", String.valueOf(new Date().getTime()));
  data.put("openid", "**************");
  data.put("sign", sign(data));
  rb.body(JSON.toJSONString(data));
  Map<?, ?> currentData = rb.send().readToJson(Map.class);

  if (!currentData.get("code").toString().equals("0")) {
    throw new Exception("获取当前记录失败");
  }
  System.out.println(currentData);
  Map<String, String> data2 = new HashMap<String, String>();
  data2.put("plat", data.get("plat"));
  data2.put("time", String.valueOf(new Date().getTime()));
  data2.put("openid", data.get("openid"));
  Map<?, ?> temp = (Map<?, ?>) currentData.get("data");
  // 不要打乱顺序,
  LinkedHashMap<String, Object> record = JSON.parseObject(temp.get("record").toString(), LinkedHashMap.class);
  // record.put("money", "9979029121");
  data2.put("record", JSON.toJSONString(record));
  data2.put("sign", sign(data2));

  RequestBuilder rb2 = Requests.post("https://wxwyjh.chiji-h5.com/api/archive/upload");
  rb2.headers(header);
  rb2.body(JSON.toJSONString(data2));

  Map<?, ?> updatedData = rb2.send().readToJson(Map.class);
  if (!updatedData.get("code").toString().equals("0")) {
    throw new Exception("修改记录失败");
  }
}

加密代码

public static String sign(Map<String, String> map) {
  Map<String, String> newMap = new HashMap<String, String>();
  List<String> keys = new ArrayList<String>();
  for (String key : map.keySet()) {
    keys.add(key);
    newMap.put(key, map.get(key));
  }
  newMap.put("wx_appid", "wxa2c324b63b2a9e5e");
  newMap.put("wx_secret", "8fbd540d0b23197df1d5095f0d6ee46d");
  keys.add("wx_appid");
  keys.add("wx_secret");
  Collections.sort(keys);
  StringBuilder sb = new StringBuilder();
  for (String key : keys) {
    sb.append(key).append("=").append(newMap.get(key)).append("&");
  }
  return HashKit.md5(sb.substring(0, sb.length() - 1));
}