微信小程序-某某牛仔城
本文中所有内容仅供研究与学习使用,禁止用于任何商业用途和非法用途,否则后果自负!!!
本章来分析一个微信小程序的协议,这个小程序的协议本身比较简单。主要是来学习一下分析微信小程序的流程
0x00 环境
设备:pixel 5 Android11.0
抓包:Charles + Postern
发包工具:postman、APIfox
0x01 抓包
环境配置好了,那么先来抓个包吧
随便选一个领取,这里我们以预览充值中心为例
抓到的包比较多,找不到多翻翻
分析这个协议
member_no:会员编号
type:操作目标的类型,例如这里33是“每日预览充值中心”
store_no:字面翻译是商店编号,这里我们抓了几次包都没变过。暂时当它是个固定值
sign:签名是本篇文章要分析的一个重点,看起来像是一个md5值
time:时间戳
- 经过分析,看起来只有这个
sign
需要我们进行分析,其它的都可以直接获取或者是固定值
- 经过分析,看起来只有这个
0x02 分析小程序源码
定位
首先定位小程序的包
因为路径下小程序文件夹太多了,先清理一下
然后重新打开一遍小程序,用everything搜索.wxapkg
定位时间最近那个
解包
小程序的解包工具有很多,在github上都可以找到。这里我们使用这个带界面的看起来比较清楚
解密成功后拖入wxapkgconvertor.exe ,反编译
- 反编译成功之后用IDE打开,这里我们使用微信开发者工具打开
- PS:里面有一个
app-service.js
文件特别大,建议复制下来在网上找工具格式化一下。本机直接格式化容易卡死
源码分析
根据前面对抓包协议的分析,我们只需要分析sign
怎么获取的就好了。那就直接搜索sign
- 运气比较好,第三个直接定位到了获取签名的函数
- 其中
var u = a.default.hexMD5(n + t + w + m + v + p);
把一系列值组合然后进行了DM5计算,这就是我们要找的sign
计算sign
把代码抠出来,自己重新计算一下获取sign
- 这里用到了一个
hexMD5
的外部方法,搜索一下把这个方法也抠出来
- 这里又用到了一个
o
方法,也找到抠出来
- 这里一堆互相调用的都抠出来
function getxSign() {
var d = "511MrfpKD^",
h = "GKXW58GHG",
p = "#iki0Dhr",
g = "ASF74XSR^",
v = "@UnOaJYS",
m = "KIoa7YXIaN",
y = "648DCGFG^",
w = "511MrfpK^";
var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
t = arguments.length > 1 ? arguments[1] : void 0,
r = e.query || {},
n = "";
console.log("getSign.times===>"), console.log(t), console.log("getSign.data===>"), console.log(r);
for (var o = Object.keys(r).sort(), i = {}, s = 0; s < o.length; s++) i[o[s]] = r[o[s]];
for (var l in console.log("data排序后===>"), console.log(i), i) n += l + i[l];
var u = a.default.hexMD5(n + t + w + m + v + p);
return console.log("getSign.sign===>"), console.log(u), u
}
function hexMD5(e) {
return function(e) {
for (var t = "0123456789abcdef", r = "", n = 0; n < 4 * e.length; n++) r += t.charAt(e[n >> 2] >> n % 4 * 8 + 4 & 15) + t.charAt(e[n >> 2] >> n % 4 * 8 & 15);
return r
}(function(e) {
for (var t = 1732584193, n = -271733879, l = -1732584194, u = 271733878, c = 0; c < e.length; c += 16) {
var f = t,
d = n,
h = l,
p = u;
t = o(t, n, l, u, e[c + 0], 7, -680876936), u = o(u, t, n, l, e[c + 1], 12, -389564586), l = o(l, u, t, n, e[c + 2], 17, 606105819), n = o(n, l, u, t, e[c + 3], 22, -1044525330), t = o(t, n, l, u, e[c + 4], 7, -176418897), u = o(u, t, n, l, e[c + 5], 12, 1200080426), l = o(l, u, t, n, e[c + 6], 17, -1473231341), n = o(n, l, u, t, e[c + 7], 22, -45705983), t = o(t, n, l, u, e[c + 8], 7, 1770035416), u = o(u, t, n, l, e[c + 9], 12, -1958414417), l = o(l, u, t, n, e[c + 10], 17, -42063), n = o(n, l, u, t, e[c + 11], 22, -1990404162), t = o(t, n, l, u, e[c + 12], 7, 1804603682), u = o(u, t, n, l, e[c + 13], 12, -40341101), l = o(l, u, t, n, e[c + 14], 17, -1502002290), t = i(t, n = o(n, l, u, t, e[c + 15], 22, 1236535329), l, u, e[c + 1], 5, -165796510), u = i(u, t, n, l, e[c + 6], 9, -1069501632), l = i(l, u, t, n, e[c + 11], 14, 643717713), n = i(n, l, u, t, e[c + 0], 20, -373897302), t = i(t, n, l, u, e[c + 5], 5, -701558691), u = i(u, t, n, l, e[c + 10], 9, 38016083), l = i(l, u, t, n, e[c + 15], 14, -660478335), n = i(n, l, u, t, e[c + 4], 20, -405537848), t = i(t, n, l, u, e[c + 9], 5, 568446438), u = i(u, t, n, l, e[c + 14], 9, -1019803690), l = i(l, u, t, n, e[c + 3], 14, -187363961), n = i(n, l, u, t, e[c + 8], 20, 1163531501), t = i(t, n, l, u, e[c + 13], 5, -1444681467), u = i(u, t, n, l, e[c + 2], 9, -51403784), l = i(l, u, t, n, e[c + 7], 14, 1735328473), t = a(t, n = i(n, l, u, t, e[c + 12], 20, -1926607734), l, u, e[c + 5], 4, -378558), u = a(u, t, n, l, e[c + 8], 11, -2022574463), l = a(l, u, t, n, e[c + 11], 16, 1839030562), n = a(n, l, u, t, e[c + 14], 23, -35309556), t = a(t, n, l, u, e[c + 1], 4, -1530992060), u = a(u, t, n, l, e[c + 4], 11, 1272893353), l = a(l, u, t, n, e[c + 7], 16, -155497632), n = a(n, l, u, t, e[c + 10], 23, -1094730640), t = a(t, n, l, u, e[c + 13], 4, 681279174), u = a(u, t, n, l, e[c + 0], 11, -358537222), l = a(l, u, t, n, e[c + 3], 16, -722521979), n = a(n, l, u, t, e[c + 6], 23, 76029189), t = a(t, n, l, u, e[c + 9], 4, -640364487), u = a(u, t, n, l, e[c + 12], 11, -421815835), l = a(l, u, t, n, e[c + 15], 16, 530742520), t = s(t, n = a(n, l, u, t, e[c + 2], 23, -995338651), l, u, e[c + 0], 6, -198630844), u = s(u, t, n, l, e[c + 7], 10, 1126891415), l = s(l, u, t, n, e[c + 14], 15, -1416354905), n = s(n, l, u, t, e[c + 5], 21, -57434055), t = s(t, n, l, u, e[c + 12], 6, 1700485571), u = s(u, t, n, l, e[c + 3], 10, -1894986606), l = s(l, u, t, n, e[c + 10], 15, -1051523), n = s(n, l, u, t, e[c + 1], 21, -2054922799), t = s(t, n, l, u, e[c + 8], 6, 1873313359), u = s(u, t, n, l, e[c + 15], 10, -30611744), l = s(l, u, t, n, e[c + 6], 15, -1560198380), n = s(n, l, u, t, e[c + 13], 21, 1309151649), t = s(t, n, l, u, e[c + 4], 6, -145523070), u = s(u, t, n, l, e[c + 11], 10, -1120210379), l = s(l, u, t, n, e[c + 2], 15, 718787259), n = s(n, l, u, t, e[c + 9], 21, -343485551), t = r(t, f), n = r(n, d), l = r(l, h), u = r(u, p)
}
return [t, n, l, u]
}(function(e) {
for (var t = 1 + (e.length + 8 >> 6), r = new Array(16 * t), n = 0; n < 16 * t; n++) r[n] = 0;
for (n = 0; n < e.length; n++) r[n >> 2] |= (255 & e.charCodeAt(n)) << n % 4 * 8;
return r[n >> 2] |= 128 << n % 4 * 8, r[16 * t - 2] = 8 * e.length, r
}(e)))
function r(e, t) {
var r = (65535 & e) + (65535 & t);
return (e >> 16) + (t >> 16) + (r >> 16) << 16 | 65535 & r
}
function n(e, t, n, o, i, a) {
return r(function (e, t) {
return e << t | e >>> 32 - t
}(r(r(t, e), r(o, a)), i), n)
}
function o(e, t, r, o, i, a, s) {
return n(t & r | ~t & o, e, t, i, a, s)
}
function i(e, t, r, o, i, a, s) {
return n(t & o | r & ~o, e, t, i, a, s)
}
function a(e, t, r, o, i, a, s) {
return n(t ^ r ^ o, e, t, i, a, s)
}
function s(e, t, r, o, i, a, s) {
return n(r ^ (t | ~o), e, t, i, a, s)
}
}
- 最终我们抠出来的代码,反编译的代码比较混乱不好看懂。我们找人工智能工具重写一下
getxSign
函数,计算MD5那个函数就算了没必要重写。
function getxSign(data = {}, timestamp) {
// 定义常量
const d = "511MrfpKD^",
h = "GKXW58GHG",
p = "#iki0Dhr",
g = "ASF74XSR^",
v = "@UnOaJYS",
m = "KIoa7YXIaN",
y = "648DCGFG^",
w = "511MrfpK^";
// 提取查询参数并初始化拼接字符串
const query = data.query || {};
let concatenatedString = "";
// 打印输入的时间戳和数据
console.log("getSign.times===>", timestamp);
console.log("getSign.data===>", query);
// 将对象键按字母顺序排序
const sortedKeys = Object.keys(query).sort();
const sortedData = {};
// 根据排序后的键重新构建对象
sortedKeys.forEach(key => {
sortedData[key] = query[key];
});
// 打印排序后的数据
console.log("data排序后===>", sortedData);
// 构建签名字符串
for (const key in sortedData) {
concatenatedString += key + sortedData[key];
}
// 使用 MD5 计算签名
const signature = hexMD5(concatenatedString + timestamp + w + m + v + p);
// 打印签名结果并返回
console.log("getSign.sign===>", signature);
return signature;
}
- 现在看起来就比较清楚了
接下来我们尝试调用一下,跟抓包的协议比较一下计算的签名是否相同
这里不知道应该怎么传递参数,问一下chatGPT
getxSign()
函数需要两个参数:第一个参数
e
是一个对象,通常包含query
属性,query
是一个键值对对象。这个对象中的键值对会被用于生成签名(sign
)。- **
e.query
**:包含要签名的数据,它的键会被按字母排序,然后与它的值拼接在一起。
例如:
const e = { query: { foo: "value1", bar: "value2" } };
- **
第二个参数
t
是一个时间戳或某种密钥值,它将在签名的生成中用到。这个值会拼接在query
的数据之后,用于生成最终的 MD5 哈希。例如:
const t = "20240916"; // 假设这个是某个时间戳或密钥
调用示例
const e = { query: { foo: "value1", bar: "value2" } }; const t = "20240916"; // 假设这是时间戳或密钥 const sign = getxSign(e, t); console.log("Generated Sign:", sign);
在这个例子中,
getxSign
会对query
对象中的键(bar
,foo
)按字母顺序排序,并与对应的值拼接成字符串,再加上时间戳t
和其他固定值,生成最终的 MD5 哈希签名。
添加调用代码
const e = {
query: {
member_no: 973470,
type: 33,
store_no: 12
}
};
// console.log(getxSign(e, t))
var sign = getxSign(e, "20240916222353") // 时间戳传递协议中的时间戳
- 跟抓包的
sign
对比一下,确定两个sign相同 - 这里计算的结果相同,说明我们正确的获取到了
sign
发包确认
接下来构建一下完整的协议,看看是否能获取服务器的返回结果
发包工具这里尝试了两种常用的工具,postman、APIfox。postman更轻量一些,APIfox的功能更强大更全面一些。用哪个都可以
另外还尝试了使用Python直接发包
postman
请求成功
这里主要有几个地方需要我们设置
改为
POST
在Charles中把URL拷贝过来
因为我们在代码中把最终的
body
保存为了Postman中的query
变量,所以要在Body选项中设置一下填入我们的代码
function hexMD5(e) { return function (e) { for (var t = "0123456789abcdef", r = "", n = 0; n < 4 * e.length; n++) r += t.charAt(e[n >> 2] >> n % 4 * 8 + 4 & 15) + t.charAt(e[n >> 2] >> n % 4 * 8 & 15); return r }(function (e) { for (var t = 1732584193, n = -271733879, l = -1732584194, u = 271733878, c = 0; c < e.length; c += 16) { var f = t, d = n, h = l, p = u; t = o(t, n, l, u, e[c + 0], 7, -680876936), u = o(u, t, n, l, e[c + 1], 12, -389564586), l = o(l, u, t, n, e[c + 2], 17, 606105819), n = o(n, l, u, t, e[c + 3], 22, -1044525330), t = o(t, n, l, u, e[c + 4], 7, -176418897), u = o(u, t, n, l, e[c + 5], 12, 1200080426), l = o(l, u, t, n, e[c + 6], 17, -1473231341), n = o(n, l, u, t, e[c + 7], 22, -45705983), t = o(t, n, l, u, e[c + 8], 7, 1770035416), u = o(u, t, n, l, e[c + 9], 12, -1958414417), l = o(l, u, t, n, e[c + 10], 17, -42063), n = o(n, l, u, t, e[c + 11], 22, -1990404162), t = o(t, n, l, u, e[c + 12], 7, 1804603682), u = o(u, t, n, l, e[c + 13], 12, -40341101), l = o(l, u, t, n, e[c + 14], 17, -1502002290), t = i(t, n = o(n, l, u, t, e[c + 15], 22, 1236535329), l, u, e[c + 1], 5, -165796510), u = i(u, t, n, l, e[c + 6], 9, -1069501632), l = i(l, u, t, n, e[c + 11], 14, 643717713), n = i(n, l, u, t, e[c + 0], 20, -373897302), t = i(t, n, l, u, e[c + 5], 5, -701558691), u = i(u, t, n, l, e[c + 10], 9, 38016083), l = i(l, u, t, n, e[c + 15], 14, -660478335), n = i(n, l, u, t, e[c + 4], 20, -405537848), t = i(t, n, l, u, e[c + 9], 5, 568446438), u = i(u, t, n, l, e[c + 14], 9, -1019803690), l = i(l, u, t, n, e[c + 3], 14, -187363961), n = i(n, l, u, t, e[c + 8], 20, 1163531501), t = i(t, n, l, u, e[c + 13], 5, -1444681467), u = i(u, t, n, l, e[c + 2], 9, -51403784), l = i(l, u, t, n, e[c + 7], 14, 1735328473), t = a(t, n = i(n, l, u, t, e[c + 12], 20, -1926607734), l, u, e[c + 5], 4, -378558), u = a(u, t, n, l, e[c + 8], 11, -2022574463), l = a(l, u, t, n, e[c + 11], 16, 1839030562), n = a(n, l, u, t, e[c + 14], 23, -35309556), t = a(t, n, l, u, e[c + 1], 4, -1530992060), u = a(u, t, n, l, e[c + 4], 11, 1272893353), l = a(l, u, t, n, e[c + 7], 16, -155497632), n = a(n, l, u, t, e[c + 10], 23, -1094730640), t = a(t, n, l, u, e[c + 13], 4, 681279174), u = a(u, t, n, l, e[c + 0], 11, -358537222), l = a(l, u, t, n, e[c + 3], 16, -722521979), n = a(n, l, u, t, e[c + 6], 23, 76029189), t = a(t, n, l, u, e[c + 9], 4, -640364487), u = a(u, t, n, l, e[c + 12], 11, -421815835), l = a(l, u, t, n, e[c + 15], 16, 530742520), t = s(t, n = a(n, l, u, t, e[c + 2], 23, -995338651), l, u, e[c + 0], 6, -198630844), u = s(u, t, n, l, e[c + 7], 10, 1126891415), l = s(l, u, t, n, e[c + 14], 15, -1416354905), n = s(n, l, u, t, e[c + 5], 21, -57434055), t = s(t, n, l, u, e[c + 12], 6, 1700485571), u = s(u, t, n, l, e[c + 3], 10, -1894986606), l = s(l, u, t, n, e[c + 10], 15, -1051523), n = s(n, l, u, t, e[c + 1], 21, -2054922799), t = s(t, n, l, u, e[c + 8], 6, 1873313359), u = s(u, t, n, l, e[c + 15], 10, -30611744), l = s(l, u, t, n, e[c + 6], 15, -1560198380), n = s(n, l, u, t, e[c + 13], 21, 1309151649), t = s(t, n, l, u, e[c + 4], 6, -145523070), u = s(u, t, n, l, e[c + 11], 10, -1120210379), l = s(l, u, t, n, e[c + 2], 15, 718787259), n = s(n, l, u, t, e[c + 9], 21, -343485551), t = r(t, f), n = r(n, d), l = r(l, h), u = r(u, p) } return [t, n, l, u] }(function (e) { for (var t = 1 + (e.length + 8 >> 6), r = new Array(16 * t), n = 0; n < 16 * t; n++) r[n] = 0; for (n = 0; n < e.length; n++) r[n >> 2] |= (255 & e.charCodeAt(n)) << n % 4 * 8; return r[n >> 2] |= 128 << n % 4 * 8, r[16 * t - 2] = 8 * e.length, r }(e))) function r(e, t) { var r = (65535 & e) + (65535 & t); return (e >> 16) + (t >> 16) + (r >> 16) << 16 | 65535 & r } function n(e, t, n, o, i, a) { return r(function (e, t) { return e << t | e >>> 32 - t }(r(r(t, e), r(o, a)), i), n) } function o(e, t, r, o, i, a, s) { return n(t & r | ~t & o, e, t, i, a, s) } function i(e, t, r, o, i, a, s) { return n(t & o | r & ~o, e, t, i, a, s) } function a(e, t, r, o, i, a, s) { return n(t ^ r ^ o, e, t, i, a, s) } function s(e, t, r, o, i, a, s) { return n(r ^ (t | ~o), e, t, i, a, s) } } // 获取时间戳 function getCurrentTimestamp() { var date = new Date(); var year = date.getFullYear(); var month = ('0' + (date.getMonth() + 1)).slice(-2); var day = ('0' + date.getDate()).slice(-2); var hours = ('0' + date.getHours()).slice(-2); var minutes = ('0' + date.getMinutes()).slice(-2); var seconds = ('0' + date.getSeconds()).slice(-2); return year + month + day + hours + minutes + seconds; } function getxSign(e = {}, t) { const w = "511MrfpK^", m = "KIoa7YXIaN", v = "@UnOaJYS", p = "#iki0Dhr"; const r = e.query || {}; let n = ""; console.log("getSign.times ===>", t, "getSign.data ===>", r); // 对 query 对象的键按字母排序后拼接 const sortedKeys = Object.keys(r).sort(); sortedKeys.forEach(key => { n += key + r[key]; }); console.log("data 排序后 ===>", n); // 计算 MD5 签名 const sign = hexMD5(n + t + w + m + v + p); console.log("getSign.sign ===>", sign); return sign; } var t = getCurrentTimestamp(); var body = { "member_no": 973470, "type": 33, "store_no": 12 }; var sign = getxSign({ query: { "member_no": 973470, "type": 33, "store_no": 12 } }, t) body.sign = sign; body.time = t; console.log("final body ==> ",body) // 将 body 设置为 Postman 变量 pm.variables.set("query", JSON.stringify(body));
- 这是最终的完整代码
APIfox
跟postman用起来差不多一样, 只是有些设置的位置改变了点
代码还是一模一样的,填入前置操作中就可以
Python
使用Python脚本直接发包
import hashlib
import requests
import json
from datetime import datetime
# 常量定义
CONSTANTS = {
'd': "511MrfpKD^",
'h': "GKXW58GHG",
'p': "#iki0Dhr",
'g': "ASF74XSR^",
'v': "@UnOaJYS",
'm': "KIoa7YXIaN",
'y': "648DCGFG^",
'w': "511MrfpK^"
}
# 获取当前时间戳
def get_current_timestamp():
now = datetime.now()
return now.strftime('%Y%m%d%H%M%S')
# 计算签名的函数,使用 MD5
def getxSign(params, timestamp):
query = params.get("query", {})
sorted_keys = sorted(query.keys())
concatenated_string = "".join([key + str(query[key]) for key in sorted_keys])
concatenated_string += timestamp + CONSTANTS['w'] + CONSTANTS['m'] + CONSTANTS['v'] + CONSTANTS['p']
# 生成 MD5 签名
sign = hashlib.md5(concatenated_string.encode('utf-8')).hexdigest()
return sign
# 发送请求的函数
def send_request():
# 请求体中的参数
body = {
"member_no": 973470,
"type": 33,
"store_no": 12
}
# 获取当前时间戳
time = get_current_timestamp()
# 生成签名
sign = getxSign({
"query": body
}, time)
# 将签名和时间戳加入到请求体
body['sign'] = sign
body['time'] = time
# 打印请求体,查看是否正确
print("Final body: ", body)
# 发送 POST 请求
url = "https://mpg.avillage.com.cn/api/grow/doGrow" # 替换为实际的API URL
headers = {
"Content-Type": "application/json"
}
# 发起 HTTP 请求
response = requests.post(url, headers=headers, data=json.dumps(body))
# 输出响应
print("Response Status Code: ", response.status_code)
print("Response JSON: ", response.json())
# 执行请求
send_request()
执行结果:
- 标题: 微信小程序-某某牛仔城
- 作者: xiaoeryu
- 创建于 : 2024-09-16 22:14:02
- 更新于 : 2024-09-16 23:29:00
- 链接: https://github.com/xiaoeryu/2024/09/16/微信小程序-某某牛仔城/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。