日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

微信反编译(二)源码还原

發(fā)布時(shí)間:2024/1/8 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 微信反编译(二)源码还原 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

概覽

我們知道,前端 Web 網(wǎng)頁(yè)編程采用的是?HTML + CSS + JS?這樣的組合,其中?HTML?是用來(lái)描頁(yè)面的結(jié)構(gòu),CSS?用來(lái)描述頁(yè)面的樣子,JS?通常用來(lái)處理頁(yè)面邏輯和用戶的交互。類似地,在小程序中也有同樣的角色,一個(gè)小程序工程主要包括如下幾類文件:

  • .json?后綴的 JSON 配置文件
  • .wxml?后綴的 WXML 模板文件
  • .wxss?后綴的 WXSS 樣式文件
  • .js?后綴的 JavaScript 腳本邏輯文件

例如“知識(shí)小集”的小程序源碼工程結(jié)構(gòu)如下:

然而,根據(jù)上一篇文章介紹,對(duì)“知識(shí)小集”小程序的?.wxapkg?解包后得到如下文件:

主要包括?app-config.json,?app-service.js,?page-frame.html,?*.html,?資源文件?等,但這些文件已經(jīng)被“編譯混淆”并重新整合壓縮,微信開發(fā)者工具并不能識(shí)別它們,我們無(wú)法直接對(duì)它們進(jìn)行調(diào)試/編譯運(yùn)行。

所以,我們先嘗試分析一下從?.wxapkg?提取出來(lái)的各個(gè)文件內(nèi)容的結(jié)構(gòu)及其用途,然后介紹如何用腳本工具把它們一鍵還原為“編譯”前的源碼,并在微信開發(fā)者工具中跑起來(lái)。

文件分析

本節(jié)主要以“知識(shí)小集”小程序的?.wxapkg?解包后的源碼文件為例,進(jìn)行分析。

你也可以跳過(guò)本節(jié)的分析,直接看下一節(jié)介紹用腳本“反編譯”還原源碼。

app-config.json

小程序工程主要包括工具配置?project.config.json,全局配置?app.json?以及頁(yè)面配置?page.json?三類 JSON 配置文件。其中:

project.config.json?主要用于對(duì)開發(fā)者工具進(jìn)行個(gè)性化配置以及包括小程序項(xiàng)目工程的一些基礎(chǔ)配置,所以它不會(huì)被“編譯”到?.wxapkg?包中;

app.json?是對(duì)當(dāng)前小程序的全局配置,包括了小程序的所有頁(yè)面路徑、界面表現(xiàn)、網(wǎng)絡(luò)超時(shí)時(shí)間、底部 tab 等;

page.json?用于對(duì)每一個(gè)頁(yè)面的窗口表現(xiàn)進(jìn)行配置,頁(yè)面中配置項(xiàng)會(huì)覆蓋?app.json?的?window?中相同的配置項(xiàng)。

因此“編譯”后的文件?app-config.json?其實(shí)就是?app.json?和各個(gè)頁(yè)面的配置文件的匯總,它的內(nèi)容大致如下:

  • {

  • "page": {?// 各頁(yè)面配置

  • "pages/index/index.html": {?// 某一頁(yè)面地址

  • "window": {?// 某一頁(yè)面具體配置

  • "navigationBarTitleText":?"知識(shí)小集",

  • "enablePullDownRefresh":?true

  • }

  • },

  • // 此處省略...

  • },

  • "entryPagePath":?"pages/index/index.html",?// 小程序入口地址

  • "pages": ["pages/index/index",?"pages/detail/detail",?"pages/search/search"],?// 頁(yè)面列表

  • "global": {?// 全局頁(yè)面配置

  • "window": {

  • "navigationBarTextStyle":?"black",

  • "navigationBarTitleText":?"知識(shí)小集",

  • "navigationBarBackgroundColor":?"#F8F8F8",

  • "backgroundColor":?"#F8F8F8"

  • }

  • }

  • }

  • 通過(guò)與原工程?app.json?和各頁(yè)面配置?page.json?內(nèi)容的對(duì)比,我們可以得出?app-config.json?匯總文件的簡(jiǎn)單整合規(guī)律,很容易把它拆分成“編譯”前對(duì)應(yīng)的各?json?文件。

    app-service.js

    在小程序項(xiàng)目中?JS?文件負(fù)責(zé)交互邏輯,主要包括?app.js,每個(gè)頁(yè)面的?page.js,開發(fā)者自定義的?JS?文件和引入的第三方?JS?文件,在“編譯”后所有這些?JS?文件都會(huì)被匯總到?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ǔ)庫(kù)版本

  • /*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");

  • // 每個(gè)頁(yè)面對(duì)應(yīng)的 JS 源碼定義

  • __wxRoute =?'pages/index/index';?// 頁(yè)面路由地址

  • __wxRouteBegin =?true;

  • define("pages/index/index.js",?function(...){

  • "use strict";

  • // ... page.js 源碼內(nèi)容

  • });

  • require("pages/index/index.js");

  • 在這個(gè)文件中,原有小程序工程中的每個(gè)?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)過(guò)混淆壓縮,我們可以使用?UglifyJS?這樣的工具進(jìn)行美化,但仍很難還原一些原始變量名,不過(guò)基本不影響正常閱讀和使用。

    page-frame.html

    在小程序中使用?WXML?文件描述頁(yè)面的結(jié)構(gòu),WXSS?文件描述頁(yè)面的樣式。工程中有一個(gè)?app.wxss?文件用于定義一些全局的樣式,會(huì)自動(dòng)被?import?到各個(gè)頁(yè)面中;另外每個(gè)頁(yè)面也都分別包含?page.wxml?和?page.wxss?用于描述其頁(yè)面的結(jié)構(gòu)和樣式;同時(shí),我們也會(huì)自定義一些公共的?xxxCommon.wxss?樣式文件和公共的?xxxTemplate.wxml?模板文件供一些頁(yè)面復(fù)用,一般在各自頁(yè)面的?page.wxss?和?page.wxml?中去?import。

    當(dāng)“編譯”小程序后,所有的?.wxml?文件和?app.wxss?及公共?xxxCommon.wxss?樣式文件的將被整合到?page-frame.html?文件中,而每個(gè)頁(yè)面的?page.wxss?樣式文件,將分別單獨(dú)在各自的路徑下生成一個(gè)?page.html?文件。

    page-frame.html?文件的內(nèi)容結(jié)構(gòu)如下:

  • <!DOCTYPE html>

  • <html?lang="zh-CN">

  • <head>

  • <meta?charset="UTF-8"?/>

  • <meta?name="viewport"?content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"?/>

  • <meta?http-equiv="Content-Security-Policy"?content="script-src 'self' 'unsafe-inline'">

  • <link?rel="icon"?href="data:image/ico;base64,aWNv">

  • <script>

  • // 一些全局變量的聲明

  • var?__pageFrameStartTime__ =?Date.now();

  • var?__webviewId__;

  • var?__wxAppCode__ = {};

  • var?__WXML_GLOBAL__ = {

  • entrys: {},

  • defines: {},

  • modules: {},

  • ops: [],

  • wxs_nf_init:?undefined,

  • total_ops:?0

  • };

  • // 小程序編譯基礎(chǔ)庫(kù)版本

  • /*v0.6vv_20180125_fbi*/

  • window.__wcc_version__ =?'v0.6vv_20180125_fbi';

  • window.__wcc_version_info__ = {

  • "customComponents":?true,

  • "fixZeroRpx":?true,

  • "propValueDeepCopy":?false

  • };

  • var?$gwxc

  • var?$gaic = {}

  • $gwx =?function(path, global)?{

  • // $gwx 方法定義(最核心)

  • }

  • var?BASE_DEVICE_WIDTH =?750;

  • var?isIOS = navigator.userAgent.match("iPhone");

  • var?deviceWidth =?window.screen.width ||?375;

  • var?deviceDPR =?window.devicePixelRatio ||?2;

  • function?checkDeviceWidth()?{

  • // checkDeviceWidth 方法定義

  • }

  • checkDeviceWidth()

  • var?eps =?1e-4;

  • function?transformRPX(number, newDeviceWidth)?{

  • // transformRPX 方法定義

  • }

  • var?setCssToHead =?function(file, _xcInvalid)?{

  • // setCssToHead 方法定義

  • }

  • setCssToHead([])();?// 先清空 Head 中的 CSS

  • setCssToHead([...]);?// 設(shè)置 app.wxss 的內(nèi)容到 Head 中,其中 ... 為小程序工程中 app.wxss 的內(nèi)容

  • var?__pageFrameEndTime__ =?Date.now()

  • </script>

  • </head>

  • <body>

  • <div></div>

  • </body>

  • </html>

  • 相比其他文件,page-frame.html?比較復(fù)雜,微信把?.wxml?和部分?.wxss?直接“編譯”并混淆成?JS?代碼放入上述文件中,然后通過(guò)調(diào)用這些?JS?代碼來(lái)構(gòu)造?Virtual-Dom,進(jìn)而渲染頁(yè)面。

    其中最核心的是?$gwx?和?setCssToHead?這兩個(gè)方法。

    $gwx?用于通過(guò)?JS?代碼生成所有?.wxml?文件,其中每個(gè)?.wxml?文件的內(nèi)容結(jié)構(gòu)都在?$gwx?方法中被定義好并混淆了,我們只要傳給它頁(yè)面的?.wxml?路徑參數(shù),即可獲取到每個(gè)?.wxml?的內(nèi)容,再簡(jiǎn)單加工一下即可還原成“編譯”前的內(nèi)容。

    在?$gwx?中有一個(gè)?x?數(shù)組用于存儲(chǔ)當(dāng)前小程序都有哪些?.wxml?文件,例如,“知識(shí)小集”小程序的?x?值如下:

    var x = ['./pages/detail/detail.wxml', '/towxml/entry.wxml', './pages/index/index.wxml', './pages/search/search.wxml', './towxml/entry.wxml', '/towxml/renderTemplate.wxml', './towxml/renderTemplate.wxml'];

    此時(shí)我們可以在?Chrome?中打開?page-frame.html?文件,然后在?Console?中輸入如下命令,即可得到?index.wxml?的內(nèi)容(輸出一個(gè)?JS?對(duì)象,通過(guò)遍歷這個(gè)對(duì)象即可還原出?.wxml?的內(nèi)容)

    $gwx("./pages/index/index.wxml")

    setCssToHead?方法用于根據(jù)幾段被拆分的樣式字符串?dāng)?shù)組生成?.wxss?代碼并設(shè)置到?HTML?的?Head?中,同時(shí),它還將所有被?import?引用的?.wxss?文件(公共?xxxCommon.wxss樣式文件)所對(duì)應(yīng)的樣式數(shù)組內(nèi)嵌在該方法中的?_C?變量中,并標(biāo)記哪些文件引用了?_C?中數(shù)據(jù)。另外在?page-frame.html?文件的末尾,調(diào)用了該方法生成全局?app.wxss?的內(nèi)容設(shè)置到?Head中。

    因此,我們可以在每個(gè)調(diào)用?setCssToHead?方法的地方提取相應(yīng)?.wxss?的內(nèi)容并還原。

    對(duì)于?page-frame.html?文件中?$gwx?和?setCssToHead?這兩個(gè)方法更詳細(xì)的分析,可以參考這篇文章。

    此外,checkDeviceWidth?方法顧明思議,用于檢測(cè)屏幕的寬度,其檢測(cè)結(jié)果將用于?transformRPX?方法中將?rpx?單位轉(zhuǎn)換為?px?像素。

    rpx?的全稱是?responsive pixel,它是小程序自己定義的一個(gè)尺寸單位,可以根據(jù)當(dāng)前設(shè)備屏幕寬度進(jìn)行自適應(yīng)。小程序中規(guī)定,所有的設(shè)備屏幕寬度都為?750rpx,根據(jù)設(shè)備屏幕實(shí)際寬度的不同,1rpx所代表的實(shí)際像素值也不一樣。

    *.html

    上面提到,每個(gè)頁(yè)面的?page.wxss?樣式文件,“編譯”后將分別在各自的所在路徑下生成一個(gè)?page.html?文件,每個(gè)?page.html?的結(jié)構(gòu)如下:

  • <style></style>

  • <page></page>

  • <script>

  • var?__setCssStartTime__ =?Date.now();

  • setCssToHead([...])()?// 設(shè)置 search.wxss 的內(nèi)容

  • var?__setCssEndTime__ =?Date.now();

  • document.dispatchEvent(new?CustomEvent("generateFuncReady", {

  • detail: {

  • generateFunc: $gwx('./pages/search/search.wxml')

  • }

  • }))

  • </script>

  • 在該文件中通過(guò)調(diào)用?setCssToHead?方法將?.wxss?樣式內(nèi)容設(shè)置到?Head?中,所以同樣地,我們可以根據(jù)?setCssToHead?的調(diào)用參數(shù)提取每個(gè)頁(yè)面的?page.wxss。

    資源文件

    小程序工程中的圖片、音頻等資源文件在“編譯”后將直接被拷貝到?.wxapkg?包中,其原始的路徑也保留不變,因此我們可以直接使用。

    “反編譯”

    在上一節(jié),我們完成了?.wxapkg?包幾乎所有文件內(nèi)容的簡(jiǎn)要分析。現(xiàn)在我們介紹一下如何通過(guò)?node.js?腳本幫我們還原出小程序的源碼。

    在這里需要再次感謝?wxappUnpacker?作者提供的還原工具,讓我們可以“站在巨人的肩膀上”輕松地去完成“反編譯”。它的使用如下:

    • node wuConfig.js <path/to/app-config.json>?: 將?app-config.json?中的內(nèi)容拆分成各個(gè)頁(yè)面所對(duì)應(yīng)的?page.json和?app.json;

    • node wuJs.js <path/to/app-service.js>?: 將?app-service.js?拆分成一系列原先獨(dú)立的?JS?文件,并使用?Uglify-ES美化工具盡可能將代碼還原為“編譯”前的內(nèi)容;

    • node wuWxml.js [-m] <path/to/page-frame.html>?: 從?page-frame.html?中提取并還原各頁(yè)面的?.wxml?和?app.wxss及公共?.wxss?樣式文件;

    • node wuWxss.js <path/to/unpack_dir>?: 該命令參數(shù)為?.wxapkg?解包后目錄,它將分析并從各個(gè)?page.html?中提取還原各頁(yè)面的?page.wxss?樣式文件;

    同時(shí),作者還提供了一鍵解包并還原的腳本,你只需要提供一個(gè)小程序的?.wxapkg?文件,然后執(zhí)行如下命令:

    node wuWxapkg.js [-d] <path/to/.wxapkg>

    此腳本就會(huì)自動(dòng)將?.wxapkg?文件解包,并將包中相關(guān)的已被“編譯/混淆”的文件自動(dòng)地恢復(fù)原狀(包括目錄結(jié)構(gòu))。

    PS: 此工具依賴?uglify-es,?vm2,?esprima,?cssbeautify,?css-tree?等?node.js?包,所以你可能需要?npm install xxx?安裝這些依賴包才能正確執(zhí)行。

    更詳細(xì)的用法及相關(guān)問(wèn)題請(qǐng)查閱該開源項(xiàng)目的 GitHub repo。

    最后,我們?cè)?微信開發(fā)者工具?中新建一個(gè)空小程序工程,并將上述還原后的相關(guān)目錄文件導(dǎo)入工程,即可編譯運(yùn)行起來(lái),如下圖為“知識(shí)小集”小程序的?.wxapkg?包還原后的代碼工程:

    以上,大功告成!

    總結(jié)

    本文詳細(xì)分析了?.wxapkg?解包后的各文件結(jié)構(gòu),并介紹了如何通過(guò)腳本“一鍵還原”得到任意小程序的源碼。

    對(duì)于一些簡(jiǎn)單的,且使用微信官方介紹的原生開發(fā)方式開發(fā)的小程序,用上述工具基本可以直接還原得到可運(yùn)行的源碼,但是對(duì)于一些邏輯復(fù)雜,或者使用?WePY、Vue?等一些框架開發(fā)的小程序,還原后的源碼可能會(huì)有一些小問(wèn)題,需要我們?nèi)巳馊シ治鼋鉀Q。

    后續(xù)

    本文對(duì)小程序源碼“編譯”后的各文件內(nèi)容結(jié)構(gòu)及用途的分析相對(duì)比較零散,而且沒有對(duì)各文件的依賴關(guān)系及加載邏輯進(jìn)行研究,后續(xù)我們?cè)賹懸恍┪恼轮v解微信客戶端是如何解析加載小程序?.wxapkg?包并運(yùn)行起來(lái)。

    總結(jié)

    以上是生活随笔為你收集整理的微信反编译(二)源码还原的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。