UNPKG

4.6 kBJavaScriptView Raw
1
2/**
3 * @file jfogs
4 *
5 * Javascript code obfuscator
6 * @author
7 * zswang (http://weibo.com/zswang)
8 * @version 0.0.0
9 * @date 2015-08-20
10 */
11var esprima = require('esprima');
12var util = require('util');
13/**
14 * 对字符串进行 Unicode 编码
15 *
16 * @param {string} str 源字符串
17 * @return {string} 返回编码后的内容
18 */
19function encodeUnicode(str) {
20 return String(str).replace(/[^\x09-\x7f\ufeff]/g, function (all) {
21 return '\\u' + (0x10000 + all.charCodeAt()).toString(16).substring(1);
22 });
23}
24var crossChars = [];
25for (var i = 0; i < 36; i++) {
26 crossChars.push(String.fromCharCode(0x0620 + i) + String.fromCharCode(0x0300 + i));
27}
28function crossCode(index) {
29 return index.toString(36).replace(/./g, function (all) {
30 return crossChars[parseInt(all, 36)];
31 });
32}
33/**
34 * 混淆 JS 代码
35 *
36 * @param {String} code JS 代码字符串
37 * @param {Object} options 配置项
38 * @param {Object} options.type 混淆类型 'zero': 零宽字符, 'reverse': 颠掉字符
39 * @return {String} 返回混淆后的代码
40 */
41function obfuscate(code, options) {
42 if (!code) {
43 return code;
44 }
45 options = options || {};
46 code = String(code).replace(/\r\n?|[\n\u2028\u2029]/g, '\n')
47 .replace(/^\uFEFF/, ''); // 数据清洗
48 var syntax = esprima.parse(code, {
49 range: true,
50 loc: false
51 });
52 var memberExpressions = [];
53 var propertys = {};
54 var names = [];
55 var expressions = [];
56 var guid = 0;
57 function scan(obj) {
58 if (!obj) {
59 return;
60 }
61 if (obj.type === 'MemberExpression') {
62 if (obj.property.type === 'Literal' ||
63 (obj.property.type === 'Identifier' && !obj.computed)) {
64 memberExpressions.push(obj);
65 var name = JSON.stringify(obj.property.type === 'Literal' ?
66 obj.property.raw : obj.property.name);
67 if (!propertys[name]) {
68 propertys[name] = '$jfogs$' + (guid++);
69 names.push(propertys[name]);
70 expressions.push(name);
71 }
72 }
73 }
74 for (var key in obj) {
75 if (typeof obj[key] === 'object') {
76 scan(obj[key]);
77 }
78 }
79 }
80 scan(syntax);
81 memberExpressions.sort(function (a, b) {
82 return b.property.range[0] - a.property.range[1];
83 });
84 if (options.cross) {
85 expressions.forEach(function (expression, index) {
86 propertys[expression] = '$jfogs$prop.' + crossCode(index);
87 });
88 }
89 memberExpressions.forEach(function (obj) {
90 var name = JSON.stringify(obj.property.type === 'Literal' ?
91 obj.property.raw : obj.property.name);
92 if (obj.property.type === 'Literal') {
93 code = code.slice(0, obj.property.range[0]) + propertys[name] +
94 code.slice(obj.property.range[1]);
95 }
96 else {
97 code = code.slice(0, obj.object.range[1]) +
98 '[' + propertys[name] + ']' +
99 code.slice(obj.property.range[1]);
100 }
101 });
102 var decryption = '';
103 switch (options.type) {
104 case 'zero':
105 expressions = expressions.map(function (item) {
106 var t = parseInt('10000000', 2);
107 return '"' + encodeUnicode(JSON.parse(item)).replace(/[^]/g, function (all) {
108 return (t + all.charCodeAt()).toString(2).substring(1).replace(/[^]/g, function (n) {
109 return {
110 0: '\u200c',
111 1: '\u200d'
112 }[n];
113 });
114 }) + '"';
115 });
116 decryption = '' +
117 'var $jfogs$argv = arguments;\n' +
118 'for (var $jfogs$i = 0; $jfogs$i < $jfogs$argv.length; $jfogs$i++) {\n' +
119 ' $jfogs$argv[$jfogs$i] = $jfogs$argv[$jfogs$i].replace(/./g,\n' +
120 ' function (a) {\n' +
121 ' return {\n' +
122 ' "\u200c": 0,\n' +
123 ' "\u200d": 1\n' +
124 ' }[a]\n' +
125 ' }\n' +
126 ' ).replace(/.{7}/g, function (a) {\n' +
127 ' return String.fromCharCode(parseInt(a, 2));\n' +
128 ' });\n' +
129 '}\n';
130 break;
131 case 'reverse':
132 expressions = expressions.map(function (item) {
133 return item.split('').reverse().join('');
134 });
135 decryption = '' +
136 'var $jfogs$argv = arguments;\n' +
137 'for (var $jfogs$i = 0; $jfogs$i < $jfogs$argv.length; $jfogs$i++) {\n' +
138 ' $jfogs$argv[$jfogs$i] = $jfogs$argv[$jfogs$i].split("").reverse().join("");\n' +
139 '}\n';
140 break;
141 }
142 if (options.cross) {
143 decryption += 'var $jfogs$prop = {};\n';
144 expressions.forEach(function (expression, index) {
145 decryption += util.format('$jfogs$prop.%s = %s;\n', crossCode(index), names[index]);
146 });
147 }
148 return util.format('(function (%s) {\n%s\n%s\n})(%s);',
149 names.join(', '),
150 decryption,
151 code,
152 expressions.join(', ')
153 );
154}
155exports.obfuscate = obfuscate;
\No newline at end of file