前言
起初是在刷dy的时候,偶然间刷到一个精美壁纸推荐分享的视频,想必在看的各位应该也都刷到过,套路呢,基本就是关注公众号回复关键字取图、添加wx取图、搜索小程序取图等等,并借此来进行引流。
那么这次刚好碰到的就是搜索小程序取图的例子,于是乎便根据评论区提示搜索到了目标小程序。
事先分析
那么当我们随意点开一张壁纸想要下载保存时,弹出了这样一个熟悉的界面,选择开通会员或者看广告才能进行下载。
目标
ok现在目标就已经很明确了,因为我只是想要下载壁纸图片,而又不想花钱开通会员或者看广告,那么只需要想办法拿到壁纸图片的url地址即可
思路
这里其实按常规的方法去进行小程序请求抓包即可,但是往往请求都会涉及到sign加密算法的分析,自然需要想办法去进行解包尝试拿到可读代码。
由于dy小程序没碰过,于是乎灵机一动,反手上wx搜到了同名的小程序,开始一波熟悉的解包分析套路。
(一部分小程序目前都是用跨平台的解决方案来开发的,例如uniapp,所以往往都会在多个不同平台上架,有时遇到不熟悉的平台,那么还是尽可能地选择转战到熟悉的wx小程序更佳)
wx小程序的解包套路目前几乎都是比较固定的:
①获取到小程序包***.wxapkg文件
②对文件进行解密 (pc端获取的需要进行这一步,手机端的好像是不用解密直接可以进行解包)
③对解密出来的文件进行解包
④打开微信开发者工具导入解包后的项目进行分析
准备工具
抓包:proxypin (支持直接对小程序抓包,不用再折腾别的,很好用)
获取小程序包***.wxapkg文件,这里我用的是pc微信
解密:UnpackMiniApp
解包:unveilr/wxappUnpacker
分析:微信开发者工具
抓包
打开proxypin,首次一般需要安装证书到本机然后开启https代{过}{滤}理才能抓到https请求,(注意默认左上角状态显示是开启抓包的,可以先点击红色按钮停止)
现在点击左上角绿色三角开启抓包,然后小程序,可以看到已经有很多包进来了,但是美中不足的就是这里的请求都是按照域名进行分组,没法直观的看到比较清晰的请求瀑布流(像浏览器F12开发者工具的network那样),并且还会有很多其他无关的请求被抓进来,因此这里可以选择代{过}{滤}理转发的方式将特定进程流量转发给其他网络请求分析软件进行处理。这里我就直接进行分析了。
将其中一些分组展开后,看到了目标请求,但是看了下返回内容,没有我需要的数据,再翻找其他的请求
经过一番苦苦寻找之后,终于看到了我想要的东西
请求头(很大一段sign值,也是后面要分析的重点)
返回数据(已经看到返回了thumb_url、cover这些关键字段的缩略图url,以及我所需要的url图片真实地址)
请求参数
这里为了验证是否有必要进行后续sign的分析,咱们先直接将当前请求进行一次重放,看能否顺利拿到数据
发送之后呢,没有出乎我的意料,果然是不能拿到正常的返回数据啊,那么反复观察之后发现每次请求携带的参数都是一样的,只有这个header里面的sign值在变,所以为了成功构造出正确的请求,我们需要进行后续的针对这个sign值的分析,去研究清楚它到底是怎么来的。
接下来进行解包工作。
解包
再一次在pc微信上将目标小程序打开之后,尽量把所有能点的页面都先点一遍,防止部分小程序存在分包的情况导致未能完全加载
然后点击pc微信左下角设置--->文件管理,找到对应路径目录下的Applet
进入后可以看到很多小程序的appid文件夹,按日期排序后的第一个应该就是,直接进入
这里就得到了需要解包的小程序***.wxapkg文件,未分包的一般只有__APP__.wxapkg这个主包,其余的便是分包
解密的话需要对每一个文件都分别进行解密,然后再解包,不是很方便
这里我使用unveilr直接命令行中指定小程序路径,进行解包,会自动识别子包,相对比于其他工具来说还是挺方便的
解包之后呢,就基本得到了一个较为完整的小程序工程项目结构
到这里熟悉wx小程序开发的同学就很开心啦,直接打开微信开发者工具,导入当前项目尝试运行起来
这里特别注意一下,正常情况下最好先将本地设置里面的这个选项去掉,避免项目编译出现一堆报错无从下手
现在项目就已经可以正常跑起来了,没有出什么别的岔子
这里根据之前我们抓包出来的请求路径,直接去搜索快速定位到对应的请求关键代码位置,不需要再去读其他大量无关代码啦
这里可以看到定义并且拼接了很多请求路径
继续往上找到了发送post请求的关键代码部分,return返回了一个o,这个o是什么呢,继续找
ok,那么很幸运,终于看到了小o的具体定义,顺带连sign的赋值也一起找到了
明显可以看到这里sign的值是直接调用了一个s(a)的方法,传入了一个a参数,那么在刚才找o的过程中,仿佛是瞟到了这个s方法的定义,找一下
这里的函数传参处理写法有点独特,于是乎将它调整为正常写法,逻辑一目了然
s()方法的定义现在已经搞清楚了,那么传入的a又是个什么东西呢,在调用的位置输出看一下
根据多次调用的情况来看,每次传入的值都是相同的,现在还没办法看出来这个东西到底是个啥,那么我们开始去找几个具体调用的代码位置看看这个东西是从哪里来的。
直接找post关键字,顺手打开第一个结果,可以明显看到这里传入了三个参数,第一个是请求的路径,第二个是携带的数据,那么这第三个e.getToken(),应该就是传入的a参数了
于是乎继续找到这个e.getToken()的具体定义
再看getUserInfo()的定义
最后到这里getCache()取缓存数据方法
调用了原生的wx.getStorageSync()方法来获取缓存数据,那么我们反手就在调试器里面的Storage找到userInfo,点开一看,好家伙,token连同openid和unionid一起存在这里
那么现在就简单了,既然存在这里了,必定是有一个方法来获取token值写入到缓存的,根据命名规则,找到了setCache()方法的定义,于是直接搜索关键词setCache("userinfo"),看到了两处调用
这里看到调用post,向定义的getOpenid路径发送了一个请求,那么我们将本地缓存清理掉之后,重新编译项目,尝试找到这个请求
很顺利啊,通过请求成功获取到了我们计算sign值所需要的token,再反手将这个请求地址扔浏览器里面看看
完事儿,那么到这里这个sign的分析就已经结束了,后续也成功通过这个逻辑构造出了正确的请求拿到所需要的数据。
总结
总体来说这次的小程序逆向难度算入门级别,没有涉及到复杂的代码混淆以及加密等。但是每次的逆向尝试,虽说不一定100%成功,但是即便再简单的项目也都能够让我有所收获。
特别是像上面sign计算的实现,对于js基础不够牢固的人来说,这样的写法算是比较少见的,一开始可能读不懂,在开发者工具控制台中多尝试写了几个类似的方法,才明白其具体作用
[JavaScript] 纯文本查看 复制代码 function a(b){
return function(b){
return b
}(b+"###")
}
虽说难度较低,但是整体过程和思路也算是比较完整,希望能够给同样入门的同学提供一个参考
(用到的工具地址自行网上找,名字都贴出来了,这里就不附带了哈) |