Autosave: 20260125-152826

This commit is contained in:
Flatlogic Bot 2026-01-25 15:28:26 +00:00
parent 6628329bbd
commit 6d9e2d7c97

View File

@ -3,214 +3,168 @@ const crypto = require('crypto');
class ObfuscatorService {
static obfuscate(sourceCode, config = {}) {
const seed = crypto.randomBytes(4).readUInt32BE(0);
const opcodeMap = this.generateRandomOpcodeMap(seed);
const { bytecode, constants } = this.compile(sourceCode, opcodeMap);
return this.generateVMRunner(bytecode, constants, opcodeMap, seed, config);
const tokens = this.tokenize(sourceCode);
const { bc, constants, ops } = this.compile(tokens);
return this.generateVM(bc, constants, ops, seed);
}
static generateRandomOpcodeMap(seed) {
const baseOpcodes = [
'LOADK', 'GETGLOBAL', 'SETGLOBAL', 'CALL', 'RETURN',
'MOVE', 'ADD', 'SUB', 'MUL', 'DIV', 'MOD', 'POW',
'UNM', 'NOT', 'LEN', 'CONCAT', 'JMP', 'EQ', 'LT', 'LE',
'FORLOOP', 'FORPREP', 'TFORCALL', 'TFORLOOP', 'SETLIST',
'CLOSE', 'CLOSURE', 'VARARG', 'FAKE'
];
// Add many fake opcodes for "noise"
for (let i = 0; i < 30; i++) {
baseOpcodes.push(`NOISE_${i}`);
}
const shuffled = [...baseOpcodes].sort(() => this.seededRandom(seed++) - 0.5);
const map = {};
shuffled.forEach((op, index) => {
map[op] = index + 5; // Offset by 5
});
return map;
}
static seededRandom(seed) {
const x = Math.sin(seed) * 10000;
return x - Math.floor(x);
}
static compile(code, opcodeMap) {
const constants = [];
const bytecode = [];
const getConstant = (val) => {
let idx = constants.indexOf(val);
if (idx === -1) {
idx = constants.length;
constants.push(val);
static tokenize(code) {
const tokens = [];
let i = 0;
while (i < code.length) {
let c = code[i];
if (/\\s/.test(c)) { i++; continue; }
if (c === '-' && code[i + 1] === '-') {
i += 2;
if (code[i] === '[' && code[i + 1] === '[') {
i += 2;
while (i < code.length && !(code[i] === ']' && code[i + 1] === ']')) i++;
i += 2;
} else {
while (i < code.length && code[i] !== '\n') i++;
}
continue;
}
if (c === '"' || c === "'") {
const q = c; let s = ""; i++;
while (i < code.length && code[i] !== q) {
if (code[i] === '\\') { s += code[i] + (code[i + 1] || ""); i += 2; }
else { s += code[i]; i++; }
}
tokens.push({ type: 'string', value: s });
i++; continue;
}
if (/\\d/.test(c)) {
let n = "";
while (i < code.length && /[\\d\.xX]/.test(code[i])) { n += code[i]; i++; }
tokens.push({ type: 'number', value: parseFloat(n) });
continue;
}
if (/[a-zA-Z_]/.test(c)) {
let id = "";
while (i < code.length && /[a-zA-Z0-9_]/.test(code[i])) { id += code[i]; i++; }
tokens.push({ type: 'identifier', value: id });
continue;
}
tokens.push({ type: 'operator', value: c }); i++;
}
return tokens;
}
static compile(tokens) {
const constants = [];
const getC = (v) => {
let idx = constants.findIndex(c => c.value === v);
if (idx === -1) { idx = constants.length; constants.push({ value: v, type: typeof v }); }
return idx;
};
const lines = code.split('\n').map(l => l.trim()).filter(l => l.length > 0 && !l.startsWith('--'));
lines.forEach(line => {
// Basic call: print("hello")
const callMatch = line.match(/^(\w+)\s*\(.*\)$/);
if (callMatch) {
const funcName = callMatch[1];
const argsStr = line.match(/\((.*)\)/)?.[1] || "";
const args = argsStr ? this.splitArgs(argsStr) : [];
bytecode.push(opcodeMap['GETGLOBAL'], 0, getConstant(funcName));
args.forEach((arg, i) => {
const reg = i + 1;
this.pushValueToReg(arg, reg, bytecode, opcodeMap, getConstant);
});
bytecode.push(opcodeMap['CALL'], 0, args.length + 1, 1);
return;
const ops = {LOADK:0, GETG:1, SETG:2, CALL:3, RET:4, GETT:5, GETS:6};
const bc = [];
let ti = 0;
const pExpr = (reg) => {
let t = tokens[ti]; if (!t) return;
if (t.type === 'number' || t.type === 'string') {
bc.push(ops.LOADK, reg, getC(t.value)); ti++;
} else if (t.type === 'identifier') {
const name = t.value; ti++;
if (tokens[ti]?.value === '(') { ti--; pCall(reg); }
else if (tokens[ti]?.value === ':') {
ti++; const m = tokens[ti].value; ti++;
bc.push(ops.GETG, reg, getC(name));
bc.push(ops.GETS, reg, reg, getC(m));
pArgs(reg, true);
} else if (tokens[ti]?.value === '.') {
bc.push(ops.GETG, reg, getC(name));
while (tokens[ti]?.value === '.') {
ti++; const k = tokens[ti].value; ti++;
bc.push(ops.GETT, reg, reg, getC(k));
}
} else { bc.push(ops.GETG, reg, getC(name)); }
}
// Basic assignment: x = 10
const assignMatch = line.match(/^(\w+)\s*=\s*(.*)$/);
if (assignMatch) {
const varName = assignMatch[1];
const val = assignMatch[2].trim();
this.pushValueToReg(val, 0, bytecode, opcodeMap, getConstant);
bytecode.push(opcodeMap['SETGLOBAL'], 0, getConstant(varName));
return;
};
const pArgs = (reg, isM) => {
if (tokens[ti]?.value !== '(') return;
ti++; let ac = isM ? 1 : 0;
while (ti < tokens.length && tokens[ti].value !== ')') {
if (tokens[ti].value === ',') { ti++; continue; }
ac++; pExpr(reg + ac);
}
// Add "opaque predicates" simulation
if (Math.random() > 0.8) {
bytecode.push(opcodeMap['JMP'], 1);
bytecode.push(opcodeMap[`NOISE_${Math.floor(Math.random() * 10)}`], 0xFF);
bc.push(ops.CALL, reg, ac + 1, 1);
if (tokens[ti]?.value === ')') ti++;
};
const pCall = (reg) => {
const fn = tokens[ti].value; ti++;
bc.push(ops.GETG, reg, getC(fn)); pArgs(reg);
};
while (ti < tokens.length) {
let t = tokens[ti];
if (t.type === 'identifier' && tokens[ti+1]?.value === '=') {
const vn = t.value; ti += 2; pExpr(0);
bc.push(ops.SETG, 0, getC(vn)); continue;
}
});
bytecode.push(opcodeMap['RETURN'], 0, 1);
return { bytecode, constants };
}
static splitArgs(argsStr) {
const args = [];
let current = "";
let inString = false;
for (let i = 0; i < argsStr.length; i++) {
const c = argsStr[i];
if (c === '"' || c === "'") inString = !inString;
if (c === ',' && !inString) {
args.push(current.trim());
current = "";
} else {
current += c;
if (t.type === 'identifier' && (tokens[ti+1]?.value === '(' || tokens[ti+1]?.value === ':')) {
pExpr(0); continue;
}
ti++;
}
args.push(current.trim());
return args.filter(a => a.length > 0);
bc.push(ops.RET, 0, 1);
return { bc, constants, ops };
}
static pushValueToReg(val, reg, bytecode, opcodeMap, getConstant) {
if (val.startsWith('"') || val.startsWith("'")) {
bytecode.push(opcodeMap['LOADK'], reg, getConstant(val.substring(1, val.length - 1)));
} else if (!isNaN(val)) {
bytecode.push(opcodeMap['LOADK'], reg, getConstant(Number(val)));
} else if (val === "true") {
bytecode.push(opcodeMap['LOADK'], reg, getConstant(true));
} else if (val === "false") {
bytecode.push(opcodeMap['LOADK'], reg, getConstant(false));
} else {
bytecode.push(opcodeMap['GETGLOBAL'], reg, getConstant(val));
}
}
static generateVMRunner(bytecode, constants, opcodeMap, seed, config) {
const k1 = (seed % 250) + 1;
const k2 = ((seed >> 8) % 250) + 1;
const encBytecode = [];
let rolling = k1;
bytecode.forEach((b) => {
const enc = (b ^ rolling) ^ k2;
encBytecode.push(enc);
rolling = (rolling + enc) % 256;
});
const encConstants = constants.map(c => {
if (typeof c === 'string') {
const chars = c.split('').map(char => {
let v = char.charCodeAt(0) ^ k1;
v = (v ^ 0xAA) ^ k2;
return v;
});
return { t: 's', d: chars };
static generateVM(bc, constants, ops, seed) {
const r = () => "_" + Math.random().toString(36).substring(7);
const nL = r(), nC = r(), nR = r(), nPC = r(), nK = r(), nD = r(), nDS = r();
const encC = constants.map(c => {
if (c.type === 'string') {
const e = Buffer.from(c.value).map(b => b ^ (seed & 0xFF)).toString('base64');
return `{t=1,v="${e}"}`;
}
if (typeof c === 'number') {
return { t: 'n', d: (c ^ k1) ^ 0x55 };
if (c.type === 'number') {
const o = Math.floor(Math.random() * 1000);
return `{t=2,v=${(c.value+o)^(seed&0xFFFF)},o=${o}}`;
}
if (typeof c === 'boolean') {
return { t: 'b', d: c ? 1 : 0 };
}
return { t: 'z', d: 0 };
});
const opsMapping = Object.entries(opcodeMap).map(([k, v]) => `[${v}] = "${k}"`).join(',');
const varL = "L_" + crypto.randomBytes(2).toString('hex');
const varC = "C_" + crypto.randomBytes(2).toString('hex');
const varK = "K_" + crypto.randomBytes(2).toString('hex');
const varO = "O_" + crypto.randomBytes(2).toString('hex');
const varB = "B_" + crypto.randomBytes(2).toString('hex');
const constantsStr = encConstants.map(c => {
const dataStr = Array.isArray(c.d) ? `{${c.d.join(',')}}` : c.d;
return `{t="${c.t}",d=${dataStr}}`;
return `{t=0}`;
}).join(',');
const d = [];
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.SETG] = `function() _G[${nD}(${nL}[${nPC}+1])] = ${nR}[${nL}[${nPC}]]; ${nPC}=${nPC}+2 end`;
d[ops.GETT] = `function() local r,t,k = ${nL}[${nPC}],${nL}[${nPC}+1],${nL}[${nPC}+2]; ${nPC}=${nPC}+3; ${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.RET] = `function() return "EXIT" end`;
const lua = `--[[ Luartex VM Protection ]]
local function Luartex_VM(...)
local ${varL} = {${encBytecode.join(',')}}
local ${varC} = {${constantsStr}}
local ${varK} = {${k1}, ${k2}}
local ${varO} = {${opsMapping}}
local ${varB} = bit32 or {bxor = function(a, b) local r, m = 0, 1 while a > 0 or b > 0 do if a % 2 ~= b % 2 then r = r + m end a, b, m = math.floor(a / 2), math.floor(b / 2), m * 2 end return r end}
local function _DECODE(v, k, k2) if v.t == "s" then local s = "" for i = 1, #v.d do local val = ${varB}.bxor(v.d[i], k2) s = s .. string.char(${varB}.bxor(val, k)) end return s elseif v.t == "n" then return ${varB}.bxor(${varB}.bxor(v.d, k), 0x55) elseif v.t == "b" then return v.d == 1 end return nil end
local function _EXECUTE()
local _ENV = getfenv() local _REG = {} local _PC = 1 local _RUNNING = true local _ROLLING = ${varK}[1]
while _RUNNING and _PC <= #${varL} do
local _RAW = ${varL}[_PC] local _OP = ${varB}.bxor(${varB}.bxor(_RAW, _ROLLING), ${varK}[2])
_ROLLING = (_ROLLING + _RAW) % 256 _PC = _PC + 1
local _MN = ${varO}[_OP]
if _MN == "LOADK" then
local _R = ${varB}.bxor(${varB}.bxor(${varL}[_PC], _ROLLING), ${varK}[2])
_ROLLING = (_ROLLING + ${varL}[_PC]) % 256 _PC = _PC + 1
local _CI = ${varB}.bxor(${varB}.bxor(${varL}[_PC], _ROLLING), ${varK}[2])
_ROLLING = (_ROLLING + ${varL}[_PC]) % 256 _PC = _PC + 1
_REG[_R] = _DECODE(${varC}[_CI + 1], ${varK}[1], ${varK}[2])
elseif _MN == "GETGLOBAL" then
local _R = ${varB}.bxor(${varB}.bxor(${varL}[_PC], _ROLLING), ${varK}[2])
_ROLLING = (_ROLLING + ${varL}[_PC]) % 256 _PC = _PC + 1
local _CI = ${varB}.bxor(${varB}.bxor(${varL}[_PC], _ROLLING), ${varK}[2])
_ROLLING = (_ROLLING + ${varL}[_PC]) % 256 _PC = _PC + 1
_REG[_R] = _ENV[_DECODE(${varC}[_CI + 1], ${varK}[1], ${varK}[2])]
elseif _MN == "SETGLOBAL" then
local _R = ${varB}.bxor(${varB}.bxor(${varL}[_PC], _ROLLING), ${varK}[2])
_ROLLING = (_ROLLING + ${varL}[_PC]) % 256 _PC = _PC + 1
local _CI = ${varB}.bxor(${varB}.bxor(${varL}[_PC], _ROLLING), ${varK}[2])
_ROLLING = (_ROLLING + ${varL}[_PC]) % 256 _PC = _PC + 1
_ENV[_DECODE(${varC}[_CI + 1], ${varK}[1], ${varK}[2])] = _REG[_R]
elseif _MN == "CALL" then
local _R = ${varB}.bxor(${varB}.bxor(${varL}[_PC], _ROLLING), ${varK}[2])
_ROLLING = (_ROLLING + ${varL}[_PC]) % 256 _PC = _PC + 1
local _NA = ${varB}.bxor(${varB}.bxor(${varL}[_PC], _ROLLING), ${varK}[2])
_ROLLING = (_ROLLING + ${varL}[_PC]) % 256 _PC = _PC + 1
local _NR = ${varB}.bxor(${varB}.bxor(${varL}[_PC], _ROLLING), ${varK}[2])
_ROLLING = (_ROLLING + ${varL}[_PC]) % 256 _PC = _PC + 1
local _AS = {}
if _NA > 1 then for i = 1, _NA - 1 do _AS[i] = _REG[_R + i] end end
local _F = _REG[_R]
local _S, _RE = pcall(_F, unpack(_AS))
if _NR > 1 then for i = 1, _NR - 1 do _REG[_R + i - 1] = _RE end end
elseif _MN == "RETURN" then _RUNNING = false
elseif _MN and _MN:find("NOISE") then _ROLLING = (_ROLLING + ${varL}[_PC]) % 256 _PC = _PC + 1
end
const xorKey = seed % 255;
return `local function VM(...)
local ${nL} = {${bc.join(',')}}
local ${nC} = {${encC}}
local ${nR} = setmetatable({}, {
__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
})
local ${nPC} = 1; local ${nK} = ${seed}
local function ${nD}(i)
local c = ${nC}[i+1]; if not c then return end
if c.t == 1 then
local b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
local s = c.v:gsub("[^"..b.."=]", ""); 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(${nK},255))) end end
end
return res
elseif c.t == 2 then return bit32.bxor(c.v, bit32.band(${nK}, 65535)) - c.o end
end
pcall(_EXECUTE)
end
Luartex_VM(...);
local ${nDS} = {${d.join(',')}}
while ${nL}[${nPC}] do
local op = ${nL}[${nPC}]; ${nPC} = ${nPC} + 1
local f = ${nDS}[op+1]
if f then if f() == "EXIT" then break end else break end
end
end; VM(...)`
}
}
module.exports = ObfuscatorService;