webpack3,webpack4,webpack5的区别

文章目录

    • webpack3
    • webpack4
    • webpack5
    • 总结
    • webpack逆向案例

js逆向中经常会遇到webpack打包过的js代码,不同的版本,打包的结果也存在差异。

先简单理解一下webpack

webpack中每个模块有一个唯一的id,是从0开始递增的。整个打包后的bundle.js是一个匿名函数自执行。参数则为一个数组。数组的每一项都为个function。function的内容则为每个模块的执行内容,并按照require的顺序排列。

下面通过自己的实践,总结出webpack3,webpack4,webpack5的区别,并记录下来,方便在js逆向过程快速定位版本,做出相应的逆向方案。

webpack3

安装

npm install -g [email protected]

打包命令

// webpack 入口文件 出口文件
webpack index.js main.js

查看版本

webpack -v

在这里插入图片描述
原始代码:index.js

let total = 0;
for (let i = 0; i < 10; i++) {
    total += i;
}
console.log("Total: " + total);

index.js打包,生成main.js,代码如下:

/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {

let total = 0;
for (let i = 0; i < 10; i++) {
    total += i;
}
console.log("Total: " + total);

/***/ })
/******/ ]);

我们来看一下打包后的代码,整个打包后的代码就是一个超大的匿名函数,这种写法匿名函数和之后的圆括号包裹成为一个整体,表明匿名函数与之后调用函数的()为一个整体。

// 这是一个简单的匿名函数案例
(function(num1,num2){
       var sum = num1 + num2;
}(1,2));

webpack3,webpack4,webpack5的区别_第1张图片
其中的关键代码如下,此代码功能:执行模块功能

/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

call方法的解释

Function.prototype.call() call()
方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call

webpack3,webpack4,webpack5的区别_第2张图片
结论:仔细查看代码,发现模块作为参数传入执行,再观察wp3.js文件,每一行都存在/******/注释,其加载器函数名是__webpack_require__,代码总体读起来也是相对简单,变量名比较长,整体代码的长度只有78多行。

打包后的模块通过modules[moduleId].call()运行,其中moduleId就是模块下标id

webpack4

安装webpack,由于webpack4.0开始需要依赖webpack-cli,所以需要一起安装。

npm install --save-dev webpack@4 webpack-cli@3.3.2
npx webpack --version
webpack --version

// 这是因为不支持旧版本加密套件导致的,需要开启旧版支持。
export NODE_OPTIONS=--openssl-legacy-provider

打包命令

// 默认把index.js打包main.js文件
webpack

原始代码:index.js

let total = 0;
for (let i = 0; i < 10; i++) {
    total += i;
}
console.log("Total: " + total);

index.js打包,生成main.js,代码如下:

!function (e) {
    var t = {};

    function r(n) {
        if (t[n]) return t[n].exports;
        var o = t[n] = {i: n, l: !1, exports: {}};
        return e[n].call(o.exports, o, o.exports, r), o.l = !0, o.exports
    }

    r.m = e, r.c = t, r.d = function (e, t, n) {
        r.o(e, t) || Object.defineProperty(e, t, {enumerable: !0, get: n})
    }, r.r = function (e) {
        "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {value: "Module"}), Object.defineProperty(e, "__esModule", {value: !0})
    }, r.t = function (e, t) {
        if (1 & t && (e = r(e)), 8 & t) return e;
        if (4 & t && "object" == typeof e && e && e.__esModule) return e;
        var n = Object.create(null);
        if (r.r(n), Object.defineProperty(n, "default", {
            enumerable: !0,
            value: e
        }), 2 & t && "string" != typeof e) for (var o in e) r.d(n, o, function (t) {
            return e[t]
        }.bind(null, o));
        return n
    }, r.n = function (e) {
        var t = e && e.__esModule ? function () {
            return e.default
        } : function () {
            return e
        };
        return r.d(t, "a", t), t
    }, r.o = function (e, t) {
        return Object.prototype.hasOwnProperty.call(e, t)
    }, r.p = "", r(r.s = 0)
}([function (e, t) {
    let r = 0;
    for (let e = 0; e < 10; e++) r += e;
    console.log("Total: " + r)
}]);

webpack4是目前打包版本中用的最多的,结构如下图:
webpack3,webpack4,webpack5的区别_第3张图片
和webpack3一样,打包后还是巨大自执行函数,模块作为参数传入执行,代码的复杂程度明显提升。

webpack5

安装webpack5

npm init
npm install babel-loader @babel/core @babel/preset-env webpack --save-dev

编写配置文件webpack.config.js

这里要注意的是在不同模式下打包后代码是不同的

  • development’:在开发模式下打包,会保留源代码的映射,以方便调试和开发。此模式下,代码不会进行压缩和混淆,以便更容易阅读和理解。
  • ‘production’:在生产模式下打包,会对代码进行优化、压缩和混淆,以减小打包后文件的大小,并提高执行效率。这是用于发布生产环境的代码。
  • ‘none’:不使用任何默认优化选项,可以用于自定义配置打包行为。
const path = require('path');

module.exports = {
	mode: 'development',
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'main.js'
    },
    module: {
        rules: [
            // Babel Loader 配置
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            },
            // 其他加载器配置...
        ]
    },
    // 其他配置...
};

原始代码:index.js

let total = 0;
for (let i = 0; i < 10; i++) {
    total += i;
}
console.log("Total: " + total);

打包命令

npx webpack

index.js打包,生成main.js,打包后代码如下:

(() => {
    for (var o = 0, l = 0; l < 10; l++) o += l;
    console.log("Total: " + o)
})();

对比index.js原代码几乎没有变化。

尝试让原始代码变的复杂一点再打包,如下:

moduleA.js

function multiply(a, b) {
  return a * b;
}

module.exports = multiply;

moduleB.js

function divide(a, b) {
  return a / b;
}

module.exports = divide;

index.js

const multiply = require('./moduleA');
const divide = require('./moduleB');

const result1 = multiply(5, 3);
const result2 = divide(10, 2);

console.log('Result 1:', result1);
console.log('Result 2:', result2);

打包后

(() => {
    var r, o, t, e, n = {
        615: r => {
            r.exports = function (r, o) {
                return r * o
            }
        }, 817: r => {
            r.exports = function (r, o) {
                return r / o
            }
        }
    }, s = {};

    function u(r) {
        var o = s[r];
        if (void 0 !== o) return o.exports;
        var t = s[r] = {exports: {}};
        return n[r](t, t.exports, u), t.exports
    }

    r = u(615), o = u(817), t = r(5, 3), e = o(10, 2), console.log("Result 1:", t), console.log("Result 2:", e)
})();

代码更加简洁,模块不再以参数的方式传入执行,所有模块代码都包含在匿名函数内,并且执行
webpack3,webpack4,webpack5的区别_第4张图片

总结

原始代码:index.js打包前代码总共5行

版本 打包后
webpack3 代码77行,原始代码结构无变化,模块作为参数传入执行
webpack4 代码39行,原始代码结构做了压缩,模块作为参数传入执行
webpack5 代码4行,原始代码结构无变化,模块作为函数体并加载;但是当增加js源代码代码复杂度并打包后,打包后代码结构变化较大

webpack逆向案例

js逆向第6例:猿人学第16题-webpack-控制台无限输出-window删除陷阱-纯手撕版
js逆向第3例:蝉妈妈大屏实时数据formDataSign加密

你可能感兴趣的:(随笔大杂烩,webpack,javascript,前端)