前言
最近掘金出了一款小游戏叫做海底掘金, 挖取矿石可以兑换周边礼品
我眼馋那款游戏机已久, 于是乎就玩起了这款游戏, 不过每天手动操作实在太过劳神, 于是, 我打算将其流程自动化
所有的数据都是离不开接口, 我们只需要模拟接口请求即可
那么首先要做的就是分析游戏中每一个动作都发送了什么数据
动作分析
上移
1
{"command":["U"]}
下移
1
{"command":["D"]}
左移
1
{"command":["L"]}
右移
1
{"command":["R"]}
跳跃
1
2
3
4
5
6
7
8//上跳跃
{command: ["8"]}
//下跳跃
{command: ["2"]}
//左跳跃
{command: ["4"]}
//右跳跃
{command: ["6"]}一层循环2次上移
1
{command: [{times: 2, command: ["U"]}]}
- times: 表示循环次数
两层循环
1
{command: [{times: 2, command: [{times: 3, command: ["D"]}]}]}
多层循环则多层嵌套
接口分析
动作接口
1
https://juejin-game.bytedance.com/game/sea-gold/game/command?uid=xxx&time=xxx
游戏结束接口
1
https://juejin-game.bytedance.com/game/sea-gold/game/over?uid=xxx&time=xxx
游戏开始接口
1
https://juejin-game.bytedance.com/game/sea-gold/game/start?uid=xxx&time=xxx
Token获取接口
1
https://juejin.cn/get/token
这个接口获取到的值其实就是其他接口中请求头
authorization
的值, 具体往下看获取矿石数量相关信息
1
https://juejin-game.bytedance.com/game/sea-gold/home/info?uid=xxx&time=xxx
浏览器控台自动化实现
我们跳过登录验证操作, 先从简单的控台自动化入手, 实现动作的自动化, 有效避免作弊检测
比如我们想要实现以下动作的反复执行
那么发送的数据如下:
1 | {"command":[{"times":10,"command":["L","2","R",{"times":10,"command":["L","4","D",{"times":10,"command":["R","D","2"]}]}]}]} |
这一步我们可以直接从浏览器控制台里拷贝出来, 不用自己计算, 方便快捷准确
然后结合动作接口和请求头进行模拟请求即可
如下请求头
- Content-type
- authorization (关键)
- x-tt-gameid (关键)
- accept
以上请求头数据直接从控制台拷贝
代码实现如下:
1 | await (async function autoRun(){ |
注意, 每一局的gameId
是不一样的, 使用相同的gameId
会报游戏异常, 这种操作大家要尽量避免, 谁知道官方有没有利用这个来判定玩家是否作弊呢, 谨慎为上
请求成功后响应数据如下:
1 | {"data":{"appendMapData":[0,0,133,0,0,0,27,122,6,0,0,22,6,0,0,6,135,6,0,21,21,0,0,122,0,0,0,0,23,0,6,6,0,0,6,0,25,0,0,121,0,6,6,0,29,0,6,6,0,151,6,0,0,6,27,0,0,6,0,0,0,0,125,6,0,0,0,5,0,135,113,143,0,6,131,0,0,102,101,151,6,6,0,0,0,0,3,0,0,6,0,6,6,6,122,0,6,6,0,6,6,0,0,0,29,0,6,0,6,0,0,101,6,0,24,6,6,0,0,0,6,0,6,6,6,132,0,6,0,0,0,0,6,0,5,6,6,131,114,0,0,0,0,0,0,6,6,0,6,0,6,6,6,0,104,0,6,0,4,0,0,0,6,125,6,113,0,0,0,6,0,6,6,0,0,0,6,6,0,0,6,0,5,6,5,121,28,0,0,24,0,0,6,151,0,0,141,6,0,24,0,6,0,0,6,6,0,0,131,6,105,6,0,0,27,113,6,0,6,0,0,143,0,0,101,0,29,0,0,6,0,0,0,133,6,6,25,24,151,29,6,104,6,0,6,6,5,0,0,131,6,131,6,6,0,0,0,6,0,28,133,0,124,6,6,0,0,0,131,0,22,0,0,0,0,0,0,0,22,131,6,6,0,6,6,24,124,6,0,0,101,6,6,0,0,0,103,0,6,26,0,0,0,0,103,0,6,0,6,131,0,6,0,6,6,6,0,141,6,29,0,0,0,6,0,3,6,0,6,6,6,0,0,114,0,0,6,0,0,6,0,6,0,6,6,6,0,0,28,23,6,102,0,115,4,6,6,0,6,27,6,6,0,6,6,0,0,24,0,3,6,0,0,143,0,29,0,26,0,4,6,105,0,0,0,6,0,21,132,21,0,0,0,6,4,103,6,0,27,6,0,24,24,0,6,104,0,104,0,0,6,6,152,6,0,6,0,0,0,0,0,0,0,6,24,6,6,23,143,0,0,24,0,6,0,6,0,0,0,151,0,26,0,6,0,0,0,0,6,0,0,0,0,6,135,0,6,6,0,0,0,0,29,6,0,0,6,0,24,25,6,0,0,0,0,0,6,0,0,6,0,6,152,28,6,25,6,0,6,0,6,6,0,0,0,25,0,0,6,122,6,0,0,6,112,6,0,0,124,0,0,0,0,25,0,6,6,6,6,27,112,24,121,6,6,0,6,0,6,0,6,115,0,22,6,6,6,115,6,6,101,0,27,5,105,0,0,0,0,0,0,0,6,0,0,0,3,6,6,0,0,0,0,6,0,0,0,0,26,6,6,0,0,6,6,6,6,6,0,6,0,6,6,0,0,0,0,6,0,6,6,0,0,3,5,104,0,0,122,27,0,0,0,0,23,0,0,0,0,0,4,23,25,6,0,153,0,0,0,0,0,6,0,0,0,6,6,6,0,6,0,4,0,6,6,0,3,0,112,0,0,0,0,0,0,6,0,113,0,0,0,6,6,21,0,6,124,0,6,115,0,0,23,132,0,0,6,0,6,6,0,6,6,6,0,6,0,6,0,0,6,6,0,6,6,26,0,134,0,6,6,6,0,0,0,0,0,0,0,0,0,6,6,6,113,111,0,6,0,6,0,0,0,0,0,124,0,0,6,0,22,6,6,24,22,6,104,0,125,0,0,6,6,0,6,6,0,0,121,6,0,6,0,0,6,0,6,6,0,0,0,0,0,25,0,0,6,0,6,6,6,111,0,0,6,0,5,0,0,0,6,6,6,0,6,0,123,6,0,6,25,0,0,6,113,0,0,6,0,0,0,0,0,6,0,0,6,122,6,134,0,0,6,0,134,0,6,0,0,0,6,103,0,24,0,6,0,6,0,0,0,0,0,6,6,0,132,6,0,6,103,6,0,6,28,26,6,0,111,6,131,0,27,27,0,6,6,0,102,0,0,6,0,6,0,0,6,21,6,0,6,0,6,121,26,0,6,0,6,0,0,0,0,0,0,6,0,24,0,0,0,6,0,4,0,22,23,28,0,6,6,111,153,131,0,0,0,0,0,6,27,0,0,23,0,0,0,0,6,0,0,104,0,0,0,104,6,0,0,0,6,6,0,0,6,29,6,0,0,0,0,6,6,0,0,6,6,0,0,0,23,6,0,0,0,6,0,113,6,0,6,6,6,0,0,0,29,152,0,0,0,153,0,6,6,0,6,6,0,26,6,6,6,151,21,0,21,6,0,21,6,6,6,0,6,0,0,0,22,6,6,6,0,6,0,21,6,6,0,6,6,22,0,5,0,6,0,0,0,0,0,28,6,0,6,114,0,6,0,0,6,0,23,0,0,6,0,0,114,6,6,0,0,0,6,0,0,0,0,6,0,6,6,0,0,0,0,0,0,0,24,0,0,0,6,134,0,0,0,131,0,0,0,0,6,6,25,0,0,6,5,0,104,0,6,6,0,0,6,0,0,0,6,0,6,101,0,0,6,0,6,0,0,0,125,0,0,6,134,0,6,6,27,0,6,0,131,6,0,0,6,0,0,6,0,123,0,0,0,0,6,0,0,6,6,6,6,0,6,0,6,0,0,0,25,0,0,0,0,0,6,0,0,6,0,6,0,0,0,6,0,6,6,6,114,6,6,0,0,0,0,0,6,0,0,6,124,6,6,0,0,154,6,0,152,0,0,22,6,6,6,0,6,26,0,0,111,0,133,29,0,0,6,6,104,133,0,0,6,26],"curPos":{"x":5,"y":210},"blockData":{"moveUp":29,"moveDown":30,"moveLeft":14,"moveRight":27,"jump":7,"loop":15},"gameDiamond":112},"code":0,"message":"success","logId":"xxxx","serviceTime":1638159487113} |
注意看返回的gameDiamond
这个字段, 我们执行一次接口立马就获取了112
个矿石, 也就是说我们掉完接口立马结束游戏, 那么这矿石就到手了, 省去了游戏执行的时间
在同一局中, 我们可以反复调接口, 直到道具不足为止, 如果道具不足就重开一局再过一遍之前的代码
到这里 问题就来了, 每一局的x-tt-gameid
都不一样, 每新开一局都要拷贝一次太繁琐了,接下来我们解决x-tt-gameid
的问题
签名破解
接下来全局翻找源码, 从js
源码中, 我大致猜测这个x-tt-gameid
是由ES256
算法生成accessToken
, 找到如下代码:
1 | h.default.sign({gameId:this.gameId,time:t},"-----BEGIN EC PARAMETERS-----\nBggqhkjOPQMBBw==\n-----END EC PARAMETERS-----\n-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIDB7KMVQd+eeKt7AwDMMUaT7DE3Sl0Mto3LEojnEkRiAoAoGCCqGSM49\nAwEHoUQDQgAEEkViJDU8lYJUenS6IxPlvFJtUCDNF0c/F/cX07KCweC4Q/nOKsoU\nnYJsb4O8lMqNXaI1j16OmXk9CkcQQXbzfg==\n-----END EC PRIVATE KEY-----\n",{algorithm:"ES256",expiresIn:2592e3,header:{alg:"ES256",typ:"JWT"}});return console.log("token",e) |
当然也只是猜测, 需要进行验证
为了方便测试, 我将代码测试从控台转移到nodejs
环境中
对于nodejs
不太熟悉的朋友, 我这里简单介绍一下:
nodejs
可以通俗理解为是模拟了一个浏览器的环境, 相当于java
中的jre
, 以前js
代码只能在浏览器中执行, 有了nodejs
环境, 我们就可以脱离浏览器随处运行js
代码啦
好了 下载安装完nodejs
之后, 安装jsonwebtoken
模块用于jwt
的生成
1 | npm install jsonwebtoken |
然后运行以下代码测试:
1 | const jwt = require('jsonwebtoken'); |
解码得到:
1 | { |
至此, 我们既验证了密钥的正确性, 同时得到了加密参数, 而这个time
则是当前时间毫秒值
那接下来我们需要在游戏中实际验证一下才行, 验证步骤如下:
- 调用游戏开始接口获取返回的
gameId
- 将
gameId
进行ES256
加密得到签名 - 调用动作接口, 并将签名放入
x-tt-gameid
请求头中 - 请求成功, 验证ok
好了 我们的第一阶段的破解工作已完毕, 接下来就是对代码进行整理, 实现自动化运行
由于代码转移到nodejs
上之后使用fetch
进行网络请求代码一直报错, 因此请求方式改为axios
这里多一嘴, 虽然nodejs
模拟了浏览器的环境, 但是浏览器内置的一些API
, nodejs
上不一定也内置了, 要用的话的npm
手动安装, 比如fetch
是浏览器内置的API, 控台直接可以使用, 如果要在node
上用的话可以执行以下指令安装:
1 | npm install node-fetch |
然后代码中引用:
1 | const fetch = require('node-fetch'); |
同样axios
也是:
1 | npm install axios |
代码引用:
1 | const axios = require('axios'); |
关于fetch
和axios
两者的实现原理和区别, 有兴趣的可自行百度, 这里就不展开介绍了
回过头来, 整理后的代码如下:
1 | const jwt = require('jsonwebtoken'); |
将以上代码拷贝到你的本地文件中, 填入你的游戏authorization
和uid
, 然后每天用node
运行一遍即可
到这里, 第一阶段的自动化已然实现
程序定时执行
每天手动跑代码太费事, 稍不留神就有可能某天给漏了, 尤其是周六日不开电脑的时候岂不是没法执行脚本了
这时 我们可以考虑放到服务器中每天定时执行, 365天风雨无阻, 推荐大家使用github
或者travis ci
免费服务部署
我这里手上刚好有一台阿里云的服务器, 因此直接用服务器跑, 这里我使用的是crontab
定时程序, 关于crontab
的用法, 可参见《Linux Crontab 命令安装和使用教程:在 VPS 上设置定时任务》
以ubuntu
为例具体操作步骤如下:
将本地脚本拷贝至服务器
1
scp 脚本文件 root@服务器IP:/root
注意:冒号前后不能有空格, 否则会提示目录不存在
登录服务器配置
crontab
任务执行
crontab -e
进入cron
编辑界面, 将以下定时代码写入1
0 8 * * * /usr/bin/node /root/jwt_test >>/root//log/jwt_test_$(date +\%Y-\%m-\%d-\%H).log 2>&
表示每天8点执行该脚本
安装
nodejs
及相关依赖如果服务器已经安装nodejs可忽略该步操作
1
2
3
4
5
6
7
8
9//更新源
sudo apt-get update
//安装nodejs
sudo apt-get install -y nodejs
//安装npm包管理器
sudo apt-get install npm
//安装脚本依赖
npm install jsonwebtoken
npm install axios配置完毕
本文为作者原创转载时请注明出处 谢谢