微信 “跳一跳” 分析筆記

Editor發表於2018-01-05

0x00 引子

目前網上已經有模擬發包上傳分數的方案了,圖形識別和人肉丈量都是不錯的選擇,此外,也有基於安卓 adb 實現的。下面給出一些這方面的分析,沒別的意思,就是純粹好玩。

0x01 抓包

抓包通常可以用 Charles 或者 Fiddler 抓小程式 https 資料,這裡說下另一種方法,從安卓程式碼入手,找到 https 明文發包點擷取封包。

從微信的 log 中看到,每次遊戲發包時都會列印 "AppBrandNetworkRequest",從 Tag 命名上猜測這是小程式程式碼通過微信 sdk 發包的介面,反編譯微信根據該關鍵詞定位到負責小程式 https 通訊的類是 Lcom/tencent/mm/plugin/appbrand/i/c;。

分析下程式碼可以發現最後使用過 Lcom/tencent/mm/sdk/f/e;->post 發包的,列印呼叫這行程式碼的函式的各個引數就可以擷取小程式通過 https 傳送的 json 明文資料了。

0x02 改包

在 1 中明文發包點可以直接修改傳送的資料,但是敏感資料是加密通訊的,比如上傳分數的介面 https://mp.weixin.qq.com/wxagame/wxagame_settlement 中 action_data 欄位就是在小程式內加密後再通過微信發出去的。

既然資料是在本地加密的,加密演算法肯定也在本地可以找到,所以"跳一跳"作弊的關鍵就是找到小程式原始碼。

0x03 尋找小程式原始碼

從微信小程式文件中可以知道,小程式的核心邏輯通常是用 js 寫的。搜尋手機中的檔案並沒有找到字尾為 ".js" 的原始碼,原始碼只能從記憶體中 dump 了。

微信 log 中搜尋 ".js",可以發現有如下 log 列印:

微信 “跳一跳” 分析筆記

從關鍵詞 WxaPkgRuntimeReader 可以猜測這是 wxapp 原始碼載入的相關程式碼,通過該關鍵詞反編譯微信定位到載入 js 程式碼的類:Lcom/tencent/mm/plugin/appbrand/appcache/ai;

分析程式碼可知該類下的 public static String a(e eVar, String str) 方法返回的就是js原始碼,把返回字串 dump 下來得到 "跳一跳" 核心原始碼 game.js。

0x04 實現作弊功能

有了原始碼,就可以根據上傳分數的相關程式碼模擬上報分數了:

{

    key: "requestSettlement",

    value: function() {

        var t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : 0,

        e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : 0,

        i = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : function() {},

        n = arguments.length > 3 && void 0 !== arguments[3] ? arguments[3] : {};

        if (a.default.sessionId) {

            var r = {

                score: t,

                times: e,

                game_data: JSON.stringify(n)

            },

            o = {

                base_req: {

                    session_id: a.default.sessionId,

                    fast: 1

                },

                action_data: (0, s.encrypt)(r, a.default.sessionId)

            };

            wx.request({

                url:

                h.AJAX_URL + "/wxagame/wxagame_settlement",

                method: "POST",

                data: o,

                success: function(t) {

                    i(200 === t.statusCode ? 0 === t.data.base_resp.errcode ? !0 : !1 : !1)

                },

                fail: function(t) {

                    i(!1)

                }

            })

        } else i(!1)

    }

},

加密部分:

e.encrypt = function(t, e) {

    var e = e.slice(0, 16),

    i = n.default.enc.Utf8.parse(e),

    r = n.default.enc.Utf8.parse(e),

    a = t;

    a = JSON.stringify(a);

    var o = n.default.AES.encrypt(a, i, {

        iv: r,

        mode: n.default.mode.CBC,

        padding: n.default.pad.Pkcs7

    });

    return o = o.toString()

};

既然原始碼在手,直接修改原始碼就可以實現作弊功能了。實現的方法是修改 3 中的 js 原始碼載入函式的返回字串,替換相應 js 程式碼。

比如修改每次跳躍加分 32,把 "this.score+=t" 替換成 "this.score+=32" 等等。

0x05 基於觸動精靈來實現

想到了下面的識別辦法:

1. 逐行進行掃描來識別要跳轉的目標座標。為了提高效率可以適當增加掃描步進。定義一個矩形區域,要跳轉的目標相對來說位置都比較固定。

2. 獲取小人的位置,通過觸動精靈的查詢顏色功能進行定位座標,雖然有一定誤差,但是隻要能獲取到座標,用來計算還是基本沒問題的。

3. 計算跳躍距離,通過直接三角形的勾股定理進行計算。按壓時間需要根據距離進行修正,我在小米 5s上測試用的1.3 基本還算可以。

已知問題:

1. 通過觸動精靈進行顏色匹配搜尋座標的做法效率較低,需要比較長的時間。

2. 執行一段時間之後,找色函式和獲取小人座標的函式會發生錯誤,導致無法獲取到真正的座標。我加了幾個判斷,出現問題的時候直接重新啟動指令碼就可以了。

3. 由於是基於顏色進行匹配的,因而相對來時識別的座標的準確度比上面的python版本要低很多。

改進方式:

1. 針對搜尋座標的函式進行匹配,折半查詢,如果小人在左側,直接搜尋右側。如果小人在右側直接搜尋左側。

2. 匹配到錯誤之後直接重啟指令碼,使用觸動精靈的迴圈執行功能

3. 其他未知的功能修改?我也不知道有啥。哈哈

指令碼檔案:

require "TSLib"

require("math")

-----------------------------------------------

-- Auto jump scripy

-- Code by obaby

-- http://www.h4ck.org.cn

-- Findu App http://findu.co

-----------------------------------------------

-- define scan zone with (x1,y1) (x2,y2)

scanZone_x1 = 50;

scanZone_y1 = 600;

scanZone_x2 = 1000;

scanZone_y2 = 922;

-- get the target object position

function getDestXY()

    -- body

    mSleep(1000);

    isfound = 0;

    dest_x = 0;

    dest_y = 0;

    for y = scanZone_y1,scanZone_x2,30 do

        for x =scanZone_x1,scanZone_x2,30 do

            colorb = getColor(x, y)

            colorc = getColor(x-30, y)

            colord = getColor(x+50, y)

            delta = math.abs(colorb -colorc)

            delta2 = math.abs(colord - colorb)

            --toast(delta.." :x:"..x.." :y:"..y,1)

            --mSleep(100)

            if delta >1000 then

                --toast(delta.." :x:"..x.." :y:"..y,1)

                nLog("COLO TO::ColorB:"..colorb.." ColorC:"..colorc);

                isfound = 1;

                dest_x = x;

                dest_y = y;

                --mSleep(5000);

                break;

            end

            --dialog("ColorB:"..colorb.."ColorC:"..colorc, 3);

            --mSleep(3000)

            --x= x+10

        end

        --y= y+10

        if isfound ==1 then

            break;

        end

    end

    return dest_x, dest_y

end

-- get the

function getDistance(dest_x, dest_y)

    -- body

    --mSleep(1000);

    x, y = findColorInRegionFuzzy( 0x39375f , 80, 0, 926, 1070, 1370);

    if x == -1 then

        x, y = findColorInRegionFuzzy( 0x39375f , 70, 0, 926, 1070, 1370);

    end

    if x == -1 then

        x, y = findColorInRegionFuzzy( 0x39375f , 80, 0, 926, 1070, 1370);

    end

    if x ==-1 then

        return 0;

    end

    nLog("JUMP FR::src_x:"..x.." src_y:"..y);

    distance = math.sqrt(math.pow(dest_x - x,2) + math.pow(dest_y-y,2));

    if math.abs(dest_y - y) < (1116-940) then

        return (1116-940)*1.4;

    end

    return distance;

end

while (true) do

    -- body

    if (isColor( 581, 1626, 0xffffff, 85) and  isColor( 558, 1714, 0x262628, 85)) then

        break;

    end

    dest_x , dest_y = getDestXY();

    dist = getDistance(dest_x,dest_y);

    toast("dest_x:"..dest_x.." dest_y:"..dest_y.." distance:"..math.floor(dist),3);

    --toast(dist,1)

    --can not get dest position or can not get the source position

    nLog("JUMP TO::dst_x:"..dest_x.." dst_y:"..dest_y.." distance:"..math.floor(dist));

    if dest_x ==scanZone_x1 or dist == 0 or dest_y == scanZone_y1 or dest_x ==scanZone_x2 then

        toast("Get posison error",1);

        nLog("ERRO ER:: Get position error")

        break;

    end

    touchDown(dest_x, dest_y);

    mSleep(dist*1.3);

    touchUp(dest_x, dest_y);

end

微信 “跳一跳” 分析筆記

本文由看雪論壇 洪荒之力&obaby 原創 轉載請註明來自看雪社群

相關文章