最近无聊打发时间喜欢玩下消灭病毒,发现后期升级武器金币太高了升不动了。网上搜了下,似乎还没有破解教程,这里记录下我的修改过程,后端代码见后面
PS:2019/04/09 游戏升级,复制出来的ID加密了,工具已删除,不过实际使用的还是openid(老ID)
抓包
这里使用的是 Fiddler 4 抓包,电脑开热点,手机电脑处于同一个局域网下
先设置下抓 https 和局域网抓包
手机连上电脑开的 wifi
手机浏览器192.168.201.1:8888
下载证书,下载完后再安装
手机 WIFI 设置代理
启动游戏打一关并退出,会看到有个 upload 的接口上传数据
同样的数据放到 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"
最终得到如下代码
伪造数据包
再 game.js 文件中不难找到加密逻辑。就是对 json 排序后拼接,再 md5 加密
不难写出生成 sign 的代码,其中 appid 和 secret 是固定值
把抓包的数据放到 postman 中
修改 record 中的部分数据,生成 sign。同时到 postman 中对应修改下数据,同时修改 sign 为最新值
post 成功
重新打开游戏,发现数据已修改。如若失败是因为服务端数据还没下载到本地,本地的数据覆盖了服务端数据。最简单的办法是,当抓完包后,删除这个小程序,禁止它上传数据,当伪造数据 post 修改数据成功后,在下载下来,肯定是会先向服务端拿数据的
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));
}