前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >微信小程序的渗透五脉(访道篇)

微信小程序的渗透五脉(访道篇)

作者头像
公众号爱国小白帽
发布2021-06-16 15:05:21
2K0
发布2021-06-16 15:05:21
举报

声明

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测以及文章作者不为此承担任何责任。 雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。

No.1

前言

道可道,⾮常道。想要全⾯的对微信⼩程序展开渗透⼯作,光会通过微信接⼝收集信息是不⾏的,只会在⼿机上抓包测试也是不够全⾯的,必须要对微信中⼩程序的数据包彻底了解才可⼊其理访其道。在这篇⽂章中作者将会带领⼤家完整的分析微信⼩程序数据包,并教导⼤家如何提取以及还原⼩程序数据包。

No.2

小程序包浅析

在开发者将⾃⼰的⼩程序上传之后,微信服务器会将⼩程序打包为以 wxapkg 作为扩展名的⼩程序数据 包供客户端下载及使⽤。⼩程序包共由:头部段、索引段、数据段三个部分组成,在iOS和安卓客户端 中并没有对⼩程序包进⾏加密保存。下⾯就让我们在Hex编辑器中打开数据包,来分别了解⼀下这个三个数据包段。

我们可以看到每⼀个⼩程序包的头部段都由 0xBE 幻数(Magic Number)开头以 0xED 幻数做结,这两个幻数分别对应了“Begin”和“End”的缩写。

微信⼩程序包的头部段固定⻓度为14字节,在除去两个幻数之后⼜可以以每4字节为⼀块分为填充块、 索引段⻓度块、数据段⻓度块三个数据块。填充块默认为0x0,占四个字节,并⽆其他实际⽤途;索引段⻓度块则代表索引段字符⻓度,⽤于微信校验⽤途;数据段⻓度块表示数据段字符⻓度,也参与了⼩程序数据包的校验。可将头部段数据归类为如下表:

在⼩程序包头部段结束之后便是索引段,此段的功能是让微信客户端快速解析包内有哪些⽂件,并将它们从数据段分离出来形成最终能访问的⽂件。索引段以占位4字节的“数量块”开头,他代表当前包内共由 多少个⽂件,例如下图:

图中数量块的值为 0x0D 对应10进制“13”,则表示当前⼩程序包内共有13个⽂件。在数量块结束之后,便以数量块的值作为循环次数来依次循环:⻓度块、名称块、偏移块、数据量块这四个数据块。⻓度块占4个字节,代表对应⽂件的⽂件名⻓度;接着便是以⻓度块的值作为占位⻓度的名称块,是⼀个字节型变量,储存的内容是对应⽂件的⽂件名,其⽂件名包含⽂件在当前数据包内的相对路径,例 如:/pages/index/index.html 、 /images/logo/logo.png ;偏移段和数据量段所占空间均同样为4 字节,分别代表对应⽂件在⼩程序包中的具体偏移位置和对应⽂件在⼩程序包中的数据⻓度。⾄此我们便可将⼩程序包索引段结构归类为下图:

最后我们来到⼩程序包的数据段,数据段的构造⾮常简单仅有“内容块”这么⼀个数据块,⾥⾯储存了每⼀个索引段中存在索引的⽂件的实际内容。

如上图,我们使⽤“BINWalk”⼯具也可轻松的得到数据段中的⽂件结构,微信⼩程序包的数据段会优先存⼊⼆进制⽂件(图⽚、声⾳⽂件等)再储存js、html等⽂件,该段⼩结如下表:

No.3

回到最初的数据

有细⼼的读者可能会发现,⼩程序数据包内存在的⽂件⽐⼩程序开发时的原项⽬⽂件少了许多⽂件,每 个⻚⾯之下的“js”、“json”、“wxml”、“wxss”等许多⽂件都不⻅了,取⽽代之是⼀个“html”⽂件:

原来,在微信服务器会将⼩程序源码中所有的“js”⽂件压⼊“app-service.js”⽂件中,将所有的“json”⽂件 压⼊“app-config.json”中,将所有的“wxml”⽂件压⼊“page-frame.html”⽂件中,“wxss”则在处理之后 以“html”⽂件的形式存留在对应⻚⾯⽬录之下。

JS数据还原:

我们打开“app-service.js”⽂件可以看到他的内容由⼀个个“define”函数构成:

我们可以清晰的看到“define”函数中包含有原本js⽂件的⽂件名及内容,其js内容被压缩了,美化⼀下即可,红框部分便是原始js⽂件的内容:

JSON数据还原:

我们在编辑器中打开“app-config.json”⽂件,“page”段中每个“window”段内的json⽂ 件便为原本对应⻚⾯下的json⽂件,剩下部分(下图红框处)则为“app.json”的⽂件内容。

例如将上图的数据还原,则为:

pages/index/index.json:

{

"backgroundTextStyle": "light", "navigationBarBackgroundColor": "#fff"

}

app.json:

{

"entryPagePath":"pages/index/index.html",

"pages":[

"pages/index/index",

"pages/companyCard/index"

],

"window":{

"backgroundTextStyle":"light", "navigationBarBackgroundColor":"#fff", "navigationBarTitleText":"示例APP啊"

}

}

WXSS数据还原:

打开对应⻚⾯下的html⽂件,我们可以发现在这个⻚⾯之内调⽤了 setCssToHead 函数,该函数的参数的内容便是经过处理之后原本对应⻚⾯下的wxss⽂件,我们只需要还原他即可:

WXML数据还原

打开“page-frame.html”⽂件,我们发现相较于还原其他⽂件,处理之后的wxml数据 还原起来⽐较复杂。微信将原本的wxml⻚⾯直接处理成js格式放⼊“page-frame.html”⽂件中,并进⾏ 了⼀些代码混淆。当⽤户需要调⽤当前⻚⾯时,通过使⽤基础库来处理这些js代码形成dom树并渲染, 使得⽤户可以看到对应的⽹⻚内容。

根据前⼈分析的结果我们可以将形成wxml⽂件的js语句理解为如下指令(笔者更新版):

♥var z=gzgwx_{id}() 调⽤ gzgwx_{id} 函数获取对应的变量存⼊ z 数组中;

♥var {name}=_n('{tag}') 创建名称为 {name} , tag 为 {tag} 的节点;

♥ _rz(z,{name},'{attrName}',{id},e,s,gg) 将 {name} 的 {attrName} 属性修改为 z 数组中对 应 {id} 的值;

♥ _({parName},{name}) 将 {name} 作为 {parName} 的⼦节点;

♥ var {name}=_oz(z,{id},e,s,gg) 创建名称为 {name} ,内容为 z 数组中对应 {id} 的值的⽂本 节点;

♥var {name}=_v() 创建名称为 {name} 的虚节点( wxml ⾥恰好提供了功能相当的虚结点 block , 这句话相当于 var {name}=_n('block') );

♥ var {name}=_mz(z,'{tag}',['{attrName1}',{id1},'{attrName2}',{id2},...], [],e,s,gg) 创建名称为 {name} , tag 为 {tag} 的节点,同时将 {attrNameX} 属性修改为 z[f({idX})] 的值( f 定义为 {idX} 与 {base} 的和;{base} 初始为 0 , f 返回的第⼀个正值后 {base} 即改为该返回值;若返回负值,表示该属性⽆值);

♥return {name} 名称为 {name} 的节点设为主节点;

♥cs.*** 调试⽤语句,直接忽略即可。

例如某wxml⻚⾯的⽣成内容为:

var m0 = function(e, s, r, gg) {

var z = gz$gwx_1()

var oB = _n('web-view')

_rz(z, oB, 'src', 0, e, s, gg)

_(r, oB) return r

}

var z = gzgwx_1() 代表调⽤ gzgwx_1 函数获取动态变量的值储存⾄ z 数组中(此函数稍后分 析);varoB = _n('web-view') 表示创建⼀个tag为web-view的节点,也就是:<web-view> </web-view> ;_rz(z, oB, 'src', 0, e, s, gg) 则表示 <web-view> 标签中有⼀个 src 的属性, 他的内容为 z[0] 数组的内容。

接着我们来看 gz$gwx_1 函数:

function gz$gwx_1() {

if(__WXML_GLOBAL__.ops_cached.gwx_1) return__WXML_GLOBAL__.ops_cached.gwx_1 __WXML_GLOBAL__.ops_cached.

(function(z) {

var a = 11;

function Z(ops) {z.push(ops)}

Z([[7],[3, 'companyUrl']])}) (__WXML_GLOBAL__.ops_cached.gwx_1); return__WXML_GLOBAL__.ops_cached.gwx_1

}

其中最关键的内容是 (function(z) {var a = 11;function Z(ops) {z.push(ops)} 和 Z([[7],[3, 'companyUrl']])}) 这两⾏。微信将所有动态计算的变量放在了⼀个由函数构造⽽成 的z数组中,并有如下格式: Z([{id},{name}]); 其中 {name} 便是对应变量的变量名,例如上⾯示例函 数变量名为 companyUrl 。最终构造得到当前wxml⻚⾯的数据内容为:

<web-view src="{{companyUrl}}"></web-view>

No.4

小程序包提取

⾸先你需要⼀台已经ROOT的安卓设备/模拟器或⼀台已经JAILBREAK的iOS设备/模拟器,这⾥我们以安 卓模拟器为例。在模拟器上下载微信并登录之后找到对应的⼩程序点击打开即可(因为兼容性问题,在 安卓模拟器中微信⼩程序可能会闪退,但这并不影响后续操作,⼩程序数据包已经成⾃带下载了)。接 着我们便能在安卓保存路径:/data/data/com.tencent.mm/MicroMsg/{⽤户ID}/appbrand/pkg/ 下;iOS保存路径:/var/mobile/Containers/Data/Application/{程序 UUID}/Library/WechatPrivate/{⽤户ID}/WeApp/LocalCache/release/{⼩程序ID}/ )下找到⼩程 序包。

在找到对应的⼩程序包之后我们可以使⽤ adb ⼯具的“adb pull {⼩程序包的绝对路径}”命令⾮常便捷的 将其从安卓系统中提取⾄电脑(IOS系统可考虑安装OPENSSH使⽤SFTP功能提取):

这⾥我们使⽤ wxappUnpacker 解包⼯具(下载地址:https://data.hackinn.com/tools/wxappUnpacke r.zip,此下载包为⼆次优化版本),直接使⽤“node wuWxapkg.js ⼩程序包名”命令即可⼀键解包(需 提前安装node.js及其他组件,详⻅包内使⽤说明),⾮常⽅便:

在解包完成之后,我们需要做的便是打开微信⼩程序开发者⼯具,选择“导⼊项⽬”,“AppID”选择测试号 并导⼊;接着来到“本地设置”模块,勾选上“不校验合法域名”功能就⼤功告成,可以愉快的开始调试对应 ⼩程序的源码了。

另外微信官⽅从微信Mac 2.4.0 Bêta版 & 微信Windows 2.7.0 Bêta版起便开始⽀持直接在 Mac/Windows客户端上打开微信⼩程序,其微信⼩程序包存放路径如下:Mac( /Users/{系统⽤户 名}/Library/Containers/com.tencent.xinWeChat/Data/Library/Containers/com.tencent.xin WeChat/Data/Library/Caches/com.tencent.xinWeChat/{微信版本号}/{⽤户 ID}/WeApp/LocalCache/release/{⼩程序ID}/ )、Windows( C:\Users\{系统⽤户 名}\Documents\WeChat Files\Applet\{⼩程序ID}\ )。微信对于Mac和Windows的⼩程序包都做了不同程度的加密(⽬前Mac⼩程序包数据段没有做加密),由于现在从安卓/iOS系统中提取⼩程序更为 ⽅便,故不在此展开“如何解密Mac/Windows客户端上微信⼩程序数据包”的话题讨论,有兴趣的读者可以⾃⾏研究。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-06-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱国小白帽 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档