|
在上一篇文章 《微信小程序“反編譯”實戰(zhàn)(一):解包》 中,我們詳細(xì)介紹了如何獲取某一個小程序的 .wxapkg 包,以及分析了 .wxapkg 包的結(jié)構(gòu),最后通過腳本解壓獲取包中的文件:小程序“編譯”后的代碼文件和資源文件,但是由于這些文件大部分被混淆了,可讀性很差,所以本文將進(jìn)一步分析,盡可能地把 .wxapkg 包的內(nèi)容還原為“編譯”前的內(nèi)容。 注:本文包含一部分源碼分析,由于手機(jī)屏幕較小,閱讀體驗可能不佳,建議在電腦上瀏覽。 特別感謝:下文使用的還原工具來自于 GitHub 上的開源項目 wxappUnpacker ,在此特別感謝原作者的無私貢獻(xiàn)。 概覽我們知道,前端 Web 網(wǎng)頁編程采用的是 HTML + CSS + JS 這樣的組合,其中 HTML 是用來描頁面的結(jié)構(gòu), CSS 用來描述頁面的樣子, JS 通常用來處理頁面邏輯和用戶的交互。類似地,在小程序中也有同樣的角色,一個小程序工程主要包括如下幾類文件:
例如“知識小集”的小程序源碼工程結(jié)構(gòu)如下:
然而,根據(jù)上一篇文章介紹,對“知識小集”小程序的 .wxapkg 解包后得到如下文件:
主要包括 app-config.json , app-service.js , page-frame.html , *.html , 資源文件等,但這些文件已經(jīng)被“編譯混淆”并重新整合壓縮,微信開發(fā)者工具并不能識別它們,我們無法直接對它們進(jìn)行調(diào)試/編譯運(yùn)行。 所以,我們先嘗試分析一下從 .wxapkg 提取出來的各個文件內(nèi)容的結(jié)構(gòu)及其用途,然后介紹如何用腳本工具把它們 一鍵還原 為“編譯”前的源碼,并在微信開發(fā)者工具中跑起來。 文件分析本節(jié)主要以“知識小集”小程序的 .wxapkg 解包后的源碼文件為例,進(jìn)行分析。 你也可以跳過本節(jié)的分析,直接看下一節(jié)介紹用腳本“反編譯”還原源碼。 app-config.json小程序工程主要包括工具配置 project.config.json ,全局配置 app.json 以及頁面配置 page.json 三類 JSON 配置文件。其中: project.config.json 主要用于對開發(fā)者工具進(jìn)行個性化配置以及包括小程序項目工程的一些基礎(chǔ)配置,所以它不會被“編譯”到 .wxapkg 包中; app.json 是對當(dāng)前小程序的全局配置,包括了小程序的所有頁面路徑、界面表現(xiàn)、網(wǎng)絡(luò)超時時間、底部 tab 等; page.json 用于對每一個頁面的窗口表現(xiàn)進(jìn)行配置,頁面中配置項會覆蓋 app.json 的 window 中相同的配置項。 因此“編譯”后的文件 app-config.json 其實就是 app.json 和各個頁面的配置文件的 匯總 ,它的內(nèi)容大致如下:
{
"page": { // 各頁面配置
"pages/index/index.html": { // 某一頁面地址
"window": { // 某一頁面具體配置
"navigationBarTitleText": "知識小集",
"enablePullDownRefresh": true
}
},
// 此處省略...
},
"entryPagePath": "pages/index/index.html", // 小程序入口地址
"pages": ["pages/index/index", "pages/detail/detail", "pages/search/search"], // 頁面列表
"global": { // 全局頁面配置
"window": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "知識小集",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
}
}
}
通過與原工程 app.json 和各頁面配置 page.json 內(nèi)容的對比,我們可以得出 app-config.json 匯總文件的簡單整合規(guī)律,很容易把它拆分成“編譯”前對應(yīng)的各 json 文件。 app-service.js在小程序項目中 JS 文件負(fù)責(zé)交互邏輯,主要包括 app.js ,每個頁面的 page.js ,開發(fā)者自定義的 JS 文件和引入的第三方 JS 文件,在“編譯”后所有這些 JS 文件都會被匯總到 app-service.js 文件中,它的結(jié)構(gòu)如下:
// 一些全局變量的聲明
var __wxAppData = {};
var __wxRoute;
var __wxRouteBegin;
var __wxAppCode__ = {};
var global = {};
var __wxAppCurrentFile__;
var Component = Component || function(){};
var definePlugin = definePlugin || function(){};
var requirePlugin = requirePlugin || function(){};
var Behavior = Behavior || function(){};
// 小程序編譯基礎(chǔ)庫版本
/*v0.6vv_20180125_fbi*/
global.__wcc_version__='v0.6vv_20180125_fbi';
global.__wcc_version_info__={"customComponents":true,"fixZeroRpx":true,"propValueDeepCopy":false};
// 工程中第三方或者自定義的一些 JS 源碼
define("utils/util.js", function(require, module, exports, window,document,frames,self,location,navigator,localStorage,history,Caches,screen,alert,confirm,prompt,XMLHttpRequest,WebSocket,Reporter,webkit,WeixinJSCore) {
"use strict";
// ... 具體源碼內(nèi)容
});
// ...
// app.js 源碼定義
define("app.js", function(...) {
"use strict";
// ... app.js 源碼內(nèi)容
});
require("app.js");
// 每個頁面對應(yīng)的 JS 源碼定義
__wxRoute = 'pages/index/index'; // 頁面路由地址
__wxRouteBegin = true;
define("pages/index/index.js", function(...){
"use strict";
// ... page.js 源碼內(nèi)容
});
require("pages/index/index.js");
在這個文件中,原有小程序工程中的每個 JS 文件都被 define 方法定義聲明,定義中包含 JS文件的路徑和內(nèi)容,如下:
define("path/to/xxx.js", function(...){
"use strict";
// ... xxx.js 源碼內(nèi)容
});
因此,我們同樣很容易提取這些 JS 文件源碼,并恢復(fù)至相應(yīng)的路徑位置中。當(dāng)然,這些 JS 文件中的內(nèi)容經(jīng)過混淆壓縮,我們可以使用 UglifyJS 這樣的工具進(jìn)行美化,但仍很難還原一些原始變量名,不過基本不影響正常閱讀和使用。 |