分享一次微信小程式的介面破解

alienwu 發表於 2020-09-23

本人是一個遊戲測試小菜雞,平時十分喜歡玩騰訊的一款老端遊QQ三國,有一次巧遇發現了一款對我體驗十分友好的小程式《三國查詢》,然後我想破解它。
先介紹一下這款小程式的功能:
1.提供快速查詢遊戲內道具擺攤/商店販賣的具體座標;
2.提供清晰知道該道具的詳細屬性;
我遇到了這款小程式後就愛上了它,它使我玩遊戲變得很便捷。但是我也是個老破壞家了,遇到一款遊戲,一款軟體總的想弄它,於是就萌生出破解這款小程式的想法了。
接著來分享破解這款小程式的歷程:
1).獲取到該款小程式的包:
在夜神模擬器中下載微信並登入微信,然後搜尋目標小程式,載入目標小程式。在載入完成後,你的本地就會生成一個微信小程式的包檔案xx.wxapkg,然後就講該包檔案發到電腦本地。具體路徑在夜神模擬器的:/data/data/com.tencent.mm/MicroMsg/...(按時間排序,一般情況下是一個資料夾)/appbrand/pkg/xx.wxapkg分享一次微信小程式的介面破解圖中有4個wxapkg檔案,一般來說最大的那個就是目標的小程式,不太確定哪個是目標的包檔案可以全部拉到pc本地後逐一反編譯排除即可。
2).獲取到源包檔案後接下來就是對小程式包檔案進行反編譯:
關於微信小程式的反編譯教程網上有很多,這裡只作簡單的介紹。
我這裡是用的是wxappUnpacker來對小程式進行反編譯。
node wuWxapkg.js [-d] 執行命令後即可反編譯出微信小程式的原始碼。
分享一次微信小程式的介面破解
3).分析小程式原始碼中的協議:
經過檢視發現這款小程式的在app-service.js上,破解一個介面的步驟分為就是破解介面中的請求引數,請求頭,返回的響應體內容。
首先,使用了fiddler進行抓包分析介面,經過分析發現介面的主要引數為:clientGuid,sign,token,破解這三個引數後即可構造請求。
請求體內容詳情如下圖:
分享一次微信小程式的介面破解
這個作者對自己的作品做了很嚴謹的介面保護,響應體也進行了加密,可想而知介面資料的重要性。
響應內容詳情如下圖:
分享一次微信小程式的介面破解
最後是開啟app-service.js找到並分析具體引數的構造過程,接著就是分析請求的內容。這個過程就不作多介紹了,靠個人操作~
4).編寫破解程式碼:
關於clientGuid請求引數:
從原始碼中我找到了clientGuid引數的具體生成過程:

function uuid() {
for (var e = [], r = 0; r < 36; r++)
e[r] = "0123456789abcdef".substr(Math.floor(16 * Math.random()), 1);
e[14] = "4",
e[19] = "0123456789abcdef".substr(3 & e[19] | 8, 1),
e[8] = e[13] = e[18] = e[23] = "-";
var t = e.join("");
return t
}

該函式就是構建clientGuid的函式。
關於token請求引數:
這個毋庸置疑,是使用者登入的令牌,這個沒辦法在客戶端原始碼中獲取,只有登入的使用者才擁有,獲取token最直接的辦法就是抓包從fiddler中獲取。

關於sign簽名引數:

function n(n, r) {
var t = (65535 & n) + (65535 & r);
return (n >> 16) + (r >> 16) + (t >> 16) << 16 | 65535 & t
}
function r(n, r) {
return n << r | n >>> 32 - r
}
function t(t, e, u, o, c, f) {
return n(r(n(n(e, t), n(o, f)), c), u)
}
function e(n, r, e, u, o, c, f) {
return t(r & e | ~r & u, n, r, o, c, f)
}
function u(n, r, e, u, o, c, f) {
return t(r & u | e & ~u, n, r, o, c, f)
}
function o(n, r, e, u, o, c, f) {
return t(r ^ e ^ u, n, r, o, c, f)
}
function c(n, r, e, u, o, c, f) {
return t(e ^ (r | ~u), n, r, o, c, f)
}
function f(r) {
for (var t = 1732584193, f = -271733879, i = -1732584194, a = 271733878, h = 0; h < r.length; h += 16) {
var l = t
, g = f
, v = i
, d = a;
f = c(f = c(f = c(f = c(f = o(f = o(f = o(f = o(f = u(f = u(f = u(f = u(f = e(f = e(f = e(f = e(f, i = e(i, a = e(a, t = e(t, f, i, a, r[h + 0], 7, -680876936), f, i, r[h + 1], 12, -389564586), t, f, r[h + 2], 17, 606105819), a, t, r[h + 3], 22, -1044525330), i = e(i, a = e(a, t = e(t, f, i, a, r[h + 4], 7, -176418897), f, i, r[h + 5], 12, 1200080426), t, f, r[h + 6], 17, -1473231341), a, t, r[h + 7], 22, -45705983), i = e(i, a = e(a, t = e(t, f, i, a, r[h + 8], 7, 1770035416), f, i, r[h + 9], 12, -1958414417), t, f, r[h + 10], 17, -42063), a, t, r[h + 11], 22, -1990404162), i = e(i, a = e(a, t = e(t, f, i, a, r[h + 12], 7, 1804603682), f, i, r[h + 13], 12, -40341101), t, f, r[h + 14], 17, -1502002290), a, t, r[h + 15], 22, 1236535329), i = u(i, a = u(a, t = u(t, f, i, a, r[h + 1], 5, -165796510), f, i, r[h + 6], 9, -1069501632), t, f, r[h + 11], 14, 643717713), a, t, r[h + 0], 20, -373897302), i = u(i, a = u(a, t = u(t, f, i, a, r[h + 5], 5, -701558691), f, i, r[h + 10], 9, 38016083), t, f, r[h + 15], 14, -660478335), a, t, r[h + 4], 20, -405537848), i = u(i, a = u(a, t = u(t, f, i, a, r[h + 9], 5, 568446438), f, i, r[h + 14], 9, -1019803690), t, f, r[h + 3], 14, -187363961), a, t, r[h + 8], 20, 1163531501), i = u(i, a = u(a, t = u(t, f, i, a, r[h + 13], 5, -1444681467), f, i, r[h + 2], 9, -51403784), t, f, r[h + 7], 14, 1735328473), a, t, r[h + 12], 20, -1926607734), i = o(i, a = o(a, t = o(t, f, i, a, r[h + 5], 4, -378558), f, i, r[h + 8], 11, -2022574463), t, f, r[h + 11], 16, 1839030562), a, t, r[h + 14], 23, -35309556), i = o(i, a = o(a, t = o(t, f, i, a, r[h + 1], 4, -1530992060), f, i, r[h + 4], 11, 1272893353), t, f, r[h + 7], 16, -155497632), a, t, r[h + 10], 23, -1094730640), i = o(i, a = o(a, t = o(t, f, i, a, r[h + 13], 4, 681279174), f, i, r[h + 0], 11, -358537222), t, f, r[h + 3], 16, -722521979), a, t, r[h + 6], 23, 76029189), i = o(i, a = o(a, t = o(t, f, i, a, r[h + 9], 4, -640364487), f, i, r[h + 12], 11, -421815835), t, f, r[h + 15], 16, 530742520), a, t, r[h + 2], 23, -995338651), i = c(i, a = c(a, t = c(t, f, i, a, r[h + 0], 6, -198630844), f, i, r[h + 7], 10, 1126891415), t, f, r[h + 14], 15, -1416354905), a, t, r[h + 5], 21, -57434055), i = c(i, a = c(a, t = c(t, f, i, a, r[h + 12], 6, 1700485571), f, i, r[h + 3], 10, -1894986606), t, f, r[h + 10], 15, -1051523), a, t, r[h + 1], 21, -2054922799), i = c(i, a = c(a, t = c(t, f, i, a, r[h + 8], 6, 1873313359), f, i, r[h + 15], 10, -30611744), t, f, r[h + 6], 15, -1560198380), a, t, r[h + 13], 21, 1309151649), i = c(i, a = c(a, t = c(t, f, i, a, r[h + 4], 6, -145523070), f, i, r[h + 11], 10, -1120210379), t, f, r[h + 2], 15, 718787259), a, t, r[h + 9], 21, -343485551),
t = n(t, l),
f = n(f, g),
i = n(i, v),
a = n(a, d)
}
return [t, f, i, a]
}
function i(n) {
for (var r = "", t = 0; t < 4 * n.length; t++)
r += "0123456789abcdef".charAt(n[t >> 2] >> t % 4 * 8 + 4 & 15) + "0123456789abcdef".charAt(n[t >> 2] >> t % 4 * 8 & 15);
return r
}
function a(n) {
for (var r = 1 + (n.length + 8 >> 6), t = new Array(16 * r), e = 0; e < 16 * r; e++)
t[e] = 0;
for (e = 0; e < n.length; e++)
t[e >> 2] |= (255 & n.charCodeAt(e)) << e % 4 * 8;
return t[e >> 2] |= 128 << e % 4 * 8,
t[16 * r - 2] = 8 * n.length,
t
}

//具體呼叫生成sign方法
var str="clientGuid=" + clientGuid + "&clientTimestamp=" + clientTimestamp + "&key=6JFzFFN5527IYdDf16VlBxErt96NTX18"
var sign =i(f(a(str)))

這裡解釋一下,sign的生成是呼叫一個重重呼叫的複雜的計算函式,這個不用多管,其目標是接收一個字串,該字串的形成為:
var str="clientGuid=" + clientGuid + "&clientTimestamp=" + clientTimestamp + "&key=6JFzFFN5527IYdDf16VlBxErt96NTX18"
然後呼叫方法var sign =i(f(a(str)))即可生成sign。我只能說這個作者有點呆萌,我第一次見人這麼直白的生成sign,毫無破解壓力。

請求引數經全部破解,這時可以構造請求了,但是這裡有個坑,就是作者對請求頭作了檢測(不太確定是微信自帶的檢測,還是作者自己設定的),請求頭必須帶欄位type:wechat方可訪問介面。
請求頭詳情如下:

POST /qqsg/boothList HTTP/1.1
Host: qqsg.pc9527.vip:3001
Connection: keep-alive
Content-Length: 366
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 MicroMessenger/7.0.9.501 NetType/WIFI MiniProgramEnv/Windows WindowsWechat
content-type: application/json
type: weChat
Accept-Encoding: gzip, deflate, br

關於響應體data的破解:
請求成功後以為一切大功告成,其實不然,作者太愛惜自己的介面了,對響應體也進行了加密,經分析原始碼發現原來用的AES加密,於是後面就是一連串的破解編碼:
最後對data解碼成功:
分享一次微信小程式的介面破解

到這裡整個破解過程已經結束了~

結語:
記一個愛搗蛋愛搞破壞的遊戲點點點菜雞的一次破解微信小程式介面分享~
(最後我的賬號已經被作者永久封禁了┭┮﹏┭┮。哼,你也是吃灰色產業的錢,你別逞強)
破解介面的好處這裡就不多說了,最直接的一個操作就是套殼,偽造同款軟體。直接盜用介面資料來進行低價出售服務來盈利,對方再怎麼作流量監控,訪問頻率控制都是沒用的,多購買幾個對方的賬號,IP,+redis快取訪問即可解決這個問題。當然我並沒有這個心思做這個事情,也堅持自己的底線不會做這個事情。畢~

QQ三國真好玩,(●'◡'●)

破解專案連結:
https://github.com/alienwu2018/Crack-QQSGSearch.git