前言:
而今大家对“js双字节”可能比较讲究,兄弟们都需要了解一些“js双字节”的相关文章。那么小编在网摘上收集了一些关于“js双字节””的相关内容,希望大家能喜欢,兄弟们一起来学习一下吧!将NodeJS模块编译为字节码文件,目的,与JS混淆加密相似,也是为了JS代码的安全性,使代码不可阅读。
一、编译模块为字节码
在NodeJS中,与编译一个直接执行的JS文件为字节码不同,如果JS代码是一个模块,有导出函数,是要被其它文件require,那么,它不能直接的调用VM.script编译成bytecode。
例如代码:
exports.hello = function () { console.log('Hello');}
有导出函数是hello,用以下代码编译:
//读取文件 var code = fs.readFileSync(filePath, 'utf-8'); //调用v8虚拟机,编译代码 var script = new vm.Script(require('module').wrap(code)); //得到字节码,即bytecode var bytecode = script.createCachedData(); //写文件,后缀为.bytecode fs.writeFileSync(filePath.replace(/\.js$/i, '.bytecode'), bytecode);
特殊之处是module模块的warp方法,它会对代码进行包裹,前面的代码,经warp之后,会成为:
(function (exports, require, module, __filename, __dirname) { exports.hello = function () { console.log('Hello');}
这是必须遵守的约定,然后才能进行编译。
二、加载并调用字节码模块
编译出字节码模块后,自然是require并调用它,方法如下:
const _module = require('module');const path = require('path');_module._extensions['.bytecode'] = function (module, filename) { //读取bytecode式的模块 var bytecode = fs.readFileSync(filename); //设置正确的文件头信息 setHeader(bytecode, 'flag_hash', getFlagBuf()); var sourceHash = buf2num(getHeader(bytecode, 'source_hash')); //申请空间并放入bytecode const script = new vm.Script('0'.repeat(sourceHash), { cachedData: bytecode, }); //bind为输出函数 const wrapperFn = script.runInThisContext(); // 这里的参数列表和之前的 wrapper 函数是一一对应的 wrapperFn.bind(module.exports)(module.exports, require, module, filename, path.dirname(filename));}//require字节码模块const hello = require('./hello.bytecode');hello.hello();
代码中调用到的几个函数如下:
let _flag_buf;function getFlagBuf() { if (!_flag_buf) { const script = new vm.Script(""); _flag_buf = getHeader(script.createCachedData(), 'flag_hash'); } return _flag_buf;}function getHeader(buffer, type) { const offset = HeaderOffsetMap[type]; return buffer.slice(offset, offset + 4);}function setHeader(buffer, type, vBuffer) { vBuffer.copy(buffer, HeaderOffsetMap[type]);}function buf2num(buf) { // 注意字节序问题 let ret = 0; ret |= buf[3] << 24; ret |= buf[2] << 16; ret |= buf[1] << 8; ret |= buf[0]; return ret;}
重点是bind方法,将函数绑定到某个对象,bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()中的第一个参数的值,例如:f.bind(obj),实际上可以理解为obj.f(),
bind(module.exports)则给输出函数进行了绑定。
const hello = require('./hello.bytecode');hello.hello();这段代码的执行效果:
与直接require原始的js文件效果是一致的。
传统的混淆加密,比如JShaman,是把代码变成“乱码”,使代码不能正常阅读理解。而此字节码方式,是把代码变成了非文本模式的二进制格式,于安全的目标而言,两者异曲同工。
标签: #js双字节