jsguardian 1.2.0
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/adversarial-tokens.js +176 -0
- package/ai-antipattern.js +235 -0
- package/ai-callgraph-poison.js +331 -0
- package/ai-confusion.js +644 -0
- package/ai-semantic-poison.js +276 -0
- package/canary.js +158 -0
- package/cne.js +686 -0
- package/index.js +248 -0
- package/integrity.js +47 -0
- package/jsobf-config.js +38 -0
- package/krak-compiler.js +1480 -0
- package/krak-vm-core.js +892 -0
- package/layers.js +136 -0
- package/opaque-pred.js +32 -0
- package/package.json +32 -0
- package/pipeline.js +327 -0
- package/prng.js +28 -0
- package/signature-break.js +101 -0
- package/temporal-keys.js +194 -0
- package/timing-oracle.js +129 -0
- package/transform-vm.js +266 -0
- package/transforms.js +371 -0
- package/vm-poison.js +247 -0
package/krak-vm-core.js
ADDED
|
@@ -0,0 +1,892 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HANDLER_MAP = exports.OPCODE_NAMES = void 0;
|
|
4
|
+
exports.generateVmNameMap = generateVmNameMap;
|
|
5
|
+
exports.applyVmNameMap = applyVmNameMap;
|
|
6
|
+
exports.generateVm = generateVm;
|
|
7
|
+
// ── OPCODE NAMES ─────────────────────────────────────────────────────────────
|
|
8
|
+
exports.OPCODE_NAMES = [
|
|
9
|
+
"MOV", "MOVR", "ADD", "SUB", "MUL", "DIV", "MOD",
|
|
10
|
+
"NOR", "XOR", "AND", "OR", "NOT", "SHL", "SHR",
|
|
11
|
+
"LOAD", "STORE", "CMP", "INC", "DEC", "JMP", "JZ",
|
|
12
|
+
"JNZ", "JLT", "JGT", "PUSH", "POP", "CALLI", "CALLE", "RET",
|
|
13
|
+
"OUT", "HALT", "STR", "GGLO", "GPRP", "SPRP", "METH",
|
|
14
|
+
"TNUM", "FNUM", "CADR", "NEW", "IOF", "CREGI", "CREGE", "TYP",
|
|
15
|
+
"BOOL", "FUNC", "SAR", "EVAL",
|
|
16
|
+
];
|
|
17
|
+
exports.HANDLER_MAP = {
|
|
18
|
+
mov: "MOV", movr: "MOVR", add: "ADD", sub: "SUB", mul: "MUL", div: "DIV", mod: "MOD",
|
|
19
|
+
nor: "NOR", xor: "XOR", and: "AND", or: "OR", not: "NOT", shl: "SHL", shr: "SHR",
|
|
20
|
+
load: "LOAD", store: "STORE", cmp: "CMP", inc: "INC", dec: "DEC",
|
|
21
|
+
jmp: "JMP", jz: "JZ", jnz: "JNZ", jlt: "JLT", jgt: "JGT",
|
|
22
|
+
push: "PUSH", pop: "POP", calli: "CALLI", calle: "CALLE", ret: "RET",
|
|
23
|
+
out: "OUT", halt: "HALT", str: "STR", getGlobal: "GGLO",
|
|
24
|
+
getProp: "GPRP", setProp: "SPRP", callMethod: "METH",
|
|
25
|
+
toNum: "TNUM", fromNum: "FNUM", callAddr: "CADR", newObject: "NEW",
|
|
26
|
+
instanceOf: "IOF", callRegInternal: "CREGI", callRegExternal: "CREGE",
|
|
27
|
+
typeOf: "TYP", toBool: "BOOL", newFunc: "FUNC", shiftRightArith: "SAR", _eval: "EVAL",
|
|
28
|
+
};
|
|
29
|
+
// ── HANDLER SOURCES (template) ────────────────────────────────────────────────
|
|
30
|
+
// Each handler is a string with @FETCH_START/@FETCH_END markers.
|
|
31
|
+
// The generator shuffles arg-fetch lines within each block.
|
|
32
|
+
const HANDLER_SRCS = {
|
|
33
|
+
mov: `function mov() {
|
|
34
|
+
//@FETCH_START
|
|
35
|
+
var r = readByte();
|
|
36
|
+
var v = readInt32();
|
|
37
|
+
//@FETCH_END
|
|
38
|
+
ctx.reg[r] = v;
|
|
39
|
+
}`,
|
|
40
|
+
movr: `function movr() {
|
|
41
|
+
//@FETCH_START
|
|
42
|
+
var d = readByte();
|
|
43
|
+
var s = readByte();
|
|
44
|
+
//@FETCH_END
|
|
45
|
+
ctx.reg[d] = ctx.reg[s];
|
|
46
|
+
}`,
|
|
47
|
+
add: `function add() {
|
|
48
|
+
//@FETCH_START
|
|
49
|
+
var d = readByte();
|
|
50
|
+
var s = readByte();
|
|
51
|
+
//@FETCH_END
|
|
52
|
+
ctx.reg[d] = ctx.reg[d] + ctx.reg[s];
|
|
53
|
+
}`,
|
|
54
|
+
sub: `function sub() {
|
|
55
|
+
//@FETCH_START
|
|
56
|
+
var d = readByte();
|
|
57
|
+
var s = readByte();
|
|
58
|
+
//@FETCH_END
|
|
59
|
+
ctx.reg[d] = ctx.reg[d] - ctx.reg[s];
|
|
60
|
+
}`,
|
|
61
|
+
mul: `function mul() {
|
|
62
|
+
//@FETCH_START
|
|
63
|
+
var d = readByte();
|
|
64
|
+
var s = readByte();
|
|
65
|
+
//@FETCH_END
|
|
66
|
+
ctx.reg[d] = ctx.reg[d] * ctx.reg[s];
|
|
67
|
+
}`,
|
|
68
|
+
div: `function div() {
|
|
69
|
+
//@FETCH_START
|
|
70
|
+
var d = readByte();
|
|
71
|
+
var s = readByte();
|
|
72
|
+
//@FETCH_END
|
|
73
|
+
ctx.reg[d] = ctx.reg[d] / ctx.reg[s];
|
|
74
|
+
}`,
|
|
75
|
+
mod: `function mod() {
|
|
76
|
+
//@FETCH_START
|
|
77
|
+
var d = readByte();
|
|
78
|
+
var s = readByte();
|
|
79
|
+
//@FETCH_END
|
|
80
|
+
ctx.reg[d] = ctx.reg[d] % ctx.reg[s];
|
|
81
|
+
}`,
|
|
82
|
+
nor: `function nor() {
|
|
83
|
+
//@FETCH_START
|
|
84
|
+
var d = readByte();
|
|
85
|
+
var s = readByte();
|
|
86
|
+
//@FETCH_END
|
|
87
|
+
ctx.reg[d] = ~(ctx.reg[d] | ctx.reg[s]);
|
|
88
|
+
}`,
|
|
89
|
+
xor: `function xor() {
|
|
90
|
+
//@FETCH_START
|
|
91
|
+
var d = readByte();
|
|
92
|
+
var s = readByte();
|
|
93
|
+
//@FETCH_END
|
|
94
|
+
ctx.reg[d] = ctx.reg[d] ^ ctx.reg[s];
|
|
95
|
+
}`,
|
|
96
|
+
and: `function and() {
|
|
97
|
+
//@FETCH_START
|
|
98
|
+
var d = readByte();
|
|
99
|
+
var s = readByte();
|
|
100
|
+
//@FETCH_END
|
|
101
|
+
ctx.reg[d] = ctx.reg[d] & ctx.reg[s];
|
|
102
|
+
}`,
|
|
103
|
+
or: `function or() {
|
|
104
|
+
//@FETCH_START
|
|
105
|
+
var d = readByte();
|
|
106
|
+
var s = readByte();
|
|
107
|
+
//@FETCH_END
|
|
108
|
+
ctx.reg[d] = ctx.reg[d] | ctx.reg[s];
|
|
109
|
+
}`,
|
|
110
|
+
not: `function not() {
|
|
111
|
+
//@FETCH_START
|
|
112
|
+
var r = readByte();
|
|
113
|
+
//@FETCH_END
|
|
114
|
+
ctx.reg[r] = ~ctx.reg[r];
|
|
115
|
+
}`,
|
|
116
|
+
shl: `function shl() {
|
|
117
|
+
//@FETCH_START
|
|
118
|
+
var d = readByte();
|
|
119
|
+
var s = readByte();
|
|
120
|
+
//@FETCH_END
|
|
121
|
+
ctx.reg[d] = ctx.reg[d] << ctx.reg[s];
|
|
122
|
+
}`,
|
|
123
|
+
shr: `function shr() {
|
|
124
|
+
//@FETCH_START
|
|
125
|
+
var d = readByte();
|
|
126
|
+
var s = readByte();
|
|
127
|
+
//@FETCH_END
|
|
128
|
+
ctx.reg[d] = ctx.reg[d] >>> ctx.reg[s];
|
|
129
|
+
}`,
|
|
130
|
+
shiftRightArith: `function shiftRightArith() {
|
|
131
|
+
//@FETCH_START
|
|
132
|
+
var d = readByte();
|
|
133
|
+
var s = readByte();
|
|
134
|
+
//@FETCH_END
|
|
135
|
+
ctx.reg[d] = ctx.reg[d] >> ctx.reg[s];
|
|
136
|
+
}`,
|
|
137
|
+
load: `function load() {
|
|
138
|
+
//@FETCH_START
|
|
139
|
+
var d = readByte();
|
|
140
|
+
var a = readByte();
|
|
141
|
+
//@FETCH_END
|
|
142
|
+
if (ctx.reg[a] < 0 || ctx.reg[a] >= 65536) crash(ctx);
|
|
143
|
+
ctx.reg[d] = ctx.heap[ctx.reg[a] & 0xFFFF];
|
|
144
|
+
}`,
|
|
145
|
+
store: `function store() {
|
|
146
|
+
//@FETCH_START
|
|
147
|
+
var a = readByte();
|
|
148
|
+
var s = readByte();
|
|
149
|
+
//@FETCH_END
|
|
150
|
+
if (ctx.reg[a] < 0 || ctx.reg[a] >= 65536) crash(ctx);
|
|
151
|
+
ctx.heap[ctx.reg[a] & 0xFFFF] = ctx.reg[s];
|
|
152
|
+
}`,
|
|
153
|
+
cmp: `function cmp() {
|
|
154
|
+
//@FETCH_START
|
|
155
|
+
var a = readByte();
|
|
156
|
+
var b = readByte();
|
|
157
|
+
//@FETCH_END
|
|
158
|
+
var v1 = ctx.reg[a]; var v2 = ctx.reg[b];
|
|
159
|
+
if (v1 === v2) ctx.reg[255] = 0;
|
|
160
|
+
else if (v1 < v2) ctx.reg[255] = -1;
|
|
161
|
+
else ctx.reg[255] = 1;
|
|
162
|
+
}`,
|
|
163
|
+
inc: `function inc() {
|
|
164
|
+
//@FETCH_START
|
|
165
|
+
var r = readByte();
|
|
166
|
+
//@FETCH_END
|
|
167
|
+
ctx.reg[r]++;
|
|
168
|
+
}`,
|
|
169
|
+
dec: `function dec() {
|
|
170
|
+
//@FETCH_START
|
|
171
|
+
var r = readByte();
|
|
172
|
+
//@FETCH_END
|
|
173
|
+
ctx.reg[r]--;
|
|
174
|
+
}`,
|
|
175
|
+
jmp: `function jmp() {
|
|
176
|
+
//@FETCH_START
|
|
177
|
+
var k = readInt32();
|
|
178
|
+
var t = readInt32();
|
|
179
|
+
//@FETCH_END
|
|
180
|
+
if (t < 0 || t >= ctx.mem.length) crash(ctx);
|
|
181
|
+
ctx.xk = k>>>0; ctx.ip = t;
|
|
182
|
+
}`,
|
|
183
|
+
jz: `function jz() {
|
|
184
|
+
//@FETCH_START
|
|
185
|
+
var r = readByte();
|
|
186
|
+
var k = readInt32();
|
|
187
|
+
var t = readInt32();
|
|
188
|
+
//@FETCH_END
|
|
189
|
+
if (t < 0 || t >= ctx.mem.length) crash(ctx);
|
|
190
|
+
if (!ctx.reg[r]) { ctx.ip = t; ctx.xk = k>>>0; }
|
|
191
|
+
}`,
|
|
192
|
+
jnz: `function jnz() {
|
|
193
|
+
//@FETCH_START
|
|
194
|
+
var r = readByte();
|
|
195
|
+
var k = readInt32();
|
|
196
|
+
var t = readInt32();
|
|
197
|
+
//@FETCH_END
|
|
198
|
+
if (t < 0 || t >= ctx.mem.length) crash(ctx);
|
|
199
|
+
if (ctx.reg[r]) { ctx.ip = t; ctx.xk = k>>>0; }
|
|
200
|
+
}`,
|
|
201
|
+
jlt: `function jlt() {
|
|
202
|
+
//@FETCH_START
|
|
203
|
+
var k = readInt32();
|
|
204
|
+
var t = readInt32();
|
|
205
|
+
//@FETCH_END
|
|
206
|
+
if (t < 0 || t >= ctx.mem.length) crash(ctx);
|
|
207
|
+
if (ctx.reg[255] < 0) { ctx.ip = t; ctx.xk = k>>>0; }
|
|
208
|
+
}`,
|
|
209
|
+
jgt: `function jgt() {
|
|
210
|
+
//@FETCH_START
|
|
211
|
+
var k = readInt32();
|
|
212
|
+
var t = readInt32();
|
|
213
|
+
//@FETCH_END
|
|
214
|
+
if (t < 0 || t >= ctx.mem.length) crash(ctx);
|
|
215
|
+
if (ctx.reg[255] > 0) { ctx.ip = t; ctx.xk = k>>>0; }
|
|
216
|
+
}`,
|
|
217
|
+
// Note: jz/jnz/jlt/jgt/jmp now use INT for k (32-bit LCG checkpoint).
|
|
218
|
+
push: `function push() {
|
|
219
|
+
//@FETCH_START
|
|
220
|
+
var r = readByte();
|
|
221
|
+
//@FETCH_END
|
|
222
|
+
if (ctx.stack.length >= MAX_STACK) crash(ctx);
|
|
223
|
+
ctx.stack.push(ctx.reg[r]);
|
|
224
|
+
}`,
|
|
225
|
+
pop: `function pop() {
|
|
226
|
+
//@FETCH_START
|
|
227
|
+
var r = readByte();
|
|
228
|
+
//@FETCH_END
|
|
229
|
+
var v = ctx.stack.pop(); ctx.reg[r] = (v !== undefined) ? v : 0;
|
|
230
|
+
}`,
|
|
231
|
+
calli: `function calli() {
|
|
232
|
+
//@FETCH_START
|
|
233
|
+
var o = readByte();
|
|
234
|
+
var c = readByte();
|
|
235
|
+
//@FETCH_END
|
|
236
|
+
var fn = ctx.reg[o]; var args = new Array(c);
|
|
237
|
+
for (var i = c - 1; i >= 0; i--) args[i] = ctx.stack.pop();
|
|
238
|
+
var addr = (fn >> 8) & 0xFFFFFF; var key = fn & 0xFF;
|
|
239
|
+
for (var i = 0; i < args.length; i++) ctx.stack.push(args[i]);
|
|
240
|
+
ctx.stack.push(null);
|
|
241
|
+
if (ctx.frames.length >= MAX_FRAMES) crash(ctx);
|
|
242
|
+
ctx.frames.push(ctx.ip, ctx.xk); ctx.ip = addr; ctx.xk = key;
|
|
243
|
+
}`,
|
|
244
|
+
calle: `function calle() {
|
|
245
|
+
//@FETCH_START
|
|
246
|
+
var o = readByte();
|
|
247
|
+
var c = readByte();
|
|
248
|
+
//@FETCH_END
|
|
249
|
+
var fn = ctx.reg[o]; var args = new Array(c);
|
|
250
|
+
for (var i = c - 1; i >= 0; i--) args[i] = ctx.stack.pop();
|
|
251
|
+
ctx.reg[0] = (typeof fn === 'function') ? fn.apply(null, args) : undefined;
|
|
252
|
+
}`,
|
|
253
|
+
ret: `function ret() {
|
|
254
|
+
var len = ctx.frames.length;
|
|
255
|
+
if (len >= 2) { ctx.xk = ctx.frames[len-1]; ctx.ip = ctx.frames[len-2]; ctx.frames.length = len-2; }
|
|
256
|
+
else { ctx.ip = ctx.mem.length + 1; }
|
|
257
|
+
}`,
|
|
258
|
+
out: `function out() {
|
|
259
|
+
//@FETCH_START
|
|
260
|
+
var r = readByte();
|
|
261
|
+
//@FETCH_END
|
|
262
|
+
console.log(ctx.reg[r]);
|
|
263
|
+
}`,
|
|
264
|
+
halt: `function halt() { ctx.ip = ctx.mem.length + 1; }`,
|
|
265
|
+
str: `function str() {
|
|
266
|
+
//@FETCH_START
|
|
267
|
+
var d = readByte();
|
|
268
|
+
//@FETCH_END
|
|
269
|
+
ctx.reg[d] = readStr();
|
|
270
|
+
}`,
|
|
271
|
+
getGlobal: `function getGlobal() {
|
|
272
|
+
//@FETCH_START
|
|
273
|
+
var d = readByte();
|
|
274
|
+
var n = readByte();
|
|
275
|
+
//@FETCH_END
|
|
276
|
+
ctx.reg[d] = ctx.env[ctx.reg[n]];
|
|
277
|
+
}`,
|
|
278
|
+
getProp: `function getProp() {
|
|
279
|
+
//@FETCH_START
|
|
280
|
+
var d = readByte();
|
|
281
|
+
var o = readByte();
|
|
282
|
+
var p = readByte();
|
|
283
|
+
//@FETCH_END
|
|
284
|
+
var obj = ctx.reg[o]; var key = ctx.reg[p];
|
|
285
|
+
ctx.reg[d] = (obj == null) ? undefined : obj[key];
|
|
286
|
+
}`,
|
|
287
|
+
setProp: `function setProp() {
|
|
288
|
+
//@FETCH_START
|
|
289
|
+
var o = readByte();
|
|
290
|
+
var p = readByte();
|
|
291
|
+
var v = readByte();
|
|
292
|
+
//@FETCH_END
|
|
293
|
+
var obj = ctx.reg[o]; if (obj != null) obj[ctx.reg[p]] = ctx.reg[v];
|
|
294
|
+
}`,
|
|
295
|
+
callMethod: `function callMethod() {
|
|
296
|
+
//@FETCH_START
|
|
297
|
+
var d = readByte();
|
|
298
|
+
var o = readByte();
|
|
299
|
+
var m = readByte();
|
|
300
|
+
var c = readByte();
|
|
301
|
+
//@FETCH_END
|
|
302
|
+
var obj = ctx.reg[o]; var name = ctx.reg[m];
|
|
303
|
+
var args = new Array(c); for (var i = c-1; i>=0; i--) args[i] = ctx.stack.pop();
|
|
304
|
+
var mfn = obj != null ? obj[name] : undefined;
|
|
305
|
+
ctx.reg[d] = (typeof mfn === 'function') ? mfn.apply(obj, args) : undefined;
|
|
306
|
+
}`,
|
|
307
|
+
toNum: `function toNum() {
|
|
308
|
+
//@FETCH_START
|
|
309
|
+
var d = readByte();
|
|
310
|
+
var s = readByte();
|
|
311
|
+
//@FETCH_END
|
|
312
|
+
ctx.reg[d] = Number(ctx.reg[s]);
|
|
313
|
+
}`,
|
|
314
|
+
fromNum: `function fromNum() {
|
|
315
|
+
//@FETCH_START
|
|
316
|
+
var d = readByte();
|
|
317
|
+
var s = readByte();
|
|
318
|
+
//@FETCH_END
|
|
319
|
+
ctx.reg[d] = ctx.reg[s];
|
|
320
|
+
}`,
|
|
321
|
+
callAddr: `function callAddr() {
|
|
322
|
+
//@FETCH_START
|
|
323
|
+
var k = readInt32();
|
|
324
|
+
var t = readInt32();
|
|
325
|
+
//@FETCH_END
|
|
326
|
+
if (ctx.frames.length >= MAX_FRAMES) crash(ctx);
|
|
327
|
+
if (t < 0 || t >= ctx.mem.length) crash(ctx);
|
|
328
|
+
ctx.frames.push(ctx.ip, ctx.xk); ctx.ip = t; ctx.xk = k>>>0;
|
|
329
|
+
}`,
|
|
330
|
+
newObject: `function newObject() {
|
|
331
|
+
//@FETCH_START
|
|
332
|
+
var d = readByte();
|
|
333
|
+
var c = readByte();
|
|
334
|
+
var n = readByte();
|
|
335
|
+
//@FETCH_END
|
|
336
|
+
var C = ctx.reg[c]; var args = new Array(n);
|
|
337
|
+
for (var i = n-1; i>=0; i--) args[i] = ctx.stack.pop();
|
|
338
|
+
ctx.reg[d] = new (Function.prototype.bind.apply(C, [null].concat(args)));
|
|
339
|
+
}`,
|
|
340
|
+
instanceOf: `function instanceOf() {
|
|
341
|
+
//@FETCH_START
|
|
342
|
+
var d = readByte();
|
|
343
|
+
var o = readByte();
|
|
344
|
+
var c = readByte();
|
|
345
|
+
//@FETCH_END
|
|
346
|
+
ctx.reg[d] = (ctx.reg[o] instanceof ctx.reg[c]) ? 1 : 0;
|
|
347
|
+
}`,
|
|
348
|
+
callRegInternal: `function callRegInternal() {
|
|
349
|
+
//@FETCH_START
|
|
350
|
+
var f = readByte();
|
|
351
|
+
var n = readByte();
|
|
352
|
+
//@FETCH_END
|
|
353
|
+
var fn = ctx.reg[f]; var args = new Array(n);
|
|
354
|
+
for (var i = n-1; i>=0; i--) args[i] = ctx.stack.pop();
|
|
355
|
+
var addr = (fn >> 8) & 0xFFFFFF; var key = fn & 0xFF;
|
|
356
|
+
for (var i = 0; i < args.length; i++) ctx.stack.push(args[i]);
|
|
357
|
+
ctx.stack.push(undefined);
|
|
358
|
+
if (ctx.frames.length >= MAX_FRAMES) crash(ctx);
|
|
359
|
+
ctx.frames.push(ctx.ip, ctx.xk); ctx.ip = addr; ctx.xk = key;
|
|
360
|
+
}`,
|
|
361
|
+
callRegExternal: `function callRegExternal() {
|
|
362
|
+
//@FETCH_START
|
|
363
|
+
var d = readByte();
|
|
364
|
+
var f = readByte();
|
|
365
|
+
var n = readByte();
|
|
366
|
+
//@FETCH_END
|
|
367
|
+
var fn = ctx.reg[f]; var args = new Array(n);
|
|
368
|
+
for (var i = n-1; i>=0; i--) args[i] = ctx.stack.pop();
|
|
369
|
+
ctx.reg[d] = (typeof fn === 'function') ? fn.apply(undefined, args) : undefined;
|
|
370
|
+
}`,
|
|
371
|
+
typeOf: `function typeOf() {
|
|
372
|
+
//@FETCH_START
|
|
373
|
+
var d = readByte();
|
|
374
|
+
var s = readByte();
|
|
375
|
+
//@FETCH_END
|
|
376
|
+
ctx.reg[d] = typeof ctx.reg[s];
|
|
377
|
+
}`,
|
|
378
|
+
toBool: `function toBool() {
|
|
379
|
+
//@FETCH_START
|
|
380
|
+
var d = readByte();
|
|
381
|
+
var v = readByte();
|
|
382
|
+
//@FETCH_END
|
|
383
|
+
ctx.reg[d] = v !== 0;
|
|
384
|
+
}`,
|
|
385
|
+
newFunc: `function newFunc() {
|
|
386
|
+
//@FETCH_START
|
|
387
|
+
var d = readByte();
|
|
388
|
+
var a = readInt32();
|
|
389
|
+
var k = readByte();
|
|
390
|
+
var n = readByte();
|
|
391
|
+
//@FETCH_END
|
|
392
|
+
ctx.reg[d] = ((a & 0xFFFFFF) << 8) | (k & 0xFF);
|
|
393
|
+
}`,
|
|
394
|
+
_eval: `function _eval() {
|
|
395
|
+
//@FETCH_START
|
|
396
|
+
var s = readStr();
|
|
397
|
+
//@FETCH_END
|
|
398
|
+
eval(s);
|
|
399
|
+
}`,
|
|
400
|
+
};
|
|
401
|
+
// ── SAFE-TO-INLINE handlers (last fetch var can be inlined) ──────────────────
|
|
402
|
+
const SAFE_INLINE = new Set([
|
|
403
|
+
"mov", "movr", "add", "sub", "mul", "div", "mod", "nor", "xor", "and", "or", "shl", "shr", "sar", "inc", "dec", "push", "pop", "str",
|
|
404
|
+
]);
|
|
405
|
+
// ── HELPERS ───────────────────────────────────────────────────────────────────
|
|
406
|
+
function fisherYates(arr, rng) {
|
|
407
|
+
const out = arr.slice();
|
|
408
|
+
for (let i = out.length - 1; i > 0; i--) {
|
|
409
|
+
const j = Math.floor(rng.next() * (i + 1));
|
|
410
|
+
const tmp = out[i];
|
|
411
|
+
out[i] = out[j];
|
|
412
|
+
out[j] = tmp;
|
|
413
|
+
}
|
|
414
|
+
return out;
|
|
415
|
+
}
|
|
416
|
+
function uniqueOpcodes(n, rng) {
|
|
417
|
+
const buf = [];
|
|
418
|
+
for (let i = 0; i < 256; i++)
|
|
419
|
+
buf.push(i);
|
|
420
|
+
return fisherYates(buf, rng).slice(0, n);
|
|
421
|
+
}
|
|
422
|
+
function randOddInt32(rng) {
|
|
423
|
+
return ((Math.floor(rng.next() * 0x7fffffff) * 2 + 1) >>> 0);
|
|
424
|
+
}
|
|
425
|
+
function mathScramble(val, rng) {
|
|
426
|
+
if (rng.next() < 0.5) {
|
|
427
|
+
const add = Math.floor(rng.next() * 500000);
|
|
428
|
+
return `(${val - add} + ${add})`;
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
const sub = Math.floor(rng.next() * 500000);
|
|
432
|
+
return `(${val + sub} - ${sub})`;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function argName(line) {
|
|
436
|
+
const m = line.match(/var\s+(\w+)\s*=/);
|
|
437
|
+
return m ? m[1] : null;
|
|
438
|
+
}
|
|
439
|
+
function argType(line) {
|
|
440
|
+
if (line.includes("readInt32()"))
|
|
441
|
+
return "INT";
|
|
442
|
+
if (line.includes("readByte()"))
|
|
443
|
+
return "BYTE";
|
|
444
|
+
if (line.includes("readStr()"))
|
|
445
|
+
return "STRING";
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
// Shuffle the @FETCH_START...@FETCH_END block inside a handler source string.
|
|
449
|
+
// Returns { shuffled source, argLayout }
|
|
450
|
+
function shuffleFetchBlock(handlerName, src, rng) {
|
|
451
|
+
const lines = src.split("\n");
|
|
452
|
+
const out = [];
|
|
453
|
+
let inBlock = false;
|
|
454
|
+
let block = [];
|
|
455
|
+
const layout = [];
|
|
456
|
+
for (const line of lines) {
|
|
457
|
+
if (line.includes("@FETCH_START")) {
|
|
458
|
+
inBlock = true;
|
|
459
|
+
out.push(line);
|
|
460
|
+
block = [];
|
|
461
|
+
}
|
|
462
|
+
else if (line.includes("@FETCH_END")) {
|
|
463
|
+
inBlock = false;
|
|
464
|
+
const shuffled = fisherYates(block, rng);
|
|
465
|
+
for (const sl of shuffled) {
|
|
466
|
+
const n = argName(sl), ty = argType(sl);
|
|
467
|
+
if (n && ty)
|
|
468
|
+
layout.push({ name: n, type: ty });
|
|
469
|
+
}
|
|
470
|
+
for (const sl of shuffled)
|
|
471
|
+
out.push(sl);
|
|
472
|
+
out.push(line);
|
|
473
|
+
}
|
|
474
|
+
else if (inBlock) {
|
|
475
|
+
block.push(line);
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
out.push(line);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return { code: out.join("\n"), layout };
|
|
482
|
+
}
|
|
483
|
+
// Optionally inline the last fetched var into its usage site (80% probability).
|
|
484
|
+
function removeMarkers(handlerName, src, rng) {
|
|
485
|
+
const lines = src.split("\n");
|
|
486
|
+
let preFetch = [], fetchLines = [], postFetch = [];
|
|
487
|
+
let state = 0;
|
|
488
|
+
for (const line of lines) {
|
|
489
|
+
const cl = line.replace(/\r$/, "");
|
|
490
|
+
if (cl.includes("@FETCH_START")) {
|
|
491
|
+
state = 1;
|
|
492
|
+
}
|
|
493
|
+
else if (cl.includes("@FETCH_END")) {
|
|
494
|
+
state = 2;
|
|
495
|
+
}
|
|
496
|
+
else if (state === 0)
|
|
497
|
+
preFetch.push(cl);
|
|
498
|
+
else if (state === 1)
|
|
499
|
+
fetchLines.push(cl);
|
|
500
|
+
else
|
|
501
|
+
postFetch.push(cl);
|
|
502
|
+
}
|
|
503
|
+
if (SAFE_INLINE.has(handlerName) && fetchLines.length > 0 && rng.next() < 0.8) {
|
|
504
|
+
const lastFetch = fetchLines[fetchLines.length - 1];
|
|
505
|
+
const m = lastFetch.match(/var\s+(\w+)\s*=\s*(read\w+\(\));/);
|
|
506
|
+
if (m) {
|
|
507
|
+
const [, varName, fetchCall] = m;
|
|
508
|
+
const regex = new RegExp(`\\b${varName}\\b`, "g");
|
|
509
|
+
const postStr = postFetch.join("\n");
|
|
510
|
+
const matches = postStr.match(regex);
|
|
511
|
+
if (matches && matches.length === 1) {
|
|
512
|
+
postFetch = postStr.replace(regex, fetchCall).split("\n");
|
|
513
|
+
fetchLines.pop();
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return [...preFetch, ...fetchLines, ...postFetch].join("\n");
|
|
518
|
+
}
|
|
519
|
+
// Rename ctx property names throughout a source string.
|
|
520
|
+
function renameCtxProps(src, propMap) {
|
|
521
|
+
const ctxProps = ["reg", "mem", "heap", "stack", "frames", "env", "ip", "xk"];
|
|
522
|
+
for (const p of ctxProps) {
|
|
523
|
+
const repl = propMap[p];
|
|
524
|
+
src = src.replace(new RegExp(`\\.${p}\\b`, "g"), `.${repl}`);
|
|
525
|
+
src = src.replace(new RegExp(`'${p}'`, "g"), `'${repl}'`);
|
|
526
|
+
src = src.replace(new RegExp(`"${p}"`, "g"), `"${repl}"`);
|
|
527
|
+
src = src.replace(new RegExp(`\\b${p}:`, "g"), `${repl}:`);
|
|
528
|
+
}
|
|
529
|
+
return src;
|
|
530
|
+
}
|
|
531
|
+
// Apply AST-level polymorphism (textual substitutions).
|
|
532
|
+
function applyASTPolymorphism(src, rng) {
|
|
533
|
+
if (rng.next() < 0.5)
|
|
534
|
+
src = src.replace(/\(raw \^ ctx\.xk\)/g, "(ctx.xk ^ raw)");
|
|
535
|
+
else
|
|
536
|
+
src = src.replace(/\(raw \^ ctx\.xk\)/g, "(~(~raw ^ ctx.xk))");
|
|
537
|
+
if (rng.next() < 0.5)
|
|
538
|
+
src = src.replace(/\(m\[p\] \^ k\)/g, "(k ^ m[p])");
|
|
539
|
+
else
|
|
540
|
+
src = src.replace(/\(m\[p\] \^ k\)/g, "(~(~k ^ m[p]))");
|
|
541
|
+
if (rng.next() < 0.5)
|
|
542
|
+
src = src.replace(/if \(ctx\.ip >= ctx\.mem\.length\)/g, "if (ctx.mem.length <= ctx.ip)");
|
|
543
|
+
if (rng.next() < 0.5)
|
|
544
|
+
src = src.replace(/if \(p \+ 4 > m\.length\)/g, "if (m.length < p + 4)");
|
|
545
|
+
if (rng.next() < 0.5)
|
|
546
|
+
src = src.replace(/while \(ctx\.ip < l\)/g, "for (; ctx.ip < l ;)");
|
|
547
|
+
if (rng.next() < 0.5)
|
|
548
|
+
src = src.replace(/if \(\!h\)/g, "if (h === undefined)");
|
|
549
|
+
if (rng.next() < 0.5)
|
|
550
|
+
src = src.replace(/if \(\+\+c >= max\)/g, "c++; if (c >= max)");
|
|
551
|
+
return src;
|
|
552
|
+
}
|
|
553
|
+
// Generate randomised error array.
|
|
554
|
+
function generateErrorArray(rng) {
|
|
555
|
+
const msgs = [
|
|
556
|
+
"Memory Exhausted " + rng.next().toString(36).slice(2, 6),
|
|
557
|
+
"Stack Bounds Exceeded " + rng.next().toString(36).slice(2, 6),
|
|
558
|
+
"Illegal Instruction " + rng.next().toString(36).slice(2, 6),
|
|
559
|
+
"Access Violation " + rng.next().toString(36).slice(2, 6),
|
|
560
|
+
];
|
|
561
|
+
const arrs = msgs.map(m => "[" + m.split("").map(c => "0x" + c.charCodeAt(0).toString(16)).join(", ") + "]");
|
|
562
|
+
if (rng.next() < 0.5) {
|
|
563
|
+
return `var _err = [\n ${arrs.join(",\n ")}\n];`;
|
|
564
|
+
}
|
|
565
|
+
else {
|
|
566
|
+
return `var _err = [];\n` + arrs.map(a => `_err.push(${a});`).join("\n");
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
// Generate random ctx property names.
|
|
570
|
+
function generateCtxPropMap(rng) {
|
|
571
|
+
const ctxProps = ["reg", "mem", "heap", "stack", "frames", "env", "ip", "xk"];
|
|
572
|
+
const used = new Set();
|
|
573
|
+
const map = {};
|
|
574
|
+
for (const p of ctxProps) {
|
|
575
|
+
let hex;
|
|
576
|
+
do {
|
|
577
|
+
hex = "_" + Math.floor(rng.next() * 0xffffff).toString(16);
|
|
578
|
+
} while (used.has(hex));
|
|
579
|
+
used.add(hex);
|
|
580
|
+
map[p] = hex;
|
|
581
|
+
}
|
|
582
|
+
return map;
|
|
583
|
+
}
|
|
584
|
+
// Generate ctx initialisation code (random form).
|
|
585
|
+
function generateCtxInit(key, propMap, rng) {
|
|
586
|
+
let keyStr;
|
|
587
|
+
const r = rng.next();
|
|
588
|
+
if (r < 0.33) {
|
|
589
|
+
const add = Math.floor(rng.next() * 100000);
|
|
590
|
+
keyStr = `(${key - add} + ${add})`;
|
|
591
|
+
}
|
|
592
|
+
else if (r < 0.66) {
|
|
593
|
+
const sub = Math.floor(rng.next() * 100000);
|
|
594
|
+
keyStr = `(${key + sub} - ${sub})`;
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
keyStr = String(key);
|
|
598
|
+
}
|
|
599
|
+
const pm = propMap;
|
|
600
|
+
const props = [
|
|
601
|
+
[pm.reg, "new Array(256).fill(0)"],
|
|
602
|
+
[pm.mem, "null"],
|
|
603
|
+
[pm.ip, "0"],
|
|
604
|
+
[pm.xk, keyStr],
|
|
605
|
+
[pm.heap, "new Array(65536).fill(0)"],
|
|
606
|
+
[pm.stack, "[]"],
|
|
607
|
+
[pm.frames, "[]"],
|
|
608
|
+
[pm.env, "(typeof globalThis !== 'undefined') ? globalThis : (typeof window !== 'undefined' ? window : global)"],
|
|
609
|
+
];
|
|
610
|
+
const shuffled = fisherYates(props, rng);
|
|
611
|
+
let initCode;
|
|
612
|
+
if (rng.next() < 0.5) {
|
|
613
|
+
const lines = shuffled.map(([k, v]) => `${k}: ${v}`);
|
|
614
|
+
initCode = `var ctx = {\n ${lines.join(",\n ")}\n};`;
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
const lines = shuffled.map(([k, v]) => `ctx.${k} = ${v};`);
|
|
618
|
+
initCode = `var ctx = {};\n${lines.join("\n")}`;
|
|
619
|
+
}
|
|
620
|
+
return { initCode, keyStr };
|
|
621
|
+
}
|
|
622
|
+
function generateConsts(mul, inc, rng) {
|
|
623
|
+
const stack = 100000 + Math.floor(rng.next() * 50000);
|
|
624
|
+
const frames = 10000 + Math.floor(rng.next() * 5000);
|
|
625
|
+
const lines = [
|
|
626
|
+
`var MAX_STACK = ${mathScramble(stack, rng)};`,
|
|
627
|
+
`var MAX_FRAMES = ${mathScramble(frames, rng)};`,
|
|
628
|
+
`var LCG_MUL = ${mathScramble(mul, rng)};`,
|
|
629
|
+
`var LCG_INC = ${mathScramble(inc, rng)};`,
|
|
630
|
+
];
|
|
631
|
+
return fisherYates(lines, rng).join("\n");
|
|
632
|
+
}
|
|
633
|
+
/** Generate a per-build map of all internal VM identifiers → random hex names. */
|
|
634
|
+
function generateVmNameMap(rng) {
|
|
635
|
+
const used = new Set();
|
|
636
|
+
function fresh() {
|
|
637
|
+
let name;
|
|
638
|
+
do {
|
|
639
|
+
// Format: _ + 4-6 hex chars → looks like any other obfuscated var
|
|
640
|
+
const len = 4 + Math.floor(rng.next() * 3);
|
|
641
|
+
name = "_" + Math.floor(rng.next() * 0xffffff).toString(16).padStart(len, "0");
|
|
642
|
+
} while (used.has(name));
|
|
643
|
+
used.add(name);
|
|
644
|
+
return name;
|
|
645
|
+
}
|
|
646
|
+
return {
|
|
647
|
+
ctx: fresh(), ops: fresh(), t1: fresh(),
|
|
648
|
+
t2: fresh(), SK: fresh(), runVM: fresh(),
|
|
649
|
+
_init: fresh(), readByte: fresh(), readInt32: fresh(),
|
|
650
|
+
readStr: fresh(), crash: fresh(), _s: fresh(),
|
|
651
|
+
_err: fresh(), _h1: fresh(), _hash: fresh(),
|
|
652
|
+
_ss: fresh(), _ver: fresh(),
|
|
653
|
+
MAX_STACK: fresh(), MAX_FRAMES: fresh(), LCG_MUL: fresh(), LCG_INC: fresh(),
|
|
654
|
+
kv_run: fresh(), kv_args: fresh(), kv_ret: fresh(),
|
|
655
|
+
kv_g: fresh(), kv_fn: fresh(),
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Apply the name map to a VM source string.
|
|
660
|
+
* Replacements are done longest-first to avoid partial matches
|
|
661
|
+
* (e.g. readInt32 must be replaced before readByte/readStr).
|
|
662
|
+
*/
|
|
663
|
+
function applyVmNameMap(src, nm) {
|
|
664
|
+
// Build list of [original, replacement] sorted by length descending
|
|
665
|
+
const pairs = [
|
|
666
|
+
["readInt32", nm.readInt32],
|
|
667
|
+
["readByte", nm.readByte],
|
|
668
|
+
["readStr", nm.readStr],
|
|
669
|
+
["MAX_STACK", nm.MAX_STACK],
|
|
670
|
+
["MAX_FRAMES", nm.MAX_FRAMES],
|
|
671
|
+
["LCG_MUL", nm.LCG_MUL],
|
|
672
|
+
["LCG_INC", nm.LCG_INC],
|
|
673
|
+
["runVM", nm.runVM],
|
|
674
|
+
["_init", nm._init],
|
|
675
|
+
["crash", nm.crash],
|
|
676
|
+
["_hash", nm._hash],
|
|
677
|
+
["_ss", nm._ss],
|
|
678
|
+
["_ver", nm._ver],
|
|
679
|
+
["_err", nm._err],
|
|
680
|
+
["_h1", nm._h1],
|
|
681
|
+
["_s", nm._s],
|
|
682
|
+
// split-table names (longer first to avoid partial matches)
|
|
683
|
+
["ops", nm.ops],
|
|
684
|
+
["ctx", nm.ctx],
|
|
685
|
+
];
|
|
686
|
+
for (const [orig, repl] of pairs) {
|
|
687
|
+
src = src.replace(new RegExp(`\\b${orig}\\b`, "g"), repl);
|
|
688
|
+
}
|
|
689
|
+
// t1/t2/SK are injected via template substitution, not regex rename
|
|
690
|
+
return src;
|
|
691
|
+
}
|
|
692
|
+
// ── CORE VM TEMPLATE ─────────────────────────────────────────────────────────
|
|
693
|
+
// All identifiers here use the canonical names from VmNameMap keys.
|
|
694
|
+
// applyVmNameMap() renames them to per-build random identifiers after generation.
|
|
695
|
+
const CORE_TEMPLATE = `
|
|
696
|
+
// @INJECT_ERR_START
|
|
697
|
+
var _err = [[0x4f,0x6f,0x6d]];
|
|
698
|
+
// @INJECT_ERR_END
|
|
699
|
+
|
|
700
|
+
function _s(b){var r='';for(var i=0;i<b.length;i++)r+=String.fromCharCode(b[i]);return r;}
|
|
701
|
+
|
|
702
|
+
function crash(ctx){
|
|
703
|
+
if(ctx){ctx.mem=null;if(ctx.reg)ctx.reg.fill(0);if(ctx.stack)ctx.stack.length=0;if(ctx.heap)ctx.heap.fill(0);}
|
|
704
|
+
throw new Error(_s(_err[(Math.random()*_err.length)|0]));
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
var _h1=0;
|
|
708
|
+
function _hash(m){if(!m)return 0;var c=0;for(var i=0;i<m.length;i++)c=((c<<5)-c+m[i])|0;return c;}
|
|
709
|
+
function _ss(m,arr){_h1=_hash(m);}
|
|
710
|
+
function _ver(ctx,arr){if(_hash(ctx.mem)!==_h1)crash(ctx);}
|
|
711
|
+
|
|
712
|
+
// @INJECT_CTX_START
|
|
713
|
+
var INIT_KEY=0;
|
|
714
|
+
var ctx={reg:new Array(256).fill(0),mem:null,ip:0,xk:INIT_KEY,heap:new Array(65536).fill(0),stack:[],frames:[],env:(typeof globalThis!=='undefined')?globalThis:(typeof window!=='undefined'?window:global)};
|
|
715
|
+
// @INJECT_CTX_END
|
|
716
|
+
|
|
717
|
+
// @INJECT_CONSTS_START
|
|
718
|
+
var MAX_STACK=100000;var MAX_FRAMES=10000;var LCG_MUL=1664525;var LCG_INC=1013904223;
|
|
719
|
+
// @INJECT_CONSTS_END
|
|
720
|
+
|
|
721
|
+
function readByte(){
|
|
722
|
+
if(ctx.ip>=ctx.mem.length)crash(ctx);
|
|
723
|
+
var raw=ctx.mem[ctx.ip++];
|
|
724
|
+
var dec=(raw^(ctx.xk>>>24))&0xFF;
|
|
725
|
+
ctx.xk=((Math.imul(ctx.xk,LCG_MUL)+LCG_INC)|0)>>>0;
|
|
726
|
+
return dec;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
function readInt32(){
|
|
730
|
+
var m=ctx.mem,p=ctx.ip,k=ctx.xk;
|
|
731
|
+
if(p+4>m.length)crash(ctx);
|
|
732
|
+
var b1=(m[p]^(k>>>24))&0xFF;k=((Math.imul(k,LCG_MUL)+LCG_INC)|0)>>>0;
|
|
733
|
+
var b2=(m[p+1]^(k>>>24))&0xFF;k=((Math.imul(k,LCG_MUL)+LCG_INC)|0)>>>0;
|
|
734
|
+
var b3=(m[p+2]^(k>>>24))&0xFF;k=((Math.imul(k,LCG_MUL)+LCG_INC)|0)>>>0;
|
|
735
|
+
var b4=(m[p+3]^(k>>>24))&0xFF;k=((Math.imul(k,LCG_MUL)+LCG_INC)|0)>>>0;
|
|
736
|
+
ctx.ip=p+4;ctx.xk=k;
|
|
737
|
+
return (b1|(b2<<8)|(b3<<16)|(b4<<24));
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
function readStr(){
|
|
741
|
+
var len=readInt32();var c=[];
|
|
742
|
+
for(var i=0;i<len;i++)c.push(String.fromCharCode(readByte()));
|
|
743
|
+
return c.join('');
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// @INJECT_SPLIT_DECL
|
|
747
|
+
var ops=new Array(256);
|
|
748
|
+
for(var i=0;i<256;i++){ops[i]=function(){crash(ctx);};}
|
|
749
|
+
|
|
750
|
+
function _init(b64){
|
|
751
|
+
var raw=atob(b64);
|
|
752
|
+
ctx.mem=new Uint8Array(raw.length);
|
|
753
|
+
for(var i=0;i<raw.length;i++)ctx.mem[i]=raw.charCodeAt(i);
|
|
754
|
+
ctx.ip=0;
|
|
755
|
+
// @INJECT_INIT_XK
|
|
756
|
+
ctx.reg.fill(0);ctx.heap.fill(0);ctx.stack=[];ctx.frames=[];
|
|
757
|
+
// @INJECT_SPLIT_MERGE
|
|
758
|
+
_ss(ctx.mem,ops);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
function runVM(b64){
|
|
762
|
+
_init(b64);
|
|
763
|
+
var l=ctx.mem.length;
|
|
764
|
+
while(ctx.ip<l){
|
|
765
|
+
var op=readByte();var h=ops[op];
|
|
766
|
+
if(!h)crash(ctx);h();
|
|
767
|
+
}
|
|
768
|
+
_ver(ctx,ops);
|
|
769
|
+
}
|
|
770
|
+
`;
|
|
771
|
+
/**
|
|
772
|
+
* Generate a fresh polymorphic KrakVm runtime + config.
|
|
773
|
+
* Every call produces a structurally distinct VM with different:
|
|
774
|
+
* - opcode byte assignments
|
|
775
|
+
* - arg-fetch ordering per handler
|
|
776
|
+
* - ctx property names
|
|
777
|
+
* - LCG parameters
|
|
778
|
+
* - AST expression forms
|
|
779
|
+
*/
|
|
780
|
+
function generateVm(rng) {
|
|
781
|
+
const initialKey = rng.int32() >>> 0; // full 32-bit initial key
|
|
782
|
+
const lcgMul = randOddInt32(rng);
|
|
783
|
+
const lcgInc = randOddInt32(rng);
|
|
784
|
+
// Per-build identifier name map (hides readByte, runVM, ctx, ops, etc.)
|
|
785
|
+
const nameMap = generateVmNameMap(rng);
|
|
786
|
+
// Assign opcode byte values
|
|
787
|
+
const opcodeVals = uniqueOpcodes(exports.OPCODE_NAMES.length, rng);
|
|
788
|
+
const opcodes = {};
|
|
789
|
+
exports.OPCODE_NAMES.forEach((name, i) => { opcodes[name] = opcodeVals[i]; });
|
|
790
|
+
// Build prop rename map (ctx.reg, ctx.mem, etc.)
|
|
791
|
+
const propMap = generateCtxPropMap(rng);
|
|
792
|
+
// Process each handler: shuffle fetches, collect layouts
|
|
793
|
+
const handlerBlocks = {};
|
|
794
|
+
for (const [hn, src] of Object.entries(HANDLER_SRCS)) {
|
|
795
|
+
const { code, layout } = shuffleFetchBlock(hn, src, rng);
|
|
796
|
+
handlerBlocks[hn] = { code, layout };
|
|
797
|
+
}
|
|
798
|
+
// Build argLayouts (keyed by opcode name)
|
|
799
|
+
const argLayouts = {};
|
|
800
|
+
for (const [hn, { layout }] of Object.entries(handlerBlocks)) {
|
|
801
|
+
const opName = exports.HANDLER_MAP[hn];
|
|
802
|
+
if (opName)
|
|
803
|
+
argLayouts[opName] = layout;
|
|
804
|
+
}
|
|
805
|
+
// Per-build split key: each opcode index is XORed with this before being
|
|
806
|
+
// stored in t1 or t2 so the slot numbers are not the real opcode values.
|
|
807
|
+
const splitKey = (rng.int32() >>> 0) & 0xFF;
|
|
808
|
+
// t1Name/t2Name/SKName will be substituted literally after nameMap apply.
|
|
809
|
+
const t1Name = nameMap.t1;
|
|
810
|
+
const t2Name = nameMap.t2;
|
|
811
|
+
const skName = nameMap.SK;
|
|
812
|
+
const opsName = nameMap.ops;
|
|
813
|
+
// Separate dynamic (40%) vs static handler assignments
|
|
814
|
+
const dynamicOps = {};
|
|
815
|
+
const staticOpsArr = [];
|
|
816
|
+
for (const [hn, { code }] of Object.entries(handlerBlocks)) {
|
|
817
|
+
const opName = exports.HANDLER_MAP[hn];
|
|
818
|
+
if (!opName)
|
|
819
|
+
continue;
|
|
820
|
+
const opcodeVal = opcodes[opName];
|
|
821
|
+
// Apply per-handler transformations
|
|
822
|
+
let processed = removeMarkers(hn, code, rng);
|
|
823
|
+
processed = applyASTPolymorphism(processed, rng);
|
|
824
|
+
processed = renameCtxProps(processed, propMap);
|
|
825
|
+
// Each handler goes into t1 or t2 (50/50 per-build random split).
|
|
826
|
+
// The slot index is opcodeVal ^ splitKey — not the raw opcode byte.
|
|
827
|
+
const tbl = rng.next() < 0.5 ? t1Name : t2Name;
|
|
828
|
+
const slot = opcodeVal ^ splitKey;
|
|
829
|
+
const assignment = `${tbl}[${slot}] = ${processed}`;
|
|
830
|
+
if (opName !== "EVAL" && opName !== "JMP" && rng.next() < 0.4) {
|
|
831
|
+
dynamicOps[opName] = { opcodeVal, src: `${tbl}[${slot}] = ${processed};` };
|
|
832
|
+
}
|
|
833
|
+
else {
|
|
834
|
+
staticOpsArr.push(assignment);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
// Shuffle static assignments
|
|
838
|
+
const shuffledStatics = fisherYates(staticOpsArr, rng);
|
|
839
|
+
// ── Split-table declarations and merge code ──────────────────────────────
|
|
840
|
+
// t1 and t2 are null-initialised (not crash-filled) so null is a reliable
|
|
841
|
+
// sentinel — exactly one of t1[slot]/t2[slot] holds the real handler.
|
|
842
|
+
// ops[] is crash-filled separately.
|
|
843
|
+
const splitDecl = [
|
|
844
|
+
`var ${skName} = ${mathScramble(splitKey, rng)};`,
|
|
845
|
+
`var ${t1Name} = new Array(${mathScramble(256, rng)}).fill(null);`,
|
|
846
|
+
`var ${t2Name} = new Array(${mathScramble(256, rng)}).fill(null);`,
|
|
847
|
+
].join("\n");
|
|
848
|
+
// Merge: ops is already crash-filled from its own init loop.
|
|
849
|
+
// For each slot i, exactly one of t1[i] or t2[i] is non-null (the real handler).
|
|
850
|
+
// Copy whichever is non-null into ops[i ^ SK].
|
|
851
|
+
const mergeCodeClean = [
|
|
852
|
+
`for(var _mi=0;_mi<256;_mi++){`,
|
|
853
|
+
` if(${t1Name}[_mi]!==null)${opsName}[_mi^${skName}]=${t1Name}[_mi];`,
|
|
854
|
+
` else if(${t2Name}[_mi]!==null)${opsName}[_mi^${skName}]=${t2Name}[_mi];`,
|
|
855
|
+
`}`,
|
|
856
|
+
].join("\n");
|
|
857
|
+
// Build core template with all substitutions
|
|
858
|
+
const { initCode, keyStr } = generateCtxInit(initialKey, propMap, rng);
|
|
859
|
+
let core = CORE_TEMPLATE;
|
|
860
|
+
core = core.replace(/\/\/ @INJECT_ERR_START[\s\S]*?\/\/ @INJECT_ERR_END/, generateErrorArray(rng));
|
|
861
|
+
core = core.replace(/\/\/ @INJECT_CONSTS_START[\s\S]*?\/\/ @INJECT_CONSTS_END/, generateConsts(lcgMul, lcgInc, rng));
|
|
862
|
+
core = core.replace(/\/\/ @INJECT_CTX_START[\s\S]*?\/\/ @INJECT_CTX_END/, initCode);
|
|
863
|
+
core = core.replace(/\/\/ @INJECT_INIT_XK/g, `${nameMap.ctx}.${propMap.xk} = ${keyStr};`);
|
|
864
|
+
core = core.replace(/\/\/ @INJECT_SPLIT_DECL/g, splitDecl);
|
|
865
|
+
core = core.replace(/\/\/ @INJECT_SPLIT_MERGE/g, mergeCodeClean);
|
|
866
|
+
core = applyASTPolymorphism(core, rng);
|
|
867
|
+
core = renameCtxProps(core, propMap);
|
|
868
|
+
// Scramble the heap literal size.
|
|
869
|
+
core = core.replace(/\bnew Array\(65536\)/g, `new Array(${mathScramble(65536, rng)})`);
|
|
870
|
+
// Note: new Array(256) in split decl is already mathScramble'd above.
|
|
871
|
+
core = core.replace(/\/\/ @INJECT_INIT_XK/g, `${nameMap.ctx}.${propMap.xk} = ${keyStr};`);
|
|
872
|
+
// Assemble full runtime; then apply nameMap over everything at once.
|
|
873
|
+
// This renames: readByte, readInt32, readStr, runVM, _init, ops, ctx,
|
|
874
|
+
// crash, _s, _err, _h1, _hash, _ss, _ver, MAX_STACK, MAX_FRAMES,
|
|
875
|
+
// LCG_MUL, LCG_INC — all in one pass, longest-first.
|
|
876
|
+
// t1/t2/SK are already in the template as literal names — nameMap renames ops/ctx etc.
|
|
877
|
+
let runtimeSrc = core + "\n" + shuffledStatics.join("\n") + "\n";
|
|
878
|
+
runtimeSrc = applyVmNameMap(runtimeSrc, nameMap);
|
|
879
|
+
// Apply nameMap to dynamicOps sources too
|
|
880
|
+
for (const key of Object.keys(dynamicOps)) {
|
|
881
|
+
dynamicOps[key].src = applyVmNameMap(dynamicOps[key].src, nameMap);
|
|
882
|
+
}
|
|
883
|
+
const config = {
|
|
884
|
+
initialKey,
|
|
885
|
+
lcgMul,
|
|
886
|
+
lcgInc,
|
|
887
|
+
opcodes: opcodes,
|
|
888
|
+
argLayouts,
|
|
889
|
+
dynamicOps,
|
|
890
|
+
};
|
|
891
|
+
return { runtimeSrc, config, nameMap };
|
|
892
|
+
}
|