微信小程序反编译 wxss 丢失问题

鉴于同事执行了删除代码的疯狂操作,并多次覆盖后,硬盘恢复文件的几率几乎为零,只能另辟蹊跷,所幸之前看到过微信小程序反编译的一篇文章。通过简单的搜索,就找到了相关的文章。根据文章所写,其他的都还算顺利,但是样式文件就迟迟弄不出来。

微信小程序反编译可以参考 https://github.com/qwerty472123/wxappUnpacker

0x1

执行wxss反编译程序后获得如下错误:

Guess wxss(first turn)...

/Users/aimuz/workspace/web/wxappUnpacker/node_modules/vm2/lib/main.js:214
            throw this._internal.Decontextify.value(e);
            ^
ReferenceError: $gwx is not defined
    at vm.js:3:3
    at Script.runInContext (vm.js:134:20)
    at VM.run (/Users/aimuz/workspace/web/wxappUnpacker/node_modules/vm2/lib/main.js:208:72)
    at runVM (/Users/aimuz/workspace/web/wxappUnpacker/wuWxss.js:69:6)
    at runOnce (/Users/aimuz/workspace/web/wxappUnpacker/wuWxss.js:86:27)
    at Array.preRun (/Users/aimuz/workspace/web/wxappUnpacker/wuWxss.js:166:5)
    at CntEvent.decount (/Users/aimuz/workspace/web/wxappUnpacker/wuLib.js:17:43)
    at ioLimit.runWithCb (/Users/aimuz/workspace/web/wxappUnpacker/wuLib.js:81:11)
    at agent (/Users/aimuz/workspace/web/wxappUnpacker/wuLib.js:54:14)
    at FSReqCallback.readFileAfterClose [as oncomplete] (internal/fs/read_file_context.js:53:3)

在翻阅相关的资料后,发现不少人遇到了相同的问题

这简直和我的一毛一样啊。可惜没有解决方法。

偌大的互联网竟然还没有人找到解决问题。😞

0x2 分析文件

到了这里可以确认,微信小程序官方应该对小程序进行了升级,导致原反编译工具失效。可是这并不能为难我胖虎,通过 http://jsnice.org/ 工具对 page-frame.html 进行简单的反混淆后。获得了较为清晰的文件

通过仔细阅读代码后发现了 page-frame.html 文件,包含了样式信息。

__wxAppCode__['components/menu/menu.wxss'] = setCssToHead([".", [1], "menuBox{ width: 100%; height: ", [0, 100], "; position: fixed; bottom: ", [0, 0], "; left: ", [0, 0], "; display: -webkit-flex; display: flex; background: #fff; padding-top: ", [0, 10], "; border-top: ", [0, 2], " solid #ccc; }\n.", [1], "menuBox .", [1], "menu{ width: ", [0, 250], "; text-align: center; font-size: ", [0, 22], "; color: #8a8a8a }\n.", [1], "menuBox wx-image{ width: ", [0, 50], "; height: ", [0, 50], "; }\n.", [1], "menuBox .", [1], "menu wx-view{ }\n",], undefined, { path: "./components/menu/menu.wxss" });

setCssToHead 函数 定义

var setCssToHead = function (file, _xcInvalid, info) {

通过查看 setCssToHead 函数,发现其中有一个 makeup 方法,这个方法是对原来的wxss中rpx进行单位转换,换成通用型的px。

makeup 方法相关的代码

function makeup(file, opt) {
    var _n = typeof(file) === "number";
    if (_n && Ca.hasOwnProperty(file)) return "";
    if (_n) Ca[file] = 1;
    var ex = _n ? _C[file] : file;
    var res = "";
    for (var i = ex.length - 1; i >= 0; i--) {
        var content = ex[i];
        if (typeof(content) === "object") {
            var op = content[0];
            if (op == 0) res = transformRPX(content[1], opt.deviceWidth) + "px" + res;
            else if (op == 1) res = opt.suffix + res;
            else if (op == 2) res = makeup(content[1], opt) + res;
        } else res = content + res
    }
    return res;
}

在这之后又找到了一个 rewritor
rewritor 相关代码

var rewritor = function(suffix, opt, style) {
    opt = opt || {};
    suffix = suffix || "";
    opt.suffix = suffix;
    if (opt.allowIllegalSelector != undefined && _xcInvalid != undefined) {
        if (opt.allowIllegalSelector) console.warn("For developer:" + _xcInvalid);
        else {
            console.error(_xcInvalid + "This wxss file is ignored.");
            return;
        }
    }
    Ca = {};
    css = makeup(file, opt);
    if (!style) {
        var head = document.head || document.getElementsByTagName('head')[0];
        window.__rpxRecalculatingFuncs__ = window.__rpxRecalculatingFuncs__ || [];
        style = document.createElement('style');
        style.type = 'text/css';
        style.setAttribute("wxss:path", info.path);
        head.appendChild(style);
        window.__rpxRecalculatingFuncs__.push(function(size) {
            opt.deviceWidth = size.width;
            rewritor(suffix, opt, style);
        });
    }
    if (style.styleSheet) {
        style.styleSheet.cssText = css;
    } else {
        if (style.childNodes.length == 0) style.appendChild(document.createTextNode(css));
        else style.childNodes[0].nodeValue = css;
    }
}

并发现setCssToHead最后就是返回了rewritor 方法,由此以及根据上述代码可以猜出,该方法应该主要是还原wxss文件。

0x3 分析代码

rewritor 拥有三个参数,阅读代码可以得出 suffix 参数是给样式添加前缀,例如原来样式名是 .abcsuffix 参数是 a,那么会拼接成 .aabc

opt 看代码结构应该是一个对象

style 应该是一个document.node的对象。

通过浏览器直接打开page-frame.html文件,在控制台中输入 __wxAppCode__['components/menu/menu.wxss'], 看看其返回值

WX20190123-104403@2x.png

看出,确实是返回一个方法,先不传任何参数,再次输入测试 __wxAppCode__['components/menu/menu.wxss']()

WX20190123-104540@2x.png

直接返回了 undefined

由此说明参数应该必填的,现在输入正确的参数类型尝试 __wxAppCode__['components/menu/menu.wxss']("",{},document.body)

WX20190123-105904@2x.png

7E7E64C7831D01CAB0D40E2C45FCB463.jpg

1_8170889.gif

把代码复制到小程序中,🙃,像素比例竟然放大了。再次翻看一下代码,rewritor又调用了makeup方法,并且用到了设备宽度

13_2c5d12435b42e1457636d3c03de5422b.jpg

改变浏览器宽度后,确实发生了改变

.menuBox{ width: 100%; height: 50px; position: fixed; bottom: 0px; left: 0px; display: -webkit-flex; display: flex; background: #fff; padding-top: 5px; border-top: 1px solid #ccc; }
.menuBox .menu{ width: 125px; text-align: center; font-size: 11px; color: #8a8a8a }
.menuBox wx-image{ width: 25px; height: 25px; }
.menuBox .menu wx-view{ }

把设备宽度调成了iPhone 6的宽度,然后复制小程序里面

3_0c2fc143a9a2197c7333f37a830500ea.jpg

再把代码过了一遍之后,了解到 opt 会使用到一个 deviceWidth 属性,传入iPhone 6的宽度像素,也得到了

__wxAppCode__["pages/index/cardDetail/carsDetail.wxss"]("",{deviceWidth:375},document.body)

wxss 代码

.menuBox{ width: 100%; height: 50px; position: fixed; bottom: 0px; left: 0px; display: -webkit-flex; display: flex; background: #fff; padding-top: 5px; border-top: 1px solid #ccc; }
.menuBox .menu{ width: 125px; text-align: center; font-size: 11px; color: #8a8a8a }
.menuBox wx-image{ width: 25px; height: 25px; }
.menuBox .menu wx-view{ }

但是这个只能在iPhone 6上适配啊,需要改成rpx,方能适配其他的设备终端。rpx和px 的转换公式,

rpx = px * 2
px = rpx / 2

微信相关文档:
https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxss.html#wxss

经过转换后,问题就完美解决了。

反编译获取微信小程序源码教程

一、准备工具

  • node.js运行环境反编译脚本基于node.js编写,如果没有安装需要先安装。
    node.js官网:https://nodejs.org/en/

二、工具使用

  • 安装node.js不作细述,按提示安装即可。安装完成后在命令窗口输入:node -v,能查看到版本号即可。
  • 打开脚本地址下载反编译脚本,解压。
  • 安装依赖在脚本目录下shift+鼠标右键,打开命令窗口,逐条命令安装:
    npm install esprima
    npm install css-tree
    npm install cssbeautify
    npm install vm2
    npm install uglify-es
    npm install js-beautify
    npm install escodegen
  • 获取小程序.wxapkg文件雷电模拟器安装好RE管理器获取root权限,进入存放路径,
    安卓存放路径:/data/data/com.tencent.mm/MicroMsg/{User}/appbrand/pkg
    其中{User}为当前用户的用户名,类似于2bc*************b65一串。

    1.png

    将文件打包发送到电脑本地。

    请输入图片描述

    请输入图片描述

    请输入图片描述

三、开始反编译

  • 在脚本目录下shift+鼠标右键,打开命令窗口,输入命令:node wuWxapkg.js <files...><files...>是指小程序.wxapkg文件存放目录。

    请输入图片描述

    请输入图片描述

  • 比如我的文件存放在D:\小程序解包\_820332684_36.wxapkg
  • 那就在命令窗口输入:node wuWxapkg.js D:\小程序解包\_820332684_36.wxapkg请输入图片描述
  • 解包成功请输入图片描述
  • 小程序存放目录下新增反编译好的小程序源码请输入图片描述

    1.png

  • 打开微信开发者工具导入,成功打开小程序,反编译完成。请输入图片描述

    请输入图片描述

 

反编译微信小程序$gwx is not defined报错解决方法

 

错误提示

1.png

错误提示代码:

ReferenceError: $gwx is not defined
    at vm.js:3:3
    at Script.runInContext (vm.js:107:20)
    at VM.run (D:\wxappUnpacker-master\node_modules\vm2\lib\main.js:208:72)
    at runVM (D:\wxappUnpacker-master\wuWxss.js:69:6)
    at runOnce (D:\wxappUnpacker-master\wuWxss.js:97:27)
    at Array.preRun (D:\wxappUnpacker-master\wuWxss.js:177:5)
    at CntEvent.decount (D:\wxappUnpacker-master\wuLib.js:17:43)
    at ioLimit.runWithCb.err (D:\wxappUnpacker-master\wuLib.js:73:11)
    at agent (D:\wxappUnpacker-master\wuLib.js:54:14)
    at FSReqWrap.oncomplete (fs.js:141:20)

解决方法

修改反编译脚本wuWxss.js代码

将原代码

function runVM(name,code){
    let wxAppCode={},handle={cssFile:name};
    let vm=new VM({sandbox:Object.assign(new GwxCfg(),{__wxAppCode__:wxAppCode,setCssToHead:cssRebuild.bind(handle)})});
    vm.run(code);
    for(let name in wxAppCode)if(name.endsWith(".wxss")){
        handle.cssFile=path.resolve(frameName,"..",name);
        wxAppCode[name]();
    }
}

修改为

function runVM(name,code){
    let wxAppCode={},handle={cssFile:name};
    let gg = new GwxCfg();
    let tsandbox ={$gwx:GwxCfg.prototype["$gwx"],__mainPageFrameReady__:GwxCfg.prototype["$gwx"],__wxAppCode__:wxAppCode,setCssToHead:cssRebuild.bind(handle)};
    let vm = new VM({sandbox:tsandbox});
    vm.run(code);
    for(let name in wxAppCode)if(name.endsWith(".wxss")){
        handle.cssFile=path.resolve(frameName,"..",name);
        wxAppCode[name]();
    }
}

相关教程

 

https://www.yuchunlai.com/index.php/archives/33/

https://www.yuchunlai.com/index.php/archives/11/

 

mysql根据经纬度获取附近的商家

创建geo表

create table geo(
    geo_id INT NOT NULL AUTO_INCREMENT,
    lng float NOT NULL,
    lat float NOT NULL,
    name VARCHAR(100) NULL,
    PRIMARY KEY ( geo_id )
);
INSERT INTO `geo`(`lng`, `lat`, `name`) VALUES (118.302416,33.958887,"实验小学");
INSERT INTO `geo`(`lng`, `lat`, `name`) VALUES (118.303997,33.95188,"宿迁市人民医院");
INSERT INTO `geo`(`lng`, `lat`, `name`) VALUES (118.302991,33.935828,"宿迁学院");
INSERT INTO `geo`(`lng`, `lat`, `name`) VALUES (118.28215,33.959307,"金陵名府");
INSERT INTO `geo`(`lng`, `lat`, `name`) VALUES (118.290081,33.925404,"润园");
INSERT INTO `geo`(`lng`, `lat`, `name`) VALUES (118.354751,33.959007,"国际馆");

下面是google给的解决方案(基于公里km)。

SELECT  
  geo_id, `name`,(  
    6371 * acos (  
      cos ( radians(33.958887) )  
      * cos( radians( lat ) )  
      * cos( radians( lng ) - radians(118.302416) )  
      + sin ( radians(33.958887) )  
      * sin( radians( lat ) )  
    )  
  ) AS distance  
FROM geo
HAVING distance < 20  
ORDER BY distance 
LIMIT 0 , 20

其中33.958887是纬度,118.302416是经度。

牛了个逼的。

/** 附近的商家 */
public function get_near_business() {
        if (!$this->validate->validate('lng', ['require','regex|-?((0|1?[0-7]?[0-9]?)(([.][0-9]{1,4})?)|180(([.][0]{1,4})?))'])) { // 经度
            $this->json->setErr(10001, '缺少lng或lng格式不正确');
            $this->json->Send();
        }
        if (!$this->validate->validate('lat', ['require','regex|-?((0|[1-8]?[0-9]?)(([.][0-9]{1,4})?)|90(([.][0]{1,4})?))'])) { // 纬度
            $this->json->setErr(10001, '缺少lat或lng格式不正确');
            $this->json->Send();
        }

        if (!isset($_POST['page']) || $_POST['page'] < 1) {
            $_POST['page'] = 1;
        }
        // 分页获取附近的商家信息
        $sql_count = "SELECT  id, `name`,( 6371 * acos (  
                    cos ( radians(".$_POST['lat'].") )  
                    * cos( radians( lat ) )  
                    * cos( radians( lng ) - radians(".$_POST['lng'].") )  
                    + sin ( radians(".$_POST['lat'].") )  
                    * sin( radians( lat ) )  
                    )  
                ) AS distance  
                FROM tf_my_business
                WHERE `status` = 1
                HAVING distance < 10";
        $count = count(M()->query($sql_count));
        $hasPage = ceil($count / C('PAGE.LIMIT'));
        $hasPage = $hasPage ? $hasPage : 1;

        if ($_POST['page'] > $hasPage) {
            $this->json->setErr(10002, '页数有误');
            $this->json->Send();
        }

        $limit = (($_POST['page'] - 1) * C('PAGE.LIMIT')) . "," .C('PAGE.LIMIT');
        $sql_list = "SELECT  `id`,`name`,`info`,( 6371 * acos (  
                    cos ( radians(".$_POST['lat'].") )  
                    * cos( radians( lat ) )  
                    * cos( radians( lng ) - radians(".$_POST['lng'].") )  
                    + sin ( radians(".$_POST['lat'].") )  
                    * sin( radians( lat ) )  
                    )  
                ) AS distance  
                FROM tf_my_business
                WHERE `status` = 1
                HAVING distance < 10 
                ORDER BY distance ASC 
                LIMIT ".$limit;
        $datalist = M()->query($sql_list);
        if ($datalist) {
            // 处理datalist
            $my_business_img_model = M('my_business_img');
            foreach ($datalist as &$v) {
                // 获取主图
                $my_business_img_info = $my_business_img_model->where(['m_b_id'=>$v['id'],'type'=>1,'is_main'=>1])->find();
                $v['img'] = $my_business_img_info['img'];
                if (strpos($v['img'],'cdn') === false) {
                    $v['img'] = C('CDN.URI').$v['img'];
                }
            }

            $data['datalist'] = $datalist;
            $data['current_page'] = $_POST['page'];
            $data['hasPage'] = $hasPage;
            $this->json->setErr(0, '获取成功');
            $this->json->setAttr('data',$data);
            $this->json->Send();
        } else {
            $this->json->setErr(10003, '暂无数据');
            $this->json->Send();
        }
}
{
    "errno": 0,
    "errdesc": "获取成功",
    "timestamp": 1524749336,
    "data": {
        "datalist": [
            {
                "id": "3",
                "name": "京东",
                "info": "京东",
                "distance": "4.665364145881758",
                "img": "https://cdn.caomall.net/1524732806744365022.png"
            }
        ],
        "current_page": "1",
        "hasPage": 1
    }
}

 

https://www.cnblogs.com/jiqing9006/p/8954831.html

 

crontab定时备份mySQL数据库

[sh]

#!/bin/bash
#Shell Command For Backup MySQL Database Everyday Automatically By Crontab

USER=root
PASSWORD=”123456″
DATABASE=”test”
HOSTNAME=”192.168.156.61″

BACKUP_DIR=/home/firefoxbug/mysql_back/ #备份文件存储路径
LOGFILE=/home/firefoxbug/mysql_back/data_backup.log #日记文件路径
DATE=`date ‘+%Y%m%d-%H%M’` #日期格式(作为文件名)
DUMPFILE=$DATE.sql #备份文件名
ARCHIVE=$DATE.sql.tgz #压缩文件名
OPTIONS=”-h$HOSTNAME -u$USER -p$PASSWORD $DATABASE”
#mysqldump -help

#判断备份文件存储目录是否存在,否则创建该目录
if [ ! -d $BACKUP_DIR ] ;
then
mkdir -p “$BACKUP_DIR”
fi

#开始备份之前,将备份信息头写入日记文件
echo ” ” >> $LOGFILE
echo ” ” >> $LOGFILE
echo “———————————————–” >> $LOGFILE
echo “BACKUP DATE:” $(date +”%y-%m-%d %H:%M:%S”) >> $LOGFILE
echo “———————————————– ” >> $LOGFILE

#切换至备份目录
cd $BACKUP_DIR
#使用mysqldump 命令备份制定数据库,并以格式化的时间戳命名备份文件
mysqldump $OPTIONS > $DUMPFILE
#判断数据库备份是否成功
if [[ $? == 0 ]]; then
#创建备份文件的压缩包
tar czvf $ARCHIVE $DUMPFILE >> $LOGFILE 2>&1
#输入备份成功的消息到日记文件
echo “[$ARCHIVE] Backup Successful!” >> $LOGFILE
#删除原始备份文件,只需保 留数据库备份文件的压缩包即可
rm -f $DUMPFILE
else
echo “Database Backup Fail!” >> $LOGFILE
fi
#输出备份过程结束的提醒消息
echo “Backup Process Done”

[/sh]

 

crontab -e #编辑定时任务

 

0 22 * * * /backup/bkdb.sh

意思是每天在22:00执行shell脚本/backup/bkdb.sh

———————
作者:谢谢俊东不用谢
来源:CSDN
原文:https://blog.csdn.net/u012865381/article/details/75258527
版权声明:本文为博主原创文章,转载请附上博文链接!