声明

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测及文章作者不为此承担任何责任。

雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。

注:本文涉及内容均已脱敏,无真实漏洞披露,分析对象均为正常功能点,所涉数据均遵从国家法律法规。

No.1 前言

时间过得好快,由于忙着谈恋爱笔者已经有快一年没有写过完整的技术文章了。再回看笔者去年写的《微信小程序测试五脉》一文,文章中还是有许多可以改进和不准确的地方,也有因为当时自身思维局限而没有突破的地方。《微信小程序测试五脉》中的许多技术手段已经过时,编写的脚本也因微信版本升级需要修改之后才能使用,但其中抽象的思路仍是十分有价值的。笔者希望自己的每一遍文章都能给读者带来独到的测试技巧及深入思维的测试逻辑,文章中的例子会过时、文章中的手段会被更新,但其中的”魂“是不会变的。

笔者最近遇到了一个小程序,需要在电梯中扫描小程序生成的二维码才能去对应楼层的电梯。二维码是动态生成且数据被进行了加密,此外小程序还有防截屏功能不让你截屏分享给他人,若多次截屏将会禁用当前账户。但笔者的手机信号不太好老是刷新不出来二维码,于是想弄一个不需要依托网络及小程序的离线电梯二维码工具。笔者发现在分析这个小程序的过程中,几乎用到了所有针对小程序分析的技巧,这些技巧都很有价值且是《微信小程序测试五脉》中没有展开详将的内容,于是便有了今天这篇笔者自认为不错的技术文章想分享给大家。本文看似是在讲一个实实在在的例子,却有许多抽象的技巧蕴含其中。

No.2 调试前准备

首先我们需要一台已经root的安卓手机或是已经越狱的IOS手机,在手机内运行目标小程序并在如下目录中找到以.wxapkg结尾的微信小程序包将其拉取至我们的电脑上(小程序包命名随机,可更具时间顺序推测出对应的包):

安卓保存路径:

/data/data/com.tencent.mm/MicroMsg/{用户ID}/appbrand/pkg/

IOS保存路径:

/var/mobile/Containers/Data/Application/{程序UUID}/Library/WechatPrivate/{用户ID}/WeApp/LocalCache/release/{小程序ID}/

在提取到微信小程序包之后只需使用wxappUnpacker工具,项目地址:

https://github.com/rtcatc/wxappUnpacker

将其解包即可,如下图所示该工具会自动分析小程序包并还原对应的文件:

还原结束之后我们便可以在当前目录下看到目标小程序的所有原始代码和资源文件:

在拿到了还原之后的文件我们就可以把其作为一个正常的项目源码在微信开发者工具中使用,我们在微信开发者工具中选择 “导入项目”功能,选择对应的小程序存放目录。在AppID一栏可以使用自己的小程序AppID或者微信分配的测试号AppID均可。

来到微信小程序开发界面之后,我们还有许多工作要做,由于微信的安全策略限制在默认情况下所有不在小程序白名单之中的域名都是无法被请求的,会被微信直接拦截。我们需要在“详情”、“本地设置”中勾选“不校验合法域名”一栏的选项。此外若对应的小程序使用了npm则需开启“开启npm模块”功能,“ES5转ES6”和“增强编译”两个功能可以按照需求进行关闭或打开操作,具体评判标准为只要小程序能正常运行即可。

当一切基本设置都结束之后,我们来到调试器Console界面,发现程序抛出了严重错误,提示:Error: This application has not registered any plugins yet.通过报错内容我们可以猜测小程序在某个位置引入了一个微信小程序插件,但是因为我们没有注册对应的插件导致插件内容无法导入,导致程序无法继续运行。

根据提示:at Bluekey.js? [sm]:1我们知道具体的问题代码位于Bluekey.js文件的第一行处,我们点击对应的超链接查看对应的报错代码。

可以看到在代码内小程序使用了requirePlugin函数来引用对应的插件,此时我们有两种处理方式:1. 在app.json配置文件中添加对应插件的引用;2.若插件对本次分析不重要可以直接使用require(js文件地址)函数将所有的引用替换为引用一个本地js文件。

在解决了外部插件引用问题之后,我们看到Console抛出了“渲染层错误”的异常的错误:Connot delete property 'WeixinJSBridge' of #<Window>。

出现此类错误意味着非代码本身存在问题,而是微信开发者工具出现了兼容性错误,我们可以选择更新至最新版本的微信开发者工具或在本地设置中选择不同版本的调试基础库即可解决此类异常。

修改完调试基础库版本之后等待小程序重新渲染,此时在Console内已无影响程序正常运行的报错,剩下的一些都是系统提示:

随后便可以在开发者工具内随意调试小程序的全部内容,调试前准备到此结束。

No.3 内容逻辑分析

来到微信小程序的主页,点击下方tab栏中的“开门”按钮,前往我们要分析的对应页面。

当我们来到对应页面时,微信小程序立刻跳转到了登录页面:

这时需要我们来到调试器窗口切换到“Wxml”选卡,查看当前页面渲染之后的原始页面源码。可以看到如下图当前页面嵌入了一个webview形式的h5的登录页面,所有的登录操作都在h5页面内完成。

遇到这种情况常规的解决思路很简单,我们只需要输入自己的账户登录即可。按照这个思路,我们在登录页面输入账号密码进行登录操作登录,随后我们发现系统提示跳转到了人脸识别验证模块,但由于此时在微信开发者工具内是无法在h5页面内唤起摄像头的,我们便也无法完成人脸识别操作进行微信小程序的登录,常规的解决思路在这里并不可行。

此时我们只能从小程序源码入手,找到小程序对应的登录判断方式来绕过全局登录校验进行下一步分析操作。来到app.json配置文件中,所有的tab栏配置均在tabBar的json列表内,可以看到对应的“开门”模块位于/pages/miniCode/minicode页面中。

来到对应页面的js文件中,在第78行处我们发现存在跳转至登录页面的代码:

可以看到小程序跳转到登录页面是因为在第76行处函数调用出现异常触发了catch功能自动执行了跳转的代码。我们向上追溯发现所调用的函数位于第44行处为s.getAppminiCode。

来到getAppminiCode函数中,我们发现函数首先会将全局数据中的e.globalApi.getQrCode的url地址传入a中作为api请求地址,接着创建一个ajax请求去触发对应的api,然后通过if函数判断http的响应码是否为200,若为200则先判断手机时间是否存在误差并将获取到的返回包中的token和qr值保存在本地的Strage中。

接着我们进入ajax函数继续进行分析,可以看到函数会将当前用户的openid赋值给uniqueId变量放置在o变量中,接着会将o变量内容与小程序的http请求头内容合并传入a变量中,最后在wx.requesthttp请求函数内将a作为当前http的请求头。至此我可以判断出当前小程序的所有api都是通过请求头内的openid参数值来进行身份校验的。

在常规思路中,我们只需要在调试器的Storage模块中新建一个openid的key然后将自己的openid存入即让成功进入登录状态。

但在笔者的测试过程中,此小程序在每次重新启动的时候会触发一个http的请求去重新获取用户的openid值导致每次Storage中的openid值被清空。

当遇到这种情况时,我们可以去寻找对应触发openid参数修改的函数或是通过全局查找的方式将所有需要通过wx.getStorageSync函数获取openid值的代码直接替换为使用我们固定的openid内容:

替换完成之后微信开发者工具会重新渲染小程序,稍等片刻便可看到微信小程序已经是登录状态,我们成功获取到开门二维码:

我们在调试器的network模块中观察对应的API请求的返回包内容,看到有两个比较关键的参数:token和userId。

随后我们点击谷歌开发者工具祖传的小鼠标按钮:

将鼠标移动到对应的二维码上面,我么可以很清晰的看见当前二维码的class为qr-image:

之后在调试器的审查元素模块中可以看到对应的地址为本地缓存目录:

之后我们在开发者工具的编辑器窗口中打开对应页面的原始wxml,搜索classqr-image我们找到了原始的地址为{{imagePath}}变量的值:

随后我们来到页面所属的js文件内,搜索imagePath变量被赋值的位置,成功搜索到位于canvasToTempFilePath函数中。

继续追溯canvasToTempFilePath被调用的位置,我们来到了createQrcode函数中。

接着继续进行追溯操作,最终我们来到第51行代码处为最初调用createQrcode函数的位置。往上研究,我们发现二维码的内容存储在46行的qrcodeUrl之中,而qrcodeUrl的内容正是通过之前API调用获取到的两个参数值userId和token经过e.default处理后产生的数据。

之后我们将鼠标放置于e.default之上,开发者工具会为我们自动显示对应内容的引用地址,我们看到对应的地址为utils/pocsirCode。

我们来到pocsirCode.js文件中,在第一行的e函数内便可以看到一个使用crypto-jsnpm库的标准aes加密函数,当前加密采用cbc模式不使用填充。e为待加密内容,r为加密密码,s为加密向量,最终的加密结果使用base64编码之后返回。

可以推测出e函数是一个非常关键的函数,我们可以先开始调试e函数,直接使用console.log命令输出函数传入的所有变量值。重新运行小程序后我们看到控制台输出了三个被crypto-jshex处理之后的内容:

接着我们改进我们输出的方式,将所有被hex处理过的函数使用.toString()的办法转换为字符串之后再次输出。重新回到控制台,我们发现待加密内容和加密密码已经成功以字符串的形式输出,加密向量则由于是object类型的变量无法被输出。我们观察待加密内容,发现开头部分好像是当前系统的时间,后面接的两段则是传入的userId变量和token变量的变量值。

结束完e函数的分析之后回到default的分析,我们来到文件末尾在第42行处找到了对应执行的函数。函数首先会将userID的值赋值给a,随后将token的值拼接000000赋值给o,随后将所有的内容通过01 + 年年月月日日分分秒秒当前日期 + 00 + a + o + 00的格式赋值给u。随后调用先前的e加密函数传入u作为待加密内容、传入s作为加密密码、传入n作为加密向量进行加密并返回加密之后的结果。

接着来到文件中间部分,我们发现了加密密码s变量和加密向量n变量的内容。n变量的值为:{sigBytes: 16, words: [1735354213, 1852405107, 825373492, 892745528]},s变量值的内容为r变量值的SHA256值,r变量的值为:{sigBytes: 8, words: [1735354213, 1852405107]},由于上述变量的值均为固定值,我们的加密分析到此结束,我们完成了所有必要的分析操作。

No.4 验证及利用

知道了加密方式及加密流程之后我们便可以开始进行利用,这里推荐使用node.js编写POC脚本,很多微信小程序的函数可以直接复制过来使用特别的方便。首先引入crypyo-js模块作为CryptoJS使用。随后将userId和token内容按照设置格式进行拼接,形成最终的待加密内容。最后在Encrypt函数中将待加密内容进行HEX处理,然后进行加密操作,返回最终的加密结果。

编写完毕脚本之后,在控制台运行,成功输出加密之后的内容:

至此,我们便可以随时随地不依托于网络及微信小程序生成自己的电梯二维码了。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

热门产品

大中专招生方法二:机器人电话外呼(ai外呼)人工智能外呼系统(含机器人话术模板)|大中专招生方法,机器人外呼,人工智能电话外呼,大专招生方法,中专招生方法,学校招生方法,中职招生方法,机器人话术模板,大中专,招生,方法,机器人,电话,外呼,ai外呼,人工智能,系统,机器,人话,模板
大中专招生方法二:机器人电话外呼(ai外呼)人工智能外呼系统(含机器人话术模板)

历史上的今天:04月29日

热门专题

云南高职单招|云南单招,云南单招网,云南高职单招网,云南高职单招,云南单招学校,云南单招培训
云南高职单招
卓越综合高中|卓越综合高中
卓越综合高中
APP开发|app开发_app开发公司_app软件开发_专业app开发_云南app开发公司_app定制_原生app开发定制
APP开发
小程序开发|微信小程序,小程序开发,小程序,小程序制作,微信小程序开发,小程序公司,小程序开发公司,分销,三级分销系统,分销系统
小程序开发
云南巨榕教育投资集团有限公司|云南巨榕教育投资集团有限公司,巨榕教育集团,巨榕教育
云南巨榕教育投资集团有限公司
外贸网站建设|外贸网站建设,英文网站制作,英文网站设计,美国主机空间,外贸建站平台,多语言网站制作
外贸网站建设
大理科技管理学校|大理科技管理学校,大理科技,大理科技中等职业技术学校,大理科技管理中等职业技术学校,大理科技学校
大理科技管理学校
安徽开放大学|安徽开放大学报名,安徽开放大学报考,安徽开放大学,什么是安徽开放大学,安徽开放大学学历,安徽开放大学学费,安徽开放大学报名条件,安徽开放大学报名时间,安徽开放大学学历,安徽开放大学专业
安徽开放大学

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部