A-A+

HZW 某游戏作弊修改分数问题解析

2022年12月01日 11:25 漏洞安全 暂无评论 共6341字 (阅读798 views次)

【注意:此文章为博主原创文章!转载需注意,请带原文链接,至少也要是txt格式!】

images

某个游戏活动(游戏早已废弃不存在,所以公开),通过小程序或者APP扫描二维码进入游戏,过程中发现,这游戏其实就是一个H5页面,地址是:

https://w.baidu.com/game-football/index.html?naviType=4

游戏作弊访问方式解析

通过微信小程序、APP都可以正常玩这款游戏,但是如果通过浏览器打开,则提示“请在小程序或APP中打开”,既然有提示就好办了,我们通过搜索发现,真正校验名字是checkRunEnable

images

这里既然是一个判断函数,那么看看isHzwEnv(),我们直接跟进,如下图:

images

从这里可以看出里面有一个“或”的判断,他们俩任意为真则可以继续。this.isHzwApp() || this.isWechatMP()到这里就比较简单了,跟进一下这两个任意一个函数即可。为了方便,直接在isHzwApp这个函数里面加一个条件,让i、e、t都满足条件为真即可。

这里的判断逻辑就过去了,然后可以通过网页玩此H5游戏了,这里最好还是要找一下具体引用checkRunEnable此判断逻辑的点,方便后续游戏作弊分析,具体引用点如下图:

images

其实只要把所有引用的点都加入“断点”即可,这个还是比较容易的。后续的判断比较简单,就是判断还可以玩多少次,这个游戏有限制次数。不过这个都无所谓了。然后就是进入startGame()函数里面,开始游戏。

分析游戏结果处理逻辑

然后开始玩游戏,过程中一直在抓包,这个游戏并没有采取websocket的时时获取数据模式,游戏过程中也没有进行数据交互,那么最终玩完游戏时才提交的数据包,具体如下:

images

通过上面的分析,加上这个数据包的提交,可以得出结论,游戏肯定可以作弊,因为所有的数据都是本地生成,最终加密提交到服务端。那么也就是说,我们可以通过多种方法作弊:

第一:hook关键点,在加密之前修改游戏结果,然后放行,系统加密修改后的数据传到服务端;

第二:找到整体加密方法,分析加密逻辑,然后自行写作弊脚本。

整体来说,第一个方法简单,第二个方法耗时耗力比较难。

既然写文章了,那么就两个方法都来一遍!!!

hook关键点,修改游戏结果作弊 (简单)

既然从本地提交数据到服务端,必然有提交入口,通过提交数据包,可以找关键点,例如关键词rank/join这就是一个,通过搜索最终发现结果如下图:

images

提交的结果是e,那么,从图中也可以明确的知道,此提交数据入口在函数join中,e的值也是清晰可见

var e = {
                        t: r.aes.aesGameScore(t)
                    };

这就很明确了,直接告诉你是用的aes加密的。 从图中也可以明确知道,应该是有一个数据结果t,此结果放入r.aes.aesGameScore(t)函数中,先hook一下看看入参t具体是什么。在这里hook一下断点,然后玩游戏,结果如下图:

images

很明显,这个t承着的就是游戏结果(游戏时长),那么可以直接通过修改此时t的值,然后放行,即可完成游戏作弊。如下图:

images

然后放行,然后如下图:

images

最终我们得到游戏的加密结果0929651bf9b6f18c3fe5983ebc95c99403811395504088afd971fcd66392deccf191064b497716bdf0609bb627a37cd8515b1def190fc81242db51403aca9ff5f93325a17a609445e15cdb1bbe63197d,这也是最简单的作弊方式,到此也算是作弊完成。最终完全放行即可。

分析游戏加密逻辑,制作游戏作弊脚本 (较难)

如果仅仅是为了游戏作弊,那么完成上述步骤即可,但是本着技术领域的探索,可以继续深究。既然aesGameScore函数就是加密游戏结果的,那么跟进此函数。具体代码如下:

 

execute: function() {
            function i(t, e, r) {
                var i = n
                  , c = i.enc.Utf8.parse(e)
                  , s = i.enc.Utf8.parse(r);
                return i.AES.encrypt(t, c, {
                    iv: s,
                    mode: i.mode.CBC,
                    padding: i.pad.PKCS7
                }).ciphertext.toString()
            }
            t({
                aesEncrypt: i,
                aesGameScore: function(t) {
                    var e = parseInt(((new Date).getTime() / 1e3).toString())
                      , n = (Array(7).join("0") + Math.floor(t).toString()).slice(-6)
                      , c = e + n
                      , s = r.cookie().uid
                      , o = (s.toString() + Array(17).join("0")).slice(0, 16)
                      , a = {
                        uid: parseInt(s),
                        timestamp: e,
                        score: Math.round(1e3 * t)
                    };
                    return i(o + JSON.stringify(a), "KcghK8WtBefxP9da", c)
                }
            }),
            e._RF.push({}, "b5c6b/CEFRKA6jsC8yvqFIG", "AES128", void 0),
            e._RF.pop()
        }

这里就需要一步一步的对加密函数进行分析。
e = parseInt(((new Date).getTime() / 1e3).toString())此步骤是获取当前时间戳,舍弃小数点后面,仅保留整数。
n = (Array(7).join("0") + Math.floor(t).toString()).slice(-6)取游戏结果整数,舍弃小数点后面,如果不足6位前面用0补齐。
c = e + n组合时间戳及游戏结果。
s = r.cookie().uid从cookie中获取当前用户uid。
o = (s.toString() + Array(17).join("0")).slice(0, 16)取用户uid,不足16位后面用0补齐。
a = {uid: parseInt(s),timestamp: e,score: Math.round(1e3 * t)};组合所需加密数据为字典格式。
i(o + JSON.stringify(a), "KcghK8WtBefxP9da", c)最终把数据再次进行组合,放入加密函数i中。
这里有一个点需要注意,就是加密结果.toString()需要通过JS变为字符格式,如果要是用python写的话就是hex
具体经过分析数据结果如下:

------------------------------
①
1669797575  时间戳 
-----------------------------
②
t = 2.2731666666666657	##这里仅取小数点后3位
(Array(7).join("0") + Math.floor(t).toString()).slice(-6)
000002		游戏分数取整值,取小数点当前分数6位
-----------------------------
③=①+②
1669797575000002	偏移量(时间戳+游戏分数取值)
-----------------------------
④
1234567890		uid
-----------------------------
⑤
s.toString() = 1234567890
(s.toString() + Array(17).join("0")).slice(0, 16)
1234567890000000	用户uid补全数据
------------------------------
⑥=⑤+{uid: ④, timestamp: ①, score: 2273}
最终需要加密的数据格式:
1234567890000000{"uid":1234567890,"timestamp":1669797575,"score":2274}

通过断点也可以看到,如下图:

images

由此可见,分析结果与预期一致,那么下一步就是根据加密逻辑,写作弊python脚本。

具体脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# -*- coding:utf-8 -*-
# __author__ = xieyunlong
# __Blog__ = https://woj.app
# __Date__ = 2022/6/10
# __ver__ = python3
# !/usr/bin/env python3
# -*- coding:utf-8 -*-
# AES/ECB/PKCS7Padding 加密解密
# 环境需求:
# pip3 install pycryptodome
 
from Crypto.Cipher import AES
from Crypto.Util import Padding
from base64 import b64encode, b64decode
from binascii import b2a_hex, a2b_hex
import time
import requests
 
 
def aes_ecb_pkcs7_b64_encrypt(data, key, iv):
    data = Padding.pad(data.encode('utf8'), AES.block_size, 'pkcs7')
    aes = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
    return b2a_hex(aes.encrypt(data))
 
 
def aes_ecb_pkcs7_b64_decrypt(data, key):
    data = b64decode(data.encode('utf-8'))
    aes = AES.new(key.encode('utf-8'), AES.MODE_CBC)
    return Padding.unpad(aes.decrypt(data), AES.block_size, 'pkcs7').decode('utf-8')
 
 
def connect_data(end_time, _game_souce):
    key = "KcghK8WtBefxP9da"
    time_ux = int(end_time)  # 游戏时间戳
    print(_game_souce)
    # 游戏分数默认6位,最多9位,不够前面用0补齐,后3位为小数点,2.123秒=002123,123456秒=123456000,后3位0是毫秒
    game_souce = '{:.3f}'.format(_game_souce)  # 必须是浮点数,小数点后面必须有值
    if '.' in str(game_souce):
        game_souce = game_souce.split('.')
        game_souce_1 = game_souce[0]
        if len(game_souce_1) < 6: game_souce_3 = '{:0>6d}'.format(int(game_souce_1))
        else:
            game_souce_3 = game_souce_1[-6:]
        # game_souce_2 = game_souce[1][:3]
        game_souce = '{}{:x<3d}'.format(game_souce_1, int(game_souce[1]))  # 这里int(game_souce[1]) 可以更换为game_souce_2
    iv = '{}{}'.format(time_ux, game_souce_3)
    print(iv)
    user_uid = 1039666656  ##用户uid
    user_uid_all = "{}000000".format(user_uid)  # 用户uid补全
    data_connect = '{user_uid_all}{{"uid":{user_uid},"timestamp":{time_ux},"score":{game_souce}}}'.format(
        user_uid_all=user_uid_all[:16], user_uid=user_uid, time_ux=time_ux, game_souce=game_souce)
    print(data_connect)
    t_data = aes_ecb_pkcs7_b64_encrypt(data_connect, key, iv)
    return t_data
 
 
session = requests.Session()
start_time = time.time()
headers = {"Origin": "https://123123.xxxx.com",
           "Sec-Ch-Ua": "\"Google Chrome\";v=\"107\", \"Chromium\";v=\"107\", \"Not=A?Brand\";v=\"24\"",
           "Accept": "application/json, text/plain, */*", "Sec-Ch-Ua-Platform": "\"Android\"",
           "User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36",
           "Referer": "https://123123.xxxx.com/", "Connection": "close", "Sec-Fetch-Site": "same-site",
           "Sec-Fetch-Dest": "empty", "Accept-Encoding": "gzip, deflate", "Sec-Fetch-Mode": "cors",
           "Accept-Language": "zh,zh-CN;q=0.9,en-US;q=0.8,en;q=0.7", "Sec-Ch-Ua-Mobile": "?1",
           "Content-Type": "application/json;charset=UTF-8"}
cookies = {"uid": "123123", "hserecomkey": ""}
paramsGet = {"sn": "XMTD5KXVRHUXE7dz"}
 
# 第一步,记录游戏开始时间
response = session.post("https://123123123.xxxx.com/api/v1/rank-reward/count/reduce", data='null',
                        params=paramsGet, headers=headers, cookies=cookies)
print(response.json())
 
# 第二步,玩了多久就等多久
time.sleep(678)  ##需要游戏多数分 这里就休息多数秒
 
end_time = time.time()
 
# 处理结果并加密
t_data = connect_data(end_time, end_time - start_time)
 
# rawBody = {"t":t_data.decode('utf-8')}
rawBody = '{{"t":"{t_data}"}}'.format(t_data=t_data.decode('utf-8'))
print(rawBody)
# 第三步,提交加密数据
response = session.post("https://123123.xxxx.com/api/v1/rank-reward/rank/join", data=rawBody, params=paramsGet,
                        headers=headers, cookies=cookies)
 
print("Status code:   %i" % response.status_code)
print("Response body: %s" % response.json())

布施恩德可便相知重

微信扫一扫打赏

支付宝扫一扫打赏

×

给我留言