js-confuser-vm 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -3
- package/README.MD +186 -107
- package/dist/build-runtime.js +59 -0
- package/dist/compiler.js +1777 -0
- package/dist/index.js +10 -0
- package/dist/minify.js +18 -0
- package/dist/options.js +1 -0
- package/dist/runtime.js +826 -0
- package/dist/transforms/bytecode/aliasedOpcodes.js +140 -0
- package/dist/transforms/bytecode/concealConstants.js +31 -0
- package/dist/transforms/bytecode/macroOpcodes.js +164 -0
- package/dist/transforms/bytecode/resolveContants.js +106 -0
- package/dist/transforms/bytecode/resolveLabels.js +80 -0
- package/dist/transforms/bytecode/selfModifying.js +108 -0
- package/dist/transforms/bytecode/specializedOpcodes.js +113 -0
- package/dist/transforms/runtime/aliasedOpcodes.js +134 -0
- package/dist/transforms/runtime/macroOpcodes.js +88 -0
- package/dist/transforms/runtime/minify.js +1 -0
- package/dist/transforms/runtime/shuffleOpcodes.js +20 -0
- package/dist/transforms/runtime/specializedOpcodes.js +107 -0
- package/{src/transforms/utils/op-utils.ts → dist/transforms/utils/op-utils.js} +25 -26
- package/dist/transforms/utils/random-utils.js +27 -0
- package/dist/types.js +15 -0
- package/dist/utils/op-utils.js +29 -0
- package/dist/utils/random-utils.js +27 -0
- package/dist/utilts.js +3 -0
- package/index.ts +10 -8
- package/jest.config.js +10 -0
- package/package.json +3 -4
- package/src/build-runtime.ts +7 -1
- package/src/compiler.ts +2395 -2069
- package/src/options.ts +2 -0
- package/src/runtime.ts +838 -771
- package/src/transforms/bytecode/aliasedOpcodes.ts +158 -0
- package/src/transforms/bytecode/concealConstants.ts +52 -0
- package/src/transforms/bytecode/macroOpcodes.ts +32 -15
- package/src/transforms/bytecode/resolveContants.ts +87 -16
- package/src/transforms/bytecode/selfModifying.ts +3 -3
- package/src/transforms/bytecode/specializedOpcodes.ts +58 -29
- package/src/transforms/runtime/aliasedOpcodes.ts +191 -0
- package/src/transforms/runtime/shuffleOpcodes.ts +1 -1
- package/src/transforms/runtime/specializedOpcodes.ts +39 -24
- package/src/utils/op-utils.ts +33 -0
- /package/src/{transforms/utils → utils}/random-utils.ts +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,59 @@
|
|
|
1
|
-
## `0.0.
|
|
1
|
+
## `0.0.6` Register based
|
|
2
|
+
|
|
3
|
+
- Switched from stack-based to register-based VM.
|
|
4
|
+
|
|
5
|
+
- `Specialized Opcodes` now applies to any fixed-size instruction, instead of just singular operands.
|
|
6
|
+
- - Specialized Opcodes never applies to N-sized instructions, such as `MAKE_CLOSURE`, `BUILD_ARRAY`, `CALL`, etc.
|
|
7
|
+
|
|
8
|
+
- `Macro Opcodes` can now include jumping/terminating opcodes if it's the last instruction in the sequence.
|
|
9
|
+
|
|
10
|
+
- Added new option `aliasedOpcodes` which creates duplicate opcodes, including variants with shuffled operand order.
|
|
11
|
+
|
|
12
|
+
```js
|
|
13
|
+
// Input Code
|
|
14
|
+
console.log("Hello, world!");
|
|
15
|
+
|
|
16
|
+
// Before
|
|
17
|
+
// [2, 1, 0], LOAD_GLOBAL reg[1] = console 1:0-1:7
|
|
18
|
+
// [0, 2, 1], LOAD_CONST reg[2] = "log" 1:0-1:28
|
|
19
|
+
// [8, 3, 1, 2], GET_PROP [3, 1, 2] 1:0-1:28
|
|
20
|
+
// [0, 4, 2], LOAD_CONST reg[4] = "Hello, world!" 1:12-1:27
|
|
21
|
+
// [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:28
|
|
22
|
+
// [0, 1, 3], LOAD_CONST reg[1] = undefined
|
|
23
|
+
// [45, 1], RETURN reg[1]
|
|
24
|
+
|
|
25
|
+
// What the opcode "LOAD_GLOBAL" looks like:
|
|
26
|
+
case OP.LOAD_GLOBAL:
|
|
27
|
+
var dst = this._operand();
|
|
28
|
+
frame.regs[dst] = this.globals[this.constants[this._operand()]];
|
|
29
|
+
break;
|
|
30
|
+
|
|
31
|
+
// After
|
|
32
|
+
// [52040, 0, 1], ALIAS_LOAD_GLOBAL_1_0 [0, 1] 1:0-1:7
|
|
33
|
+
// [24862, 1, 2], ALIAS_LOAD_CONST_1_0 [1, 2] 1:0-1:28
|
|
34
|
+
// [25202, 1, 2, 3], ALIAS_GET_PROP_1_2_0 [1, 2, 3] 1:0-1:28
|
|
35
|
+
// [24862, 2, 4], ALIAS_LOAD_CONST_1_0 [2, 4] 1:12-1:27
|
|
36
|
+
// [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:28
|
|
37
|
+
// [24862, 3, 1], ALIAS_LOAD_CONST_1_0 [3, 1]
|
|
38
|
+
// [51807, 1], ALIAS_RETURN_0 1
|
|
39
|
+
|
|
40
|
+
// What the opcode "ALIAS_LOAD_GLOBAL_1_0" (52040) looks like:
|
|
41
|
+
case 52040:
|
|
42
|
+
// ALIAS_LOAD_GLOBAL_1_0 (order: [1,0])
|
|
43
|
+
let _unsortedOperands = [this._operand(), this._operand()];
|
|
44
|
+
let _operands = [_unsortedOperands[1], _unsortedOperands[0]];
|
|
45
|
+
var dst = _operands[0];
|
|
46
|
+
frame.regs[dst] = this.globals[this.constants[_operands[1]]];
|
|
47
|
+
break;
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- Added new option `concealConstants` which XOR decrypts numbers and strings at runtime.
|
|
51
|
+
|
|
52
|
+
- Top level variables are now renamed and not exposed globally. To export a global function, you can use `window.MyGlobalFunction = function(){...}`
|
|
53
|
+
|
|
54
|
+
- Accessing an undeclared global variable will throw a ReferenceError
|
|
55
|
+
|
|
56
|
+
## `0.0.5` Generated Opcodes
|
|
2
57
|
|
|
3
58
|
- Added new option `specializedOpcodes` which creates specialized opcodes for commonly used opcode+operand pairs.
|
|
4
59
|
|
|
@@ -15,8 +70,8 @@ console.log("Hello world!");
|
|
|
15
70
|
// [14], POP 1:0-1:28
|
|
16
71
|
|
|
17
72
|
// What the opcode "LOAD_GLOBAL" looks like:
|
|
18
|
-
case OP.
|
|
19
|
-
this._push(this.constants[this._operand()]);
|
|
73
|
+
case OP.LOAD_GLOBAL:
|
|
74
|
+
this._push(this.globals[this.constants[this._operand()]]);
|
|
20
75
|
break;
|
|
21
76
|
|
|
22
77
|
// After
|
package/README.MD
CHANGED
|
@@ -37,10 +37,12 @@ JsConfuserVM.obfuscate(`
|
|
|
37
37
|
target: "browser", // or "node"
|
|
38
38
|
randomizeOpcodes: true, // randomize the opcode numbers?
|
|
39
39
|
shuffleOpcodes: true, // shuffle order of opcode handlers in the runtime?
|
|
40
|
-
encodeBytecode: true, // encode bytecode?
|
|
40
|
+
encodeBytecode: true, // encode the bytecode array?
|
|
41
|
+
concealConstants: true, // conceal strings and integers in the constant pool?
|
|
41
42
|
selfModifying: true, // do self-modifying bytecode for function bodies?
|
|
42
43
|
macroOpcodes: true, // create combined opcodes for repeated instruction sequences?
|
|
43
44
|
specializedOpcodes: true, // create specialized opcodes for commonly used opcode+operand pairs?
|
|
45
|
+
aliasedOpcodes: true, // create duplicate opcodes for commonly used opcodes?
|
|
44
46
|
timingChecks: true, // add timing checks to detect debuggers?
|
|
45
47
|
minify: true // pass final output through Google Closure Compiler? (Renames VM class properties)
|
|
46
48
|
}).then(result => {
|
|
@@ -48,22 +50,25 @@ JsConfuserVM.obfuscate(`
|
|
|
48
50
|
})
|
|
49
51
|
|
|
50
52
|
/*
|
|
51
|
-
var
|
|
52
|
-
function
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
for(e=0;e<
|
|
60
|
-
|
|
61
|
-
a.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
typeof
|
|
65
|
-
|
|
66
|
-
|
|
53
|
+
var c=Symbol();function ha(b,k){this.A=b;this.H=k;this.I=!1;this.J=void 0}function ia(b){return b.I?b.J:b.A.g[b.H]}function ja(b,k){b.I?b.J=k:b.A.g[b.H]=k}function y(b){this.v=b;this.D=[];this.prototype={}}function B(b,k,a){this.K=b;this.g=Array(b.v.L).fill(void 0);this.i=b.v.P;this.V=k!==void 0?k:void 0;this.G=a!==void 0?a:0;this.m=null;this.F=[]}function C(b,k,a,g,t){this.o=b;this.l=g;this.u=t;this.j=[];this.B=[];this.h=new B(new y({C:0,L:a,P:k}),void 0,0)}function D(b){return b.o[b.h.i++]}
|
|
54
|
+
function ka(b,k,a){for(var g=0;g<b.B.length;g++){var t=b.B[g];if(t.A===k&&t.H===a)return t}t=new ha(k,a);b.B.push(t);return t}function R(b,k){b.B=b.B.filter(function(a){return a.A===k?(a.J=a.A.g[a.H],a.I=!0,!1):!0})}
|
|
55
|
+
function S(b){for(var k=performance.now();;){var a=b.h;if(a.i>=b.o.length)break;var g=b.o[a.i++],t=performance.now(),la=t-k>1E3;k=t;if(la){for(var e=0;e<b.o.length;e++)b.o[e]=0;a.g.fill(void 0);g=50848;a.i=b.o.length}try{switch(g){case 6517:var d=D(b),f=a.g[D(b)];a.g[d]=f===a.g[D(b)];break;case 50054:d=D(b);var p=a.g[D(b)],x=D(b),m=Array(x);for(e=0;e<x;e++)m[e]=a.g[D(b)];if(p&&p[c]){var v=p[c],r=new B(v,b.u,d);for(e=0;e<m.length;e++)r.g[e]=m[e];r.g[v.v.C]=m;b.j.push(b.h);b.h=r}else a.g[d]=p.apply(null,
|
|
56
|
+
m);break;case 5504:var l=a.g[D(b)],n=a.g[D(b)],ma=a.g[D(b)],z=Object.getOwnPropertyDescriptor(l,n);a={set:ma,configurable:!0,enumerable:!0};z&&typeof z.get==="function"&&(a.get=z.get);Object.defineProperty(l,n,a);break;case 36488:d=6;a.g[d]=b.l[1];break;case 29667:d=D(b);f=a.g[D(b)];a.g[d]=f|a.g[D(b)];break;case 29500:d=D(b);a.g[d]=+a.g[D(b)];break;case 16142:d=D(b);var V=a.g[D(b)];p=a.g[D(b)];x=D(b);m=Array(x);for(e=0;e<x;e++)m[e]=a.g[D(b)];if(p&&p[c]){v=p[c];r=new B(v,V,d);for(e=0;e<m.length;e++)r.g[e]=
|
|
57
|
+
m[e];r.g[v.v.C]=m;b.j.push(b.h);b.h=r}else a.g[d]=p.apply(V,m);break;case 2036:d=D(b);l=a.g[D(b)];n=a.g[D(b)];a.g[d]=l[n];break;case 50848:a.i=D(b);break;case 46964:d=3;a.g[d]=b.l[3];break;case 17567:d=D(b);f=a.g[D(b)];a.g[d]=f/a.g[D(b)];break;case 46176:var h=a.g[3];R(b,a);if(b.j.length===0)return h;a.m===null||typeof h==="object"&&h!==null||(h=a.m);var u=b.j.pop();u.g[a.G]=h;b.h=u;break;case 40235:d=D(b);f=a.g[D(b)];a.g[d]=f!=a.g[D(b)];break;case 2835:d=5;a.g[d]=b.l[0];break;case 53433:var na=D(b),
|
|
58
|
+
W=D(b),oa=D(b);for(a=W;a<oa;a++)b.o[na+(a-W)]=b.o[a];break;case 42352:l=a.g[D(b)];n=a.g[D(b)];var E=a.g[D(b)];Reflect.set(l,n,E);break;case 23743:d=4;a.g[d]=a.g[5];break;case 50800:d=1;a.g[d]=a.g[3];break;case 36349:throw a.g[D(b)];case 53101:d=4;a.g[d]=b.l[1];break;case 545:d=D(b);f=a.g[D(b)];a.g[d]=f>a.g[D(b)];break;case 55196:var pa=D(b);b.u[b.l[pa]]=a.g[D(b)];break;case 22757:var w=4,q=76;a.g[w]||(a.i=q);break;case 58785:d=3;a.g[d]=b.l[1];break;case 25598:d=D(b);a.g[d]=a.V;break;default:throw Error("Unknown opcode: "+
|
|
59
|
+
g+" at pc "+(a.i-1));case 53665:d=5;l=a.g[3];n=a.g[4];a.g[d]=l[n];break;case 9914:d=3;a.g[d]=a.g[4];break;case 14527:d=D(b);f=a.g[D(b)];a.g[d]=f^a.g[D(b)];break;case 4831:d=D(b);a.g[d]=-a.g[D(b)];break;case 41053:d=D(b);a.g[d]=typeof a.g[D(b)];break;case 23985:d=D(b);D(b);a.g[d]=void 0;break;case 9348:d=3;a.g[d]=b.u[b.l[4]];break;case 1904:a.F.push({T:D(b),R:D(b),S:b.j.length});break;case 14261:w=9;q=107;a.g[w]||(a.i=q);break;case 44576:d=D(b);l=a.g[D(b)];n=a.g[D(b)];a.g[d]=delete l[n];break;case 6789:w=
|
|
60
|
+
D(b);q=D(b);a.g[w]||(a.i=q);break;case 17207:w=D(b);q=D(b);a.g[w]&&(a.i=q);break;case 21753:d=D(b);var X=b.l[D(b)];E=Object.prototype.hasOwnProperty.call(b.u,X)?b.u[X]:void 0;a.g[d]=typeof E;break;case 9073:d=3;a.g[d]=a.g[6];break;case 26633:d=D(b);a.g[d]=b.l[D(b)];break;case 57348:d=D(b);f=a.g[D(b)];a.g[d]=f>=a.g[D(b)];break;case 43802:d=D(b);var Y=D(b),Z=Array(Y);for(e=0;e<Y;e++)Z[e]=a.g[D(b)];a.g[d]=Z;break;case 38611:d=4;a.g[d]=b.l[5];break;case 43097:d=5;a.g[d]=b.l[2];break;case 16365:d=D(b);
|
|
61
|
+
a.g[d]=b.u[b.l[D(b)]];break;case 63592:d=D(b);p=a.g[D(b)];x=D(b);m=Array(x);for(e=0;e<x;e++)m[e]=a.g[D(b)];if(p&&p[c]){v=p[c];var aa=Object.create(v.prototype||null);r=new B(v,aa,d);r.m=aa;for(e=0;e<m.length;e++)r.g[e]=m[e];r.g[v.v.C]=m;b.j.push(b.h);b.h=r}else a.g[d]=Reflect.construct(p,m);break;case 56057:d=3;a.g[d]=b.l[2];break;case 20064:d=3;a.g[d]=a.g[2];break;case 46958:d=D(b);f=a.g[D(b)];a.g[d]=f==a.g[D(b)];break;case 5066:d=D(b);f=a.g[D(b)];a.g[d]=f-a.g[D(b)];break;case 16426:h=a.g[4];R(b,
|
|
62
|
+
a);if(b.j.length===0)return h;a.m===null||typeof h==="object"&&h!==null||(h=a.m);u=b.j.pop();u.g[a.G]=h;b.h=u;break;case 47875:a.i=45;break;case 51418:d=2;a.g[d]=a.g[5];break;case 25520:d=D(b);f=a.g[D(b)];a.g[d]=f*a.g[D(b)];break;case 41854:d=D(b);l=a.g[D(b)];g=[];if(l!==null&&l!==void 0)for(var ba=Object.create(null),F=Object(l);F!==null;){var ca=Object.getOwnPropertyNames(F);for(e=0;e<ca.length;e++){var H=ca[e];if(!(H in ba)){ba[H]=!0;var da=Object.getOwnPropertyDescriptor(F,H);da&&da.enumerable&&
|
|
63
|
+
g.push(H)}}F=Object.getPrototypeOf(F)}a.g[d]={N:g,O:0};break;case 45737:d=D(b);var qa=D(b);g={};for(e=0;e<qa;e++)n=a.g[D(b)],E=a.g[D(b)],g[n]=E;a.g[d]=g;break;case 3520:d=D(b);f=a.g[D(b)];a.g[d]=f<<a.g[D(b)];break;case 34870:d=D(b);a.g[d]=!a.g[D(b)];break;case 11596:d=D(b);a.g[d]=~a.g[D(b)];break;case 16818:w=9;q=25;a.g[w]||(a.i=q);break;case 38378:d=D(b);a.g[d]=ia(a.K.D[D(b)]);break;case 50935:d=D(b);f=a.g[D(b)];a.g[d]=f>>>a.g[D(b)];break;case 7720:d=0;a.g[d]=a.g[7];break;case 61003:d=8;a.g[d]=b.l[1];
|
|
64
|
+
break;case 40007:d=D(b);f=a.g[D(b)];a.g[d]=f&a.g[D(b)];break;case 48619:debugger;break;case 21396:d=D(b);a.g[d]=a.g[D(b)];break;case 34467:d=D(b);a.g[d]=D(b);break;case 26361:h=a.g[D(b)];R(b,a);if(b.j.length===0)return h;a.m===null||typeof h==="object"&&h!==null||(h=a.m);u=b.j.pop();u.g[a.G]=h;b.h=u;break;case 7585:l=a.g[D(b)];n=a.g[D(b)];var ra=a.g[D(b)];z=Object.getOwnPropertyDescriptor(l,n);a={get:ra,configurable:!0,enumerable:!0};z&&typeof z.set==="function"&&(a.set=z.set);Object.defineProperty(l,
|
|
65
|
+
n,a);break;case 23121:var sa=D(b);ja(a.K.D[sa],a.g[D(b)]);break;case 51231:d=D(b);f=a.g[D(b)];a.g[d]=f%a.g[D(b)];break;case 22797:d=D(b);var I=a.g[D(b)],ta=D(b);I.O>=I.N.length?a.i=ta:a.g[d]=I.N[I.O++];break;case 37593:a.i=9;break;case 504:d=D(b);f=a.g[D(b)];a.g[d]=f>>a.g[D(b)];break;case 5174:h=a.g[5];R(b,a);if(b.j.length===0)return h;a.m===null||typeof h==="object"&&h!==null||(h=a.m);u=b.j.pop();u.g[a.G]=h;b.h=u;break;case 9939:d=5;a.g[d]=a.g[0];break;case 41972:d=5;f=a.g[2];a.g[d]=f+a.g[4];break;
|
|
66
|
+
case 49424:d=7;f=a.g[0];a.g[d]=f-a.g[6];break;case 26807:d=D(b);f=a.g[D(b)];a.g[d]=f<a.g[D(b)];break;case 21843:d=4;a.g[d]=a.g[0];break;case 19103:d=D(b);f=a.g[D(b)];a.g[d]=f in a.g[D(b)];break;case 40335:d=D(b);l=a.g[D(b)];var M=a.g[D(b)];if(typeof M==="function")a.g[d]=l instanceof M;else{var ua=M.prototype;q=Object.getPrototypeOf(l);for(g=!1;q!==null;){if(q===ua){g=!0;break}q=Object.getPrototypeOf(q)}a.g[d]=g}break;case 47743:d=D(b);f=a.g[D(b)];a.g[d]=f+a.g[D(b)];break;case 27740:d=4;f=a.g[2];
|
|
67
|
+
a.g[d]=f<=a.g[3];break;case 51318:d=D(b);f=a.g[D(b)];a.g[d]=f!==a.g[D(b)];break;case 64752:d=2;a.g[d]=a.g[3];break;case 1927:a.i=91;break;case 22484:d=5;f=a.g[2];a.g[d]=f+a.g[3];break;case 64028:d=D(b);var va=D(b),wa=D(b),xa=D(b),ea=D(b),J=Array(ea);for(e=0;e<ea;e++){var ya=D(b),za=D(b);J[e]={U:ya,M:za}}var G=new y({C:wa,L:xa,P:va,W:J});for(e=0;e<J.length;e++){var N=J[e];N.U?G.D.push(ka(b,a,N.M)):G.D.push(a.K.D[N.M])}var K=b,Q=function(A){return function(){for(var O=Array.prototype.slice.call(arguments),
|
|
68
|
+
fa=new C(K.o,0,A.v.L,K.l,K.u),P=new B(A,this==null?K.u:this,0),L=0;L<O.length;L++)P.g[L]=O[L];P.g[A.v.C]=O;fa.h=P;return S(fa)}}(G);Q[c]=G;Q.prototype=G.prototype;a.g[d]=Q;break;case 30313:d=9;f=a.g[5];a.g[d]=f>a.g[8];break;case 50428:a.F.pop();break;case 35999:d=D(b),f=a.g[D(b)],a.g[d]=f<=a.g[D(b)]}}catch(A){a=null;for(g=b.h;;){if(g.F.length>0){a=g;break}R(b,g);if(b.j.length===0)break;g=b.j.pop();b.h=g}if(!a)throw A;g=a.F.pop();b.j.length=g.S;a.g[g.R]=A;a.i=g.T;b.h=a}}}var T={},U;
|
|
69
|
+
for(U of Object.getOwnPropertyNames(globalThis))T[U]=globalThis[U];typeof window!=="undefined"&&(T.window=window);T.undefined=void 0;T.Infinity=Infinity;T.NaN=NaN;
|
|
70
|
+
S(new C(function(b){b=typeof Buffer!=="undefined"?Buffer.from(b,"base64"):Uint8Array.from(atob(b),function(g){return g.charCodeAt(0)});for(var k=new Uint16Array(b.length/2),a=0;a<k.length;a++)k[a]=b[a*2]|b[a*2+1]<<8;return k}("udAEAHIAdwAOP263K53rveu9udANAHcAgwCUUyud6pWUUx/IHPo3Q/2Nj50hArdobre50B0AgwCGAMoT+WYc+rnQJACGAI8AIQJo+Dxzo4afRO0/98YhAqDGudAxAI8AoQCwY7FdoR0NWX+6PHNRWuu9f7qUU7dot2ifRJ+MBOCAFYAVt2i50EcAoQCmAJ+M/Y1ut3ClDVm50FAApgCoAF2gHPq50FYAqACtAONzoMbfEvQHqbK50F8ArQC5AONzNoj4AXAH43P3xpRT/MSc14bDdsjADbnQbwC5ALwAPHM3Q+0/EwvayIiOcSNTVdMmiI4QwSgeS+5pdrJB1Fe/XPD8uibZkipAWag2FBz6AwBSAAEACgAAAHDGoeXw/HS3XGzlWIQk05ah0YbDBgABAAEAAgAOPwcAAwAFAAIAAgAGAGBObc/0o9rIA7v52mC0EwvayIiOcSNTVdMmiI4QwSgeS+5pdrU31Fe/XPD8uiaHBypAWag2FA=="),32,
|
|
71
|
+
0,[0,1,void 0,25,"console","log"],T));
|
|
67
72
|
*/
|
|
68
73
|
```
|
|
69
74
|
|
|
@@ -115,6 +120,7 @@ T(new u(function(a){a=typeof Buffer!=="undefined"?Buffer.from(a,"base64"):Uint8A
|
|
|
115
120
|
- [ ] dead bytecode insertion
|
|
116
121
|
- [x] macro opcodes (Combine multiple opcodes into a "macro opcode")
|
|
117
122
|
- [x] specialized opcodes (Create specific opcodes for opcode+operand pairs)
|
|
123
|
+
- [x] aliased opcodes (create duplicate opcodes, including variants with shuffled operand order)
|
|
118
124
|
- [x] encoded bytecode array
|
|
119
125
|
- [x] self-modifying bytecode
|
|
120
126
|
- [x] timing checks
|
|
@@ -142,10 +148,22 @@ Encodes the bytecode array.
|
|
|
142
148
|
|
|
143
149
|
```js
|
|
144
150
|
// Before
|
|
145
|
-
var BYTECODE = [
|
|
151
|
+
var BYTECODE = [2, 1, 0, 0, 2, 1, 8, 3, 1, 2, 0, 4, 2, 43, 5, 1, 3, 1, 4, 0, 1, 3, 45, 1];
|
|
146
152
|
|
|
147
153
|
// After
|
|
148
|
-
var BYTECODE = "
|
|
154
|
+
var BYTECODE = "AgABAAAAAAACAAEACAADAAEAAgAAAAQAAgArAAUAAQADAAEABAAAAAEAAwAtAAEA";
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### `concealConstants` (true/false)
|
|
158
|
+
|
|
159
|
+
Conceals strings and integers in the constant pool.
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
// Before
|
|
163
|
+
var CONSTANTS = [/* 0 */"console", /* 1 */"log", /* 2 */"Hello world!", /* 3 */undefined];
|
|
164
|
+
|
|
165
|
+
// After
|
|
166
|
+
var CONSTANTS = [/* 0 */"DaQApB6kAqQdpB+kEaQ=", /* 1 */"TCFOIUUh", /* 2 */"kKK8orait6Kzov2iqaKwopKijaKGosKi", /* 3 */undefined];
|
|
149
167
|
```
|
|
150
168
|
|
|
151
169
|
#### `macroOpcodes` (true/false)
|
|
@@ -158,44 +176,57 @@ console.log("Hello world!");
|
|
|
158
176
|
console.log("Hello world!");
|
|
159
177
|
|
|
160
178
|
// Before
|
|
161
|
-
// [
|
|
162
|
-
// [0, 1],
|
|
163
|
-
// [
|
|
164
|
-
// [0, 2],
|
|
165
|
-
// [
|
|
166
|
-
// [
|
|
167
|
-
// [
|
|
168
|
-
// [
|
|
169
|
-
// [
|
|
170
|
-
// [
|
|
171
|
-
// [12, 1], CALL_METHOD (1 args) 2:0-2:27
|
|
172
|
-
// [14], POP 2:0-2:28
|
|
179
|
+
// [2, 1, 0], LOAD_GLOBAL reg[1] = console 2:0-2:7
|
|
180
|
+
// [0, 2, 1], LOAD_CONST reg[2] = "log" 2:0-2:27
|
|
181
|
+
// [8, 3, 1, 2], GET_PROP [3, 1, 2] 2:0-2:27
|
|
182
|
+
// [0, 4, 2], LOAD_CONST reg[4] = "Hello world!" 2:12-2:26
|
|
183
|
+
// [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)2:0-2:27
|
|
184
|
+
// [2, 1, 0], LOAD_GLOBAL reg[1] = console 3:0-3:7
|
|
185
|
+
// [0, 2, 1], LOAD_CONST reg[2] = "log" 3:0-3:27
|
|
186
|
+
// [8, 3, 1, 2], GET_PROP [3, 1, 2] 3:0-3:27
|
|
187
|
+
// [0, 4, 2], LOAD_CONST reg[4] = "Hello world!" 3:12-3:26
|
|
188
|
+
// [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)3:0-3:27
|
|
173
189
|
|
|
174
190
|
// After
|
|
175
|
-
// [
|
|
176
|
-
// [
|
|
177
|
-
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
191
|
+
// [5074, 1, 0, 2, 1, 3, 1, 2, 4, 2, 5, 1, 3, 1, 4], LOAD_GLOBAL,LOAD_CONST,GET_PROP,LOAD_CONST,CALL_METHOD [1, 0, 2, 1, 3, 1, 2, 4, 2, 5, 1, 3, 1, 4]2:0-2:7
|
|
192
|
+
// [5074, 1, 0, 2, 1, 3, 1, 2, 4, 2, 5, 1, 3, 1, 4], LOAD_GLOBAL,LOAD_CONST,GET_PROP,LOAD_CONST,CALL_METHOD [1, 0, 2, 1, 3, 1, 2, 4, 2, 5, 1, 3, 1, 4]3:0-3:7
|
|
193
|
+
|
|
194
|
+
// What the opcode "LOAD_GLOBAL,LOAD_CONST,GET_PROP,LOAD_CONST,CALL_METHOD" (5074) looks like:
|
|
195
|
+
case 5074:
|
|
196
|
+
// LOAD_GLOBAL
|
|
197
|
+
var dst = this._operand();
|
|
198
|
+
frame.regs[dst] = this.globals[this.constants[this._operand()]];
|
|
199
|
+
// LOAD_CONST
|
|
200
|
+
var dst = this._operand();
|
|
201
|
+
frame.regs[dst] = this.constants[this._operand()];
|
|
202
|
+
// GET_PROP
|
|
203
|
+
// dst = regs[obj][regs[key]]
|
|
204
|
+
var dst = this._operand();
|
|
205
|
+
var obj = frame.regs[this._operand()];
|
|
206
|
+
var key = frame.regs[this._operand()];
|
|
207
|
+
frame.regs[dst] = obj[key];
|
|
208
|
+
// LOAD_CONST
|
|
209
|
+
var dst = this._operand();
|
|
210
|
+
frame.regs[dst] = this.constants[this._operand()];
|
|
211
|
+
// CALL_METHOD
|
|
212
|
+
// dst, receiverReg, calleeReg, argc, [argReg...]
|
|
213
|
+
var dst = this._operand();
|
|
214
|
+
var receiver = frame.regs[this._operand()];
|
|
215
|
+
var callee = frame.regs[this._operand()];
|
|
216
|
+
var argc = this._operand();
|
|
217
|
+
var args = new Array(argc);
|
|
218
|
+
for (var i = 0; i < argc; i++) args[i] = frame.regs[this._operand()];
|
|
219
|
+
if (callee && callee[CLOSURE_SYM]) {
|
|
220
|
+
var c = callee[CLOSURE_SYM];
|
|
221
|
+
var f = new Frame(c, frame._pc, frame, receiver, dst);
|
|
222
|
+
for (var i = 0; i < args.length; i++) f.regs[i] = args[i];
|
|
223
|
+
f.regs[c.fn.paramCount] = args;
|
|
224
|
+
this._frameStack.push(this._currentFrame);
|
|
225
|
+
this._currentFrame = f;
|
|
226
|
+
} else {
|
|
227
|
+
frame.regs[dst] = callee.apply(receiver, args);
|
|
228
|
+
}
|
|
229
|
+
break;
|
|
199
230
|
```
|
|
200
231
|
|
|
201
232
|
#### `specializedOpcodes` (true/false)
|
|
@@ -207,30 +238,72 @@ Creates specialized opcodes for commonly used opcode+operand pairs.
|
|
|
207
238
|
console.log("Hello world!");
|
|
208
239
|
|
|
209
240
|
// Before
|
|
210
|
-
// [
|
|
211
|
-
// [0, 1],
|
|
212
|
-
// [
|
|
213
|
-
// [0, 2],
|
|
214
|
-
// [
|
|
215
|
-
// [14], POP 1:0-1:28
|
|
241
|
+
// [2, 1, 0], LOAD_GLOBAL reg[1] = console 1:0-1:7
|
|
242
|
+
// [0, 2, 1], LOAD_CONST reg[2] = "log" 1:0-1:27
|
|
243
|
+
// [8, 3, 1, 2], GET_PROP [3, 1, 2] 1:0-1:27
|
|
244
|
+
// [0, 4, 2], LOAD_CONST reg[4] = "Hello world!" 1:12-1:26
|
|
245
|
+
// [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:27
|
|
216
246
|
|
|
217
247
|
// What the opcode "LOAD_GLOBAL" looks like:
|
|
218
248
|
case OP.LOAD_CONST:
|
|
219
|
-
|
|
249
|
+
var dst = this._operand();
|
|
250
|
+
frame.regs[dst] = this.constants[this._operand()];
|
|
251
|
+
break;
|
|
252
|
+
|
|
253
|
+
// After
|
|
254
|
+
// [16316], LOAD_GLOBAL_1_0 1:0-1:7
|
|
255
|
+
// [43765], LOAD_CONST_2_1 1:0-1:27
|
|
256
|
+
// [58568], GET_PROP_3_1_2 1:0-1:27
|
|
257
|
+
// [35110], LOAD_CONST_4_2 1:12-1:26
|
|
258
|
+
// [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:27
|
|
259
|
+
|
|
260
|
+
// What the opcode "LOAD_GLOBAL_1_0" (16316) looks like:
|
|
261
|
+
case 16316:
|
|
262
|
+
// LOAD_GLOBAL_1_0 (specialized)
|
|
263
|
+
var dst = 1;
|
|
264
|
+
frame.regs[dst] = this.globals[this.constants[0]];
|
|
265
|
+
break;
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
#### `aliasedOpcodes` (true/false)
|
|
269
|
+
|
|
270
|
+
Creates duplicate opcodes, including variants with shuffled operand order.
|
|
271
|
+
|
|
272
|
+
```js
|
|
273
|
+
// Input Code
|
|
274
|
+
console.log("Hello, world!");
|
|
275
|
+
|
|
276
|
+
// Before
|
|
277
|
+
// [2, 1, 0], LOAD_GLOBAL reg[1] = console 1:0-1:7
|
|
278
|
+
// [0, 2, 1], LOAD_CONST reg[2] = "log" 1:0-1:28
|
|
279
|
+
// [8, 3, 1, 2], GET_PROP [3, 1, 2] 1:0-1:28
|
|
280
|
+
// [0, 4, 2], LOAD_CONST reg[4] = "Hello, world!" 1:12-1:27
|
|
281
|
+
// [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:28
|
|
282
|
+
// [0, 1, 3], LOAD_CONST reg[1] = undefined
|
|
283
|
+
// [45, 1], RETURN reg[1]
|
|
284
|
+
|
|
285
|
+
// What the opcode "LOAD_GLOBAL" looks like:
|
|
286
|
+
case OP.LOAD_GLOBAL:
|
|
287
|
+
var dst = this._operand();
|
|
288
|
+
frame.regs[dst] = this.globals[this.constants[this._operand()]];
|
|
220
289
|
break;
|
|
221
290
|
|
|
222
291
|
// After
|
|
223
|
-
// [
|
|
224
|
-
// [
|
|
225
|
-
// [
|
|
226
|
-
// [
|
|
227
|
-
// [
|
|
228
|
-
// [
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
292
|
+
// [52040, 0, 1], ALIAS_LOAD_GLOBAL_1_0 [0, 1] 1:0-1:7
|
|
293
|
+
// [24862, 1, 2], ALIAS_LOAD_CONST_1_0 [1, 2] 1:0-1:28
|
|
294
|
+
// [25202, 1, 2, 3], ALIAS_GET_PROP_1_2_0 [1, 2, 3] 1:0-1:28
|
|
295
|
+
// [24862, 2, 4], ALIAS_LOAD_CONST_1_0 [2, 4] 1:12-1:27
|
|
296
|
+
// [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:28
|
|
297
|
+
// [24862, 3, 1], ALIAS_LOAD_CONST_1_0 [3, 1]
|
|
298
|
+
// [51807, 1], ALIAS_RETURN_0 1
|
|
299
|
+
|
|
300
|
+
// What the opcode "ALIAS_LOAD_GLOBAL_1_0" (52040) looks like:
|
|
301
|
+
case 52040:
|
|
302
|
+
// ALIAS_LOAD_GLOBAL_1_0 (order: [1,0])
|
|
303
|
+
let _unsortedOperands = [this._operand(), this._operand()];
|
|
304
|
+
let _operands = [_unsortedOperands[1], _unsortedOperands[0]];
|
|
305
|
+
var dst = _operands[0];
|
|
306
|
+
frame.regs[dst] = this.globals[this.constants[_operands[1]]];
|
|
234
307
|
break;
|
|
235
308
|
```
|
|
236
309
|
|
|
@@ -239,43 +312,49 @@ case 64:
|
|
|
239
312
|
Function bodies are replaced upon runtime entry to the real bytecode.
|
|
240
313
|
|
|
241
314
|
```diff
|
|
242
|
-
// Input
|
|
243
|
-
|
|
244
|
-
console.log("Hello, world!");
|
|
245
|
-
}
|
|
315
|
+
// Input Code
|
|
316
|
+
console.log("Hello, world!");
|
|
246
317
|
|
|
247
318
|
// Before
|
|
248
|
-
// [
|
|
249
|
-
// [0, 2],
|
|
250
|
-
// [
|
|
251
|
-
// [0,
|
|
252
|
-
// [
|
|
253
|
-
// [
|
|
254
|
-
// [
|
|
255
|
-
// [13], RETURN 1:0-3:1
|
|
319
|
+
// [2, 1, 0], LOAD_GLOBAL reg[1] = console 1:0-1:7
|
|
320
|
+
// [0, 2, 1], LOAD_CONST reg[2] = "log" 1:0-1:28
|
|
321
|
+
// [8, 3, 1, 2], GET_PROP [3, 1, 2] 1:0-1:28
|
|
322
|
+
// [0, 4, 2], LOAD_CONST reg[4] = "Hello, world!" 1:12-1:27
|
|
323
|
+
// [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:28
|
|
324
|
+
// [0, 1, 3], LOAD_CONST reg[1] = undefined
|
|
325
|
+
// [45, 1], RETURN reg[1]
|
|
256
326
|
|
|
257
327
|
// After
|
|
258
|
-
// [56,
|
|
259
|
-
-// [
|
|
260
|
-
-// [
|
|
261
|
-
-// [
|
|
262
|
-
-// [
|
|
263
|
-
-// [
|
|
264
|
-
-// [
|
|
265
|
-
-// [
|
|
266
|
-
-// [
|
|
267
|
-
-// [
|
|
268
|
-
-// [
|
|
269
|
-
-// [
|
|
270
|
-
-// [
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
328
|
+
// [56, 4, 28, 50], PATCH [4, 28, 50]
|
|
329
|
+
-// [52], FOR_IN_SETUP <-- 22 ints of garbage code
|
|
330
|
+
-// [39], JUMP
|
|
331
|
+
-// [12], SUB
|
|
332
|
+
-// [12], SUB
|
|
333
|
+
-// [2], LOAD_GLOBAL
|
|
334
|
+
-// [28], LOOSE_EQ
|
|
335
|
+
-// [12], SUB
|
|
336
|
+
-// [46], THROW
|
|
337
|
+
-// [18], BXOR
|
|
338
|
+
-// [8], GET_PROP
|
|
339
|
+
-// [5], MOVE
|
|
340
|
+
-// [55], TRY_END
|
|
341
|
+
-// [57], DEBUGGER
|
|
342
|
+
-// [46], THROW
|
|
343
|
+
-// [23], GT
|
|
344
|
+
-// [48], BUILD_ARRAY
|
|
345
|
+
-// [28], LOOSE_EQ
|
|
346
|
+
-// [18], BXOR
|
|
347
|
+
-// [2], LOAD_GLOBAL
|
|
348
|
+
-// [50], DEFINE_GETTER
|
|
349
|
+
-// [4], LOAD_THIS
|
|
350
|
+
-// [24], LTE
|
|
351
|
+
// [45, 1], RETURN reg[1]
|
|
352
|
+
+// [2, 1, 0], LOAD_GLOBAL reg[1] = console <-- 22 ints of real code
|
|
353
|
+
+// [0, 2, 1], LOAD_CONST reg[2] = "log" 1:0-1:28
|
|
354
|
+
+// [8, 3, 1, 2], GET_PROP [3, 1, 2] 1:0-1:28
|
|
355
|
+
+// [0, 4, 2], LOAD_CONST reg[4] = "Hello, world!" 1:12-1:27
|
|
356
|
+
+// [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:28
|
|
357
|
+
+// [0, 1, 3], LOAD_CONST reg[1] = undefined
|
|
279
358
|
```
|
|
280
359
|
|
|
281
360
|
#### `timingChecks` (true/false)
|
|
@@ -311,7 +390,7 @@ Please transpile your code down first using [Babel](https://github.com/babel/bab
|
|
|
311
390
|
|
|
312
391
|
### Project
|
|
313
392
|
|
|
314
|
-
-
|
|
393
|
+
- Register base VM
|
|
315
394
|
- Lua-style closure and upvalue model
|
|
316
395
|
- CPython-style opcodes and codegen
|
|
317
396
|
- Compiler is in src/compiler.ts
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { generate } from "@babel/generator";
|
|
2
|
+
import { parse } from "@babel/parser";
|
|
3
|
+
import { applyMacroOpcodes } from "./transforms/runtime/macroOpcodes.js";
|
|
4
|
+
import { applyShuffleOpcodes } from "./transforms/runtime/shuffleOpcodes.js";
|
|
5
|
+
import { applyMinify } from "./transforms/runtime/minify.js";
|
|
6
|
+
import { applySpecializedOpcodes } from "./transforms/runtime/specializedOpcodes.js";
|
|
7
|
+
import { applyAliasedOpcodes } from "./transforms/runtime/aliasedOpcodes.js";
|
|
8
|
+
export async function obfuscateRuntime(runtime, bytecode, options, compiler) {
|
|
9
|
+
let ast;
|
|
10
|
+
try {
|
|
11
|
+
ast = parse(runtime, {
|
|
12
|
+
sourceType: "unambiguous"
|
|
13
|
+
});
|
|
14
|
+
} catch (error) {
|
|
15
|
+
throw new Error("VM-Runtime final parsing failed", {
|
|
16
|
+
cause: error
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Specialized opcode cases must be applied BEFORE shuffleOpcodes
|
|
21
|
+
if (options.specializedOpcodes) {
|
|
22
|
+
applySpecializedOpcodes(ast, bytecode, compiler);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Macro opcode cases must be applied BEFORE shuffleOpcodes
|
|
26
|
+
if (options.macroOpcodes && Object.keys(compiler.MACRO_OPS).length > 0) {
|
|
27
|
+
applyMacroOpcodes(ast, compiler);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Aliased opcode cases must be applied BEFORE shuffleOpcodes
|
|
31
|
+
if (options.aliasedOpcodes) {
|
|
32
|
+
applyAliasedOpcodes(ast, compiler);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Shuffle opcode handle order
|
|
36
|
+
if (options.shuffleOpcodes) {
|
|
37
|
+
applyShuffleOpcodes(ast);
|
|
38
|
+
}
|
|
39
|
+
let generated;
|
|
40
|
+
try {
|
|
41
|
+
generated = generate(ast).code;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
throw new Error("VM-Runtime final generation failed", {
|
|
44
|
+
cause: error
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Minify code?
|
|
49
|
+
if (options.minify) {
|
|
50
|
+
try {
|
|
51
|
+
generated = await applyMinify(generated);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
throw new Error("VM-Runtime final minification failed", {
|
|
54
|
+
cause: error
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return generated;
|
|
59
|
+
}
|