智谱清言说:
大家好,今天我将为大家带来一篇关于微信小程序逆向之牛仔城签到接口逆向的技术分享。在这篇文章中,我们将一起探讨如何逆向牛仔城微信小程序的签到接口,并分析其背后的原理。
由于已经逆向成功了,现在只是重新复现。
参考贴:
技术分享|微信小程序绕过sign签名思路
使用工具:
反编译小程序工具
解密小程序源码工具
小程序包解密.rar
(9.65 KB, 下载次数: 268)
这边推荐再下一个everything,方便你找到.wxapkg结尾的文件,除非你能准确知道小程序的存放路径就可以不用。打开我们的微信PC,然后登录🤣搜索【疯狂牛仔城】微信小程序
然后点开小程序,这时候进入小程序后,多点击几个功能,比如什么登录啊,我的,主页,活动和二维码都点击一下,方便让小程序把需要的文件都下载完成。
打开everything,开始我们的搜索!直接搜.wxapkg,选择修改时间-倒序,然后打开_APP_.wxapkg所在的文件夹
当然,到这一步,你可能想问,你都没和我说怎么抓签到接口啊!😅
我想说,不好意思我忘了😋
这边你再下载一个抓包工具,叫HTTP Debugger Pro,或者你有可以抓到微信小程序的抓包工具都可以用
然后,打开HTTP Debugger Pro,这是他的页面。
开始抓包,点击一下签到,然后抓到该请求。
我们可以发现,通过抓包,发现有个doGrow,经过翻译,可以大致推断他就是签到接口
通过接口response返回,可以看到request发送了如下json
{
"member_no" : 9999,
"sign" : "2675c58926fe37d01a39874b6e210486",
"store_no" : 12,
"time" : "20240418200528",
"type" : 12
}
通过询问GPT,可以得知:
member_no:表示会员编号,这里的值为44246,可能是指一个特定会员的唯一识别码。
sign:通常表示一个签名或者校验码,用于验证数据的完整性和真实性。这里的值“2675c58926fe37d01a39874b6e210486”可能是一个通过特定算法生成的签名。
store_no:表示商店编号,这里的值为12,可能指的是特定商店的编号。
time:表示时间,这里的值“20240418200528”看起来是一个时间戳,格式可能是YYYYMMDDHHMMSS,即2024年4月18日20点05分28秒。
type:表示类型,这里的值为12,具体指什么类型需要根据上下文来判断,比如可能是指交易类型、消息类型等。
并且可以看到,返回了如下json:
{
"data" : null,
"errorCode" : 0,
"errorMessage" : "领取成功",
"filename" : null,
"success" : true,
"url" : null
}
尝试重发请求,修改time值为20240419200528 原time值为:20240418200528
使用APIFOX发送请求可以发现,只修改time会出现416错误返回:
由此可以判断,只修改time是没法进行签到的,需要一同把sign修改了,可是我们该如何得到sign值呢?
通过长度判断,可以发现sign是md5的32位长度,猜测是md5加盐了
接下来开始解密__APP__.wxapkg
打开UnpackMiniApp.exe
打开之前找到的_APP_.wxapkg的所在的文件路径
然后打开\wxpack\ 目录
找到如下文件wx28881238baa06aeb.wxapkg
然后打开wxapkgconvertor.exe ,把wx28881238baa06aeb.wxapkg 拖进去
反编译成功,生成了wx28881238baa06aeb 目录
格式化打开后如下:
尝试搜索关键词doGrow,现有个函数定义了doGrow
通过分析,可以发现,上面的a变量,对照了下图中的a.default.doGrow ,可以发现,query对象和我们抓接口出来请求时的对象一致
猜想一下,既然可能是MD5加盐加密,很有可能方法名就叫MD5,或者尝试搜索sign看看能不能搜出结果。
下图可以发现,第一条包含有sign ,还包含有hexMD5 ,看样子是MD5加密函数,并且下面第三条也有定义hexMD5 方法
点击第一条搜索结果,可以看到有一段[JavaScript] 纯文本查看 复制代码 var f = "911e529a9bcf081551797284f095f061acun"
可以粗略判断它是加盐的密钥,把这部分代码贴到GPT,问问它
GPT已经帮我们分析好了,它说f可能是一个签名或密钥,那就先扣下这段代码,新建一个test.js文件,然后粘贴
继续分析代码
发现有段代码进行了解构,i.default.hexMD5(c + l + f) 中的c和l都是通过解构了arguments 获得的
var r, n, s, c, l, h, d, p, g, m = arguments;
暂时没法判断出用途,那就换个思路,直接搜hexMD5,可以发现有两个地方调用了hexMD5,第一个是咱们刚刚看过的位置。
第二个咱没看过,点进去一看,发现,诶这不是我想要的getSign吗?
同样的,在这里也发现了e,t,r,n解构了arguments变量
先别管他干嘛的,扣下来扣下来。这时候是没法运行的,因为会报错,hexMD5未被定义。点击搜索的第三个,把hexMD5也扣下来
咱可以发现,hexMD5调用了c,u,l这三个方法,刚好这三个方法肯定就在这附近,所以上面的代码可以判断,上面的方法就是被调用的c,u,l。
也要一并扣下来,这时候还没法运行,因为u方法会报错,会提示o未被定义
通过hexMD5方法的上下文,可以看到有o,r,n,i,a,s方法被定义,一起扣下来
这是最终扣下来的代码:
尝试运行看看,可以发现成功了,并且md5和现成的库加密出来的也是一样的:
回到getSign方法,将i.default.hexMD5改成hexMD5。
尝试运行看看,你会发现加密出来的sign并不一致。
关键点就在于getSIgn的arguments,当你看不懂的时候,可以问问AI,咱问问它到底是什么意思:
这下知道getSign接受什么参数了,参数一: object, 参数二: time值,也就是20240418200528
并且通过 r = e.query || {}可以知道,e是参数1的query值,也就是我们发送请求时的object对象,这时候的query可不存在time和sign字段,可不要搞混了:
{
"member_no" : 44246,
"store_no" : 12,
"type" : 12
}
所以咱可以知道,e本质上是传一个query,然后通过time值还有密钥key进行混淆,尝试一下:
成功!!让AI重写一下getSign方便阅读:
尝试运行一下,会发现结果一致。
回到apifox,尝试发送新的请求:
成功!逆向完成。
|