38029-vm/core/obfuscator.py
Flatlogic Bot bfe3f1d544 14:36
2026-01-31 13:36:20 +00:00

180 lines
6.5 KiB
Python

import random
import string
import base64
import json
from .parser import Lexer, Parser
class LuauVMObfuscator:
def __init__(self):
# Full Luau-style Opcode list
self.opcodes = [
"MOVE", "LOADK", "LOADBOOL", "LOADNIL", "GETGLOBAL", "SETGLOBAL",
"GETTABLE", "SETTABLE", "NEWTABLE", "SELF", "ADD", "SUB", "MUL",
"DIV", "MOD", "POW", "UNM", "NOT", "LEN", "CONCAT", "JMP", "EQ",
"LT", "LE", "TEST", "TESTSET", "CALL", "TAILCALL", "RETURN",
"FORLOOP", "FORPREP", "TFORLOOP", "SETLIST", "CLOSE", "CLOSURE", "VARARG"
]
self.op_map = list(range(len(self.opcodes)))
random.shuffle(self.op_map)
self.op_to_id = {name: self.op_map[i] for i, name in enumerate(self.opcodes)}
def encrypt_constant(self, value):
if isinstance(value, str):
# Remove quotes if present
if (value.startswith('"') and value.endswith('"')) or (value.startswith("'" ) and value.endswith("'" )):
value = value[1:-1]
return {"type": "string", "value": base64.b64encode(value.encode()).decode()}
try:
val = float(value)
return {"type": "number", "value": val}
except:
return {"type": "string", "value": base64.b64encode(str(value).encode()).decode()}
def generate_vm_source(self, bytecode_json):
# Hardened VM for Luau
vm_lua = f"""
-- [[ VM-LUAU HARDENED ]]
local _ENV = getfenv()
local _B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
local _D = function(data)
data = string.gsub(data, '[^'.._B64..'=]', '')
return (data:gsub('.', function(x)
if (x == '=') then return '' end
local r,f='',(_B64:find(x)-1)
for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
return r;
end):gsub('%d%d%d%d%d%d%d%d', function(x)
local r=0
for i=1,8 do r=r+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
return string.char(r)
end))
end
local _BYTECODE = [[{bytecode_json}]]
local function _VM_LIFECYCLE()
local bc = game:GetService("HttpService"):JSONDecode(_BYTECODE)
local insts = bc.instructions
local consts = bc.constants
local op_map = bc.op_map
local registers = {{}}
local vip = 1
local dispatch = {{}}
local function set_r(i, v) registers[i] = v end
local function get_r(i) return registers[i] end
dispatch[op_map[{self.opcodes.index("MOVE")+1}]] = function(a, b)
set_r(a, get_r(b))
end
dispatch[op_map[{self.opcodes.index("LOADK")+1}]] = function(a, b)
local c = consts[b+1]
set_r(a, c.type == "string" and _D(c.value) or c.value)
end
dispatch[op_map[{self.opcodes.index("GETGLOBAL")+1}]] = function(a, b)
local n = _D(consts[b+1].value)
set_r(a, _ENV[n])
end
dispatch[op_map[{self.opcodes.index("SETGLOBAL")+1}]] = function(a, b)
local n = _D(consts[b+1].value)
_ENV[n] = get_r(a)
end
dispatch[op_map[{self.opcodes.index("CALL")+1}]] = function(a, b, c)
local f = get_r(a)
local args = {{}}
if b > 1 then for i=1, b-1 do args[i] = get_r(a+i) end end
local res = {{f(unpack(args))}}
if c > 1 then for i=1, c-1 do set_r(a+i-1, res[i]) end end
end
dispatch[op_map[{self.opcodes.index("RETURN")+1}]] = function()
vip = #insts + 1
end
while vip <= #insts do
local inst = insts[vip]
local f = dispatch[inst[1]]
if f then f(inst[2], inst[3], inst[4]) end
vip = vip + 1
end
end
task.spawn(_VM_LIFECYCLE)
"""
return vm_lua
def compile_to_bytecode(self, ast):
constants = []
instructions = []
locals_map = {}
next_reg = 0
def add_const(val):
enc = self.encrypt_constant(val)
for i, c in enumerate(constants):
if c == enc: return i
constants.append(enc)
return len(constants) - 1
def load_expr_to_reg(expr, reg):
if expr['type'] == 'IDENT':
if expr['value'] in locals_map:
instructions.append([self.op_to_id["MOVE"], reg, locals_map[expr['value']]])
else:
instructions.append([self.op_to_id["GETGLOBAL"], reg, add_const(expr['value'])])
elif expr['type'] in ['STRING', 'NUMBER']:
instructions.append([self.op_to_id["LOADK"], reg, add_const(expr['value'])])
for node in ast:
if node['type'] == 'call':
func_reg = next_reg
# func_reg might be used, so we need to be careful if we have many locals
# For this simple obfuscator, we use registers above the current locals
# Load function
if node['name'] in locals_map:
instructions.append([self.op_to_id["MOVE"], func_reg, locals_map[node['name']]])
else:
instructions.append([self.op_to_id["GETGLOBAL"], func_reg, add_const(node['name'])])
# Load args
for i, arg_expr in enumerate(node['args']):
load_expr_to_reg(arg_expr, func_reg + 1 + i)
instructions.append([self.op_to_id["CALL"], func_reg, len(node['args']) + 1, 1])
elif node['type'] == 'assign':
val_reg = next_reg
load_expr_to_reg(node['value'], val_reg)
if node.get('local'):
locals_map[node['name']] = val_reg
next_reg += 1
else:
instructions.append([self.op_to_id["SETGLOBAL"], val_reg, add_const(node['name'])])
instructions.append([self.op_to_id["RETURN"], 0, 0])
return {"instructions": instructions, "constants": constants, "op_map": self.op_map}
def obfuscate(self, code):
if not code.strip(): return "-- No input"
try:
lexer = Lexer(code)
tokens = lexer.tokenize()
parser = Parser(tokens)
ast = parser.parse()
if not ast: return "-- VM Parser: No valid structures found."
bytecode = self.compile_to_bytecode(ast)
return self.generate_vm_source(json.dumps(bytecode))
except Exception as e:
import traceback
return f"-- Error: {str(e)}\n{traceback.format_exc()}"
def obfuscate(code):
return LuauVMObfuscator().obfuscate(code)