Newest 16:33
This commit is contained in:
parent
6d9e2d7c97
commit
6eb4a6e0d6
@ -13,7 +13,7 @@ class ObfuscatorService {
|
|||||||
let i = 0;
|
let i = 0;
|
||||||
while (i < code.length) {
|
while (i < code.length) {
|
||||||
let c = code[i];
|
let c = code[i];
|
||||||
if (/\\s/.test(c)) { i++; continue; }
|
if (/\s/.test(c)) { i++; continue; }
|
||||||
if (c === '-' && code[i + 1] === '-') {
|
if (c === '-' && code[i + 1] === '-') {
|
||||||
i += 2;
|
i += 2;
|
||||||
if (code[i] === '[' && code[i + 1] === '[') {
|
if (code[i] === '[' && code[i + 1] === '[') {
|
||||||
@ -28,15 +28,15 @@ class ObfuscatorService {
|
|||||||
if (c === '"' || c === "'") {
|
if (c === '"' || c === "'") {
|
||||||
const q = c; let s = ""; i++;
|
const q = c; let s = ""; i++;
|
||||||
while (i < code.length && code[i] !== q) {
|
while (i < code.length && code[i] !== q) {
|
||||||
if (code[i] === '\\') { s += code[i] + (code[i + 1] || ""); i += 2; }
|
if (code[i] === '\\') { s += code[i] + (code[i + 1] || ""); i += 2; }
|
||||||
else { s += code[i]; i++; }
|
else { s += code[i]; i++; }
|
||||||
}
|
}
|
||||||
tokens.push({ type: 'string', value: s });
|
tokens.push({ type: 'string', value: s });
|
||||||
i++; continue;
|
i++; continue;
|
||||||
}
|
}
|
||||||
if (/\\d/.test(c)) {
|
if (/\d/.test(c)) {
|
||||||
let n = "";
|
let n = "";
|
||||||
while (i < code.length && /[\\d\.xX]/.test(code[i])) { n += code[i]; i++; }
|
while (i < code.length && /[\d\.\xX]/.test(code[i])) { n += code[i]; i++; }
|
||||||
tokens.push({ type: 'number', value: parseFloat(n) });
|
tokens.push({ type: 'number', value: parseFloat(n) });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -51,6 +51,32 @@ class ObfuscatorService {
|
|||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static numberToExpression(n) {
|
||||||
|
if (typeof n !== 'number') return n;
|
||||||
|
const ops = ['+', '-', '*'];
|
||||||
|
let current = Math.floor(Math.random() * 200) - 100;
|
||||||
|
let expr = `(${current})`;
|
||||||
|
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
const op = ops[Math.floor(Math.random() * ops.length)];
|
||||||
|
const val = Math.floor(Math.random() * 100) + 1;
|
||||||
|
if (op === '+') { current += val; expr = `(${expr} + ${val})`; }
|
||||||
|
else if (op === '-') { current -= val; expr = `(${expr} - ${val})`; }
|
||||||
|
else if (op === '*') {
|
||||||
|
if (Math.abs(current * val) < 1000000) {
|
||||||
|
current *= val; expr = `(${expr} * ${val})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const diff = n - current;
|
||||||
|
if (diff >= 0) expr = `(${expr} + ${diff})`;
|
||||||
|
else expr = `(${expr} - ${Math.abs(diff)})`;
|
||||||
|
|
||||||
|
const noise = Math.floor(Math.random() * 255);
|
||||||
|
return `bit32.bxor(bit32.bxor(${expr}, ${noise}), ${noise})`;
|
||||||
|
}
|
||||||
|
|
||||||
static compile(tokens) {
|
static compile(tokens) {
|
||||||
const constants = [];
|
const constants = [];
|
||||||
const getC = (v) => {
|
const getC = (v) => {
|
||||||
@ -58,65 +84,119 @@ class ObfuscatorService {
|
|||||||
if (idx === -1) { idx = constants.length; constants.push({ value: v, type: typeof v }); }
|
if (idx === -1) { idx = constants.length; constants.push({ value: v, type: typeof v }); }
|
||||||
return idx;
|
return idx;
|
||||||
};
|
};
|
||||||
const ops = {LOADK:0, GETG:1, SETG:2, CALL:3, RET:4, GETT:5, GETS:6};
|
const ops = {
|
||||||
|
LOADK: 0, GETG: 1, SETG: 2, CALL: 3, RET: 4, GETT: 5, GETS: 6,
|
||||||
|
NEWTABLE: 7, SETTABLE: 8, GETLOCAL: 9, SETLOCAL: 10
|
||||||
|
};
|
||||||
const bc = [];
|
const bc = [];
|
||||||
let ti = 0;
|
let ti = 0;
|
||||||
const pExpr = (reg) => {
|
const locals = {};
|
||||||
let t = tokens[ti]; if (!t) return;
|
let nextLocal = 0;
|
||||||
|
|
||||||
|
const peek = () => tokens[ti];
|
||||||
|
const consume = () => tokens[ti++];
|
||||||
|
|
||||||
|
const parseExpr = (reg) => {
|
||||||
|
let t = peek(); if (!t) return;
|
||||||
if (t.type === 'number' || t.type === 'string') {
|
if (t.type === 'number' || t.type === 'string') {
|
||||||
bc.push(ops.LOADK, reg, getC(t.value)); ti++;
|
bc.push(ops.LOADK, reg, getC(consume().value));
|
||||||
} else if (t.type === 'identifier') {
|
} else if (t.type === 'identifier') {
|
||||||
const name = t.value; ti++;
|
let name = consume().value;
|
||||||
if (tokens[ti]?.value === '(') { ti--; pCall(reg); }
|
if (peek()?.value === '(') {
|
||||||
else if (tokens[ti]?.value === ':') {
|
ti--; parseCall(reg);
|
||||||
ti++; const m = tokens[ti].value; ti++;
|
} else {
|
||||||
bc.push(ops.GETG, reg, getC(name));
|
emitGet(reg, name);
|
||||||
bc.push(ops.GETS, reg, reg, getC(m));
|
while (peek()?.value === '.' || peek()?.value === ':') {
|
||||||
pArgs(reg, true);
|
let op = consume().value;
|
||||||
} else if (tokens[ti]?.value === '.') {
|
let key = consume().value;
|
||||||
bc.push(ops.GETG, reg, getC(name));
|
if (op === ':') {
|
||||||
while (tokens[ti]?.value === '.') {
|
bc.push(ops.GETS, reg, reg, getC(key));
|
||||||
ti++; const k = tokens[ti].value; ti++;
|
parseArgs(reg, true);
|
||||||
bc.push(ops.GETT, reg, reg, getC(k));
|
} else {
|
||||||
|
bc.push(ops.GETT, reg, reg, getC(key));
|
||||||
|
if (peek()?.value === '(') parseArgs(reg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else { bc.push(ops.GETG, reg, getC(name)); }
|
}
|
||||||
|
} else if (t.value === '{') {
|
||||||
|
parseTable(reg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const pArgs = (reg, isM) => {
|
|
||||||
if (tokens[ti]?.value !== '(') return;
|
const parseTable = (reg) => {
|
||||||
ti++; let ac = isM ? 1 : 0;
|
bc.push(ops.NEWTABLE, reg); consume(); // {
|
||||||
while (ti < tokens.length && tokens[ti].value !== ')') {
|
while (ti < tokens.length && peek().value !== '}') {
|
||||||
if (tokens[ti].value === ',') { ti++; continue; }
|
if (peek().value === ',') { consume(); continue; }
|
||||||
ac++; pExpr(reg + ac);
|
let t = peek();
|
||||||
|
if (tokens[ti+1]?.value === '=') {
|
||||||
|
let key = consume().value; consume(); // =
|
||||||
|
parseExpr(reg + 1);
|
||||||
|
bc.push(ops.SETTABLE, reg, getC(key), reg + 1);
|
||||||
|
} else {
|
||||||
|
parseExpr(reg + 1);
|
||||||
|
}
|
||||||
|
if (peek()?.value === ',') consume();
|
||||||
|
}
|
||||||
|
if (peek()?.value === '}') consume();
|
||||||
|
};
|
||||||
|
|
||||||
|
const emitGet = (reg, name) => {
|
||||||
|
if (locals[name] !== undefined) bc.push(ops.GETLOCAL, reg, locals[name]);
|
||||||
|
else bc.push(ops.GETG, reg, getC(name));
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseArgs = (reg, isM) => {
|
||||||
|
if (peek()?.value !== '(') return;
|
||||||
|
consume(); let ac = isM ? 1 : 0;
|
||||||
|
while (ti < tokens.length && peek().value !== ')') {
|
||||||
|
if (peek().value === ',') { consume(); continue; }
|
||||||
|
ac++; parseExpr(reg + ac);
|
||||||
}
|
}
|
||||||
bc.push(ops.CALL, reg, ac + 1, 1);
|
bc.push(ops.CALL, reg, ac + 1, 1);
|
||||||
if (tokens[ti]?.value === ')') ti++;
|
if (peek()?.value === ')') consume();
|
||||||
};
|
};
|
||||||
const pCall = (reg) => {
|
|
||||||
const fn = tokens[ti].value; ti++;
|
const parseCall = (reg) => {
|
||||||
bc.push(ops.GETG, reg, getC(fn)); pArgs(reg);
|
let fn = consume().value;
|
||||||
|
emitGet(reg, fn);
|
||||||
|
parseArgs(reg);
|
||||||
};
|
};
|
||||||
|
|
||||||
while (ti < tokens.length) {
|
while (ti < tokens.length) {
|
||||||
let t = tokens[ti];
|
let t = peek();
|
||||||
if (t.type === 'identifier' && tokens[ti+1]?.value === '=') {
|
if (t.value === 'local') {
|
||||||
const vn = t.value; ti += 2; pExpr(0);
|
consume();
|
||||||
bc.push(ops.SETG, 0, getC(vn)); continue;
|
let name = consume().value;
|
||||||
|
if (peek()?.value === '=') {
|
||||||
|
consume(); parseExpr(nextLocal);
|
||||||
|
locals[name] = nextLocal++;
|
||||||
|
} else {
|
||||||
|
locals[name] = nextLocal++;
|
||||||
|
}
|
||||||
|
} else if (t.type === 'identifier' && tokens[ti+1]?.value === '=') {
|
||||||
|
let vn = consume().value; consume();
|
||||||
|
parseExpr(nextLocal);
|
||||||
|
if (locals[vn] !== undefined) bc.push(ops.SETLOCAL, locals[vn], nextLocal);
|
||||||
|
else bc.push(ops.SETG, nextLocal, getC(vn));
|
||||||
|
} else if (t.type === 'identifier' && (tokens[ti+1]?.value === '(' || tokens[ti+1]?.value === ':')) {
|
||||||
|
parseExpr(nextLocal);
|
||||||
|
} else {
|
||||||
|
ti++;
|
||||||
}
|
}
|
||||||
if (t.type === 'identifier' && (tokens[ti+1]?.value === '(' || tokens[ti+1]?.value === ':')) {
|
|
||||||
pExpr(0); continue;
|
|
||||||
}
|
|
||||||
ti++;
|
|
||||||
}
|
}
|
||||||
bc.push(ops.RET, 0, 1);
|
bc.push(ops.RET, 0, 1);
|
||||||
return { bc, constants, ops };
|
return { bc, constants, ops };
|
||||||
}
|
}
|
||||||
|
|
||||||
static generateVM(bc, constants, ops, seed) {
|
static generateVM(bc, constants, ops, seed) {
|
||||||
const r = () => "_" + Math.random().toString(36).substring(7);
|
const r = () => "_" + Math.random().toString(36).substring(7) + "_" + Math.floor(Math.random() * 10000);
|
||||||
const nL = r(), nC = r(), nR = r(), nPC = r(), nK = r(), nD = r(), nDS = r();
|
const nL = r(), nC = r(), nR = r(), nPC = r(), nK = r(), nD = r(), nDS = r(), nLOC = r();
|
||||||
|
const nENV = r(), nTIME = r(), nX = r(), nY = r();
|
||||||
|
|
||||||
const encC = constants.map(c => {
|
const encC = constants.map(c => {
|
||||||
if (c.type === 'string') {
|
if (c.type === 'string') {
|
||||||
const e = Buffer.from(c.value).map(b => b ^ (seed & 0xFF)).toString('base64');
|
const key = seed & 0xFF;
|
||||||
|
const e = Buffer.from(c.value).map(b => b ^ key).toString('base64');
|
||||||
return `{t=1,v="${e}"}`;
|
return `{t=1,v="${e}"}`;
|
||||||
}
|
}
|
||||||
if (c.type === 'number') {
|
if (c.type === 'number') {
|
||||||
@ -125,7 +205,33 @@ class ObfuscatorService {
|
|||||||
}
|
}
|
||||||
return `{t=0}`;
|
return `{t=0}`;
|
||||||
}).join(',');
|
}).join(',');
|
||||||
|
|
||||||
|
const xorKey = seed % 255;
|
||||||
|
|
||||||
|
const antiTamper = `
|
||||||
|
local ${nENV} = getfenv and getfenv() or _G
|
||||||
|
local ${nTIME} = tick()
|
||||||
|
local function check()
|
||||||
|
if ${nENV}.debug and ${nENV}.debug.getinfo then
|
||||||
|
local i = ${nENV}.debug.getinfo(check)
|
||||||
|
if i.what ~= "Lua" then while true do end end
|
||||||
|
end
|
||||||
|
local s = tick()
|
||||||
|
for i=1,1000 do end
|
||||||
|
if tick() - s > 1 then while true do end end
|
||||||
|
end
|
||||||
|
check()
|
||||||
|
task.spawn(function()
|
||||||
|
while true do
|
||||||
|
if tick() - ${nTIME} > 4 then
|
||||||
|
local ${nX} = nil; ${nX}.crash()
|
||||||
|
end
|
||||||
|
${nTIME} = tick()
|
||||||
|
task.wait(3)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
`;
|
||||||
|
|
||||||
const d = [];
|
const d = [];
|
||||||
d[ops.LOADK] = `function() ${nR}[${nL}[${nPC}]] = ${nD}(${nL}[${nPC}+1]); ${nPC}=${nPC}+2 end`;
|
d[ops.LOADK] = `function() ${nR}[${nL}[${nPC}]] = ${nD}(${nL}[${nPC}+1]); ${nPC}=${nPC}+2 end`;
|
||||||
d[ops.GETG] = `function() ${nR}[${nL}[${nPC}]] = _G[${nD}(${nL}[${nPC}+1])]; ${nPC}=${nPC}+2 end`;
|
d[ops.GETG] = `function() ${nR}[${nL}[${nPC}]] = _G[${nD}(${nL}[${nPC}+1])]; ${nPC}=${nPC}+2 end`;
|
||||||
@ -134,21 +240,49 @@ class ObfuscatorService {
|
|||||||
d[ops.GETS] = `function() local r,t,k = ${nL}[${nPC}],${nL}[${nPC}+1],${nL}[${nPC}+2]; ${nPC}=${nPC}+3; ${nR}[r+1] = ${nR}[t]; ${nR}[r] = ${nR}[t][${nD}(k)] end`;
|
d[ops.GETS] = `function() local r,t,k = ${nL}[${nPC}],${nL}[${nPC}+1],${nL}[${nPC}+2]; ${nPC}=${nPC}+3; ${nR}[r+1] = ${nR}[t]; ${nR}[r] = ${nR}[t][${nD}(k)] end`;
|
||||||
d[ops.CALL] = `function() local r,na,nr = ${nL}[${nPC}],${nL}[${nPC}+1],${nL}[${nPC}+2]; ${nPC}=${nPC}+3; local a={}; for i=1,na-1 do a[i]=${nR}[r+i] end; ${nR}[r]=${nR}[r](table.unpack(a)) end`;
|
d[ops.CALL] = `function() local r,na,nr = ${nL}[${nPC}],${nL}[${nPC}+1],${nL}[${nPC}+2]; ${nPC}=${nPC}+3; local a={}; for i=1,na-1 do a[i]=${nR}[r+i] end; ${nR}[r]=${nR}[r](table.unpack(a)) end`;
|
||||||
d[ops.RET] = `function() return "EXIT" end`;
|
d[ops.RET] = `function() return "EXIT" end`;
|
||||||
|
d[ops.NEWTABLE] = `function() ${nR}[${nL}[${nPC}]] = {}; ${nPC}=${nPC}+1 end`;
|
||||||
|
d[ops.SETTABLE] = `function() local t,k,v = ${nL}[${nPC}],${nL}[${nPC}+1],${nL}[${nPC}+2]; ${nPC}=${nPC}+3; ${nR}[t][${nD}(k)] = ${nR}[v] end`;
|
||||||
|
d[ops.GETLOCAL] = `function() ${nR}[${nL}[${nPC}]] = ${nLOC}[${nL}[${nPC}+1]+1]; ${nPC}=${nPC}+2 end`;
|
||||||
|
d[ops.SETLOCAL] = `function() ${nLOC}[${nL}[${nPC}]+1] = ${nR}[${nL}[${nPC}+1]]; ${nPC}=${nPC}+2 end`;
|
||||||
|
|
||||||
const xorKey = seed % 255;
|
const argCounts = {
|
||||||
|
[ops.LOADK]: 2, [ops.GETG]: 2, [ops.SETG]: 2, [ops.GETT]: 3,
|
||||||
|
[ops.GETS]: 3, [ops.CALL]: 3, [ops.RET]: 2, [ops.NEWTABLE]: 1,
|
||||||
|
[ops.SETTABLE]: 3, [ops.GETLOCAL]: 2, [ops.SETLOCAL]: 2
|
||||||
|
};
|
||||||
|
|
||||||
return `local function VM(...)
|
const shuffled = Object.keys(ops).sort(() => Math.random() - 0.5);
|
||||||
local ${nL} = {${bc.join(',')}}
|
const opMap = {};
|
||||||
|
shuffled.forEach((k, i) => opMap[ops[k]] = i);
|
||||||
|
|
||||||
|
const finalBC = [];
|
||||||
|
for (let i = 0; i < bc.length; ) {
|
||||||
|
const op = bc[i];
|
||||||
|
finalBC.push(opMap[op]);
|
||||||
|
i++;
|
||||||
|
const count = argCounts[op] || 0;
|
||||||
|
for (let j = 0; j < count; j++) {
|
||||||
|
finalBC.push(bc[i] || 0);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dispatchFunctions = shuffled.map(k => d[ops[k]] || "function() end").join(',');
|
||||||
|
|
||||||
|
const vmCode = `local function VM(...)
|
||||||
|
${antiTamper}
|
||||||
|
local ${nL} = {${finalBC.join(',')}}
|
||||||
local ${nC} = {${encC}}
|
local ${nC} = {${encC}}
|
||||||
|
local ${nLOC} = {}
|
||||||
local ${nR} = setmetatable({}, {
|
local ${nR} = setmetatable({}, {
|
||||||
__index = function(t, k) return rawget(t, bit32.bxor(k, ${xorKey})) end,
|
__index = function(t, k) return rawget(t, bit32.bxor(k, ${xorKey})) end,
|
||||||
__newindex = function(t, k, v) rawset(t, bit32.bxor(k, ${xorKey}), v) end
|
__newindex = function(t, k, v) rawset(t, bit32.bxor(k, ${xorKey}), v) end
|
||||||
})
|
})
|
||||||
local ${nPC} = 1; local ${nK} = ${seed}
|
local ${nPC} = ${this.numberToExpression(1)}; local ${nK} = ${this.numberToExpression(seed)}
|
||||||
local function ${nD}(i)
|
local function ${nD}(i)
|
||||||
local c = ${nC}[i+1]; if not c then return end
|
local c = ${nC}[i+1]; if not c then return end
|
||||||
if c.t == 1 then
|
if c.t == 1 then
|
||||||
local b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
local b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
local s = c.v:gsub("[^"..b.."=]", ""); local res = ""
|
local s = c.v:gsub("[^"..b.."=]", ""); local res = ""
|
||||||
for j=1,#s,4 do
|
for j=1,#s,4 do
|
||||||
local v = 0
|
local v = 0
|
||||||
@ -158,13 +292,39 @@ class ObfuscatorService {
|
|||||||
return res
|
return res
|
||||||
elseif c.t == 2 then return bit32.bxor(c.v, bit32.band(${nK}, 65535)) - c.o end
|
elseif c.t == 2 then return bit32.bxor(c.v, bit32.band(${nK}, 65535)) - c.o end
|
||||||
end
|
end
|
||||||
local ${nDS} = {${d.join(',')}}
|
local ${nDS} = {${dispatchFunctions}}
|
||||||
while ${nL}[${nPC}] do
|
while true do
|
||||||
local op = ${nL}[${nPC}]; ${nPC} = ${nPC} + 1
|
local op = ${nL}[${nPC}]; if not op then break end
|
||||||
|
${nPC} = ${nPC} + 1
|
||||||
local f = ${nDS}[op+1]
|
local f = ${nDS}[op+1]
|
||||||
if f then if f() == "EXIT" then break end else break end
|
if f then if f() == "EXIT" then break end else break end
|
||||||
end
|
end
|
||||||
end; VM(...)`
|
end; VM(...)`;
|
||||||
|
|
||||||
|
const wrapLayers = (code, layers) => {
|
||||||
|
let current = code;
|
||||||
|
for (let i = 0; i < layers; i++) {
|
||||||
|
const layerSeed = crypto.randomBytes(4).readUInt32BE(0);
|
||||||
|
const encoded = Buffer.from(current).map(b => b ^ (layerSeed & 0xFF)).toString('base64');
|
||||||
|
current = `local _v = "${encoded}"
|
||||||
|
local _s = ${this.numberToExpression(layerSeed)}
|
||||||
|
local function _d(s)
|
||||||
|
local b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
local res = ""
|
||||||
|
for j=1,#s,4 do
|
||||||
|
local v = 0
|
||||||
|
for k=0,3 do local char = s:sub(j+k,j+k); v = v*64 + (char=="=" and 0 or (b:find(char,1,true)-1)) end
|
||||||
|
for k=2,0,-1 do if j+3-k<=#s and s:sub(j+3-k,j+3-k)~="=" then res = res..string.char(bit32.bxor(bit32.extract(v,k*8,8),bit32.band(_s,255))) end end
|
||||||
|
end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
local _f = loadstring(_d(_v))
|
||||||
|
if _f then _f() end`;
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
};
|
||||||
|
|
||||||
|
return wrapLayers(vmCode, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
module.exports = ObfuscatorService;
|
module.exports = ObfuscatorService;
|
||||||
Loading…
x
Reference in New Issue
Block a user