diff --git a/core/__pycache__/obfuscator.cpython-311.pyc b/core/__pycache__/obfuscator.cpython-311.pyc index cea59a1..a5a3068 100644 Binary files a/core/__pycache__/obfuscator.cpython-311.pyc and b/core/__pycache__/obfuscator.cpython-311.pyc differ diff --git a/core/obfuscator.py b/core/obfuscator.py index 16400f0..d30098b 100644 --- a/core/obfuscator.py +++ b/core/obfuscator.py @@ -14,26 +14,40 @@ class LuauVMObfuscator: "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)} + # Randomize opcode mapping + self.op_order = list(range(len(self.opcodes))) + random.shuffle(self.op_order) + + # Arithmetic keys for opcode decoding + self.k1 = random.randint(10, 100) + self.k2 = random.randint(10, 100) + + # Opcode to encoded ID: (real_index + k1) ^ k2 + self.op_to_id = {name: ((self.opcodes.index(name) + self.k1) ^ self.k2) % 256 for name in 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 encrypt_string(self, s, key): + return "".join(chr(ord(c) ^ ((key + i) % 256)) for i, c in enumerate(s)) + + def generate_vm_source(self, bytecode): + # Pack instructions into a string + inst_str = "".join(chr(i) for inst in bytecode['instructions'] for i in inst) + inst_b64 = base64.b64encode(inst_str.encode('latin-1')).decode() + + # Prepare constants + encrypted_consts = [] + for i, c in enumerate(bytecode['constants']): + if c['type'] == 'string': + # Encrypt with index-based key + key = (i * 137 + 42) % 256 + enc_val = self.encrypt_string(c['value'], key) + encrypted_consts.append({"t": 1, "v": base64.b64encode(enc_val.encode('latin-1')).decode()}) + else: + encrypted_consts.append({"t": 2, "v": c['value']}) + + consts_json = json.dumps(encrypted_consts) - def generate_vm_source(self, bytecode_json): - # Hardened VM for Luau vm_lua = f""" --- [[ VM-LUAU HARDENED ]] +-- [[ LUAU-VM PROTECTED ]] local _ENV = getfenv() local _B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' local _D = function(data) @@ -50,61 +64,60 @@ local _D = function(data) end)) end -local _BYTECODE = [[{bytecode_json}]] +local _INST_RAW = _D('{inst_b64}') +local _CONSTS = game:GetService("HttpService"):JSONDecode('{consts_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 function _EXECUTE() local registers = {{}} local vip = 1 - local dispatch = {{}} + local running = true - 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 + local function get_const(idx) + local c = _CONSTS[idx + 1] + if not c then return nil end + if c.t == 1 then + local raw = _D(c.v) + local key = (idx * 137 + 42) % 256 + local res = "" + for i=1, #raw do + res = res .. string.char(bit32.bxor(string.byte(raw, i), (key + i - 1) % 256)) + end + return res + end + return c.v 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 + while running and vip <= #_INST_RAW do + local op_raw = string.byte(_INST_RAW, vip) + local a = string.byte(_INST_RAW, vip + 1) + local b = string.byte(_INST_RAW, vip + 2) + local c = string.byte(_INST_RAW, vip + 3) + vip = vip + 4 + + -- Arithmetic Opcode Decoding + local op = bit32.bxor(op_raw, {self.k2}) - {self.k1} + + if op == {self.opcodes.index('MOVE')} then + registers[a] = registers[b] + elseif op == {self.opcodes.index('LOADK')} then + registers[a] = get_const(b) + elseif op == {self.opcodes.index('GETGLOBAL')} then + registers[a] = _ENV[get_const(b)] + elseif op == {self.opcodes.index('SETGLOBAL')} then + _ENV[get_const(b)] = registers[a] + elseif op == {self.opcodes.index('CALL')} then + local f = registers[a] + local args = {{}} + if b > 1 then for i=1, b-1 do args[i] = registers[a+i] end end + local res = {{f(unpack(args))}} + if c > 1 then for i=1, c-1 do registers[a+i-1] = res[i] end end + elseif op == {self.opcodes.index('RETURN')} then + running = false + end end end -task.spawn(_VM_LIFECYCLE) +task.spawn(_EXECUTE) """ return vm_lua @@ -115,34 +128,38 @@ task.spawn(_VM_LIFECYCLE) next_reg = 0 def add_const(val): - enc = self.encrypt_constant(val) + if isinstance(val, str): + if (val.startswith('"') and val.endswith('"')) or (val.startswith("'" ) and val.endswith("'" )): + val = val[1:-1] + for i, c in enumerate(constants): - if c == enc: return i - constants.append(enc) + if c['value'] == val: return i + + t = 'string' if isinstance(val, str) else 'number' + constants.append({'type': t, 'value': val}) 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']]]) + instructions.append([self.op_to_id["MOVE"], reg, locals_map[expr['value']], 0]) else: - instructions.append([self.op_to_id["GETGLOBAL"], reg, add_const(expr['value'])]) + instructions.append([self.op_to_id["GETGLOBAL"], reg, add_const(expr['value']), 0]) elif expr['type'] in ['STRING', 'NUMBER']: - instructions.append([self.op_to_id["LOADK"], reg, add_const(expr['value'])]) + val = expr['value'] + if expr['type'] == 'NUMBER': + try: val = float(val) + except: pass + instructions.append([self.op_to_id["LOADK"], reg, add_const(val), 0]) 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']]]) + instructions.append([self.op_to_id["MOVE"], func_reg, locals_map[node['name']], 0]) else: - instructions.append([self.op_to_id["GETGLOBAL"], func_reg, add_const(node['name'])]) + instructions.append([self.op_to_id["GETGLOBAL"], func_reg, add_const(node['name']), 0]) - # Load args for i, arg_expr in enumerate(node['args']): load_expr_to_reg(arg_expr, func_reg + 1 + i) @@ -156,10 +173,10 @@ task.spawn(_VM_LIFECYCLE) 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["SETGLOBAL"], val_reg, add_const(node['name']), 0]) - instructions.append([self.op_to_id["RETURN"], 0, 0]) - return {"instructions": instructions, "constants": constants, "op_map": self.op_map} + instructions.append([self.op_to_id["RETURN"], 0, 0, 0]) + return {"instructions": instructions, "constants": constants} def obfuscate(self, code): if not code.strip(): return "-- No input" @@ -170,10 +187,10 @@ task.spawn(_VM_LIFECYCLE) 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)) + return self.generate_vm_source(bytecode) except Exception as e: import traceback return f"-- Error: {str(e)}\n{traceback.format_exc()}" def obfuscate(code): - return LuauVMObfuscator().obfuscate(code) + return LuauVMObfuscator().obfuscate(code) \ No newline at end of file diff --git a/fixer.py b/fixer.py deleted file mode 100644 index 51caef8..0000000 --- a/fixer.py +++ /dev/null @@ -1,82 +0,0 @@ -# fixer.py -content = """import re - -class Lexer: - def __init__(self, code): - self.code = code - self.tokens = [] - self.pos = 0 - self.rules = [ - ('COMMENT', r'--\[\[.*?\].*?--.*'), - ('STRING', r'\"(?:\\.|[^\"\\])*\"|\'(?:\\.|[^\'\])*\'|\\[\\[.*?\\]\\].*?'), - ('NUMBER', r'\d+\.?\d*'), - ('KEYWORD', r'\b(and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b'), - ('IDENT', r'[a-zA-Z_][a-zA-Z0-9_]*'), - ('OP', r'==|~=|<=|>=|\.\.\.|\.\.|>>|<<|[\+\-\*/%^#=\<\>\(\)\{\}\[\];:,.\.]'), - ('SPACE', r'\s+') - ] - - def tokenize(self): - while self.pos < len(self.code): - match = None - for name, pattern in self.rules: - regex = re.compile(pattern, re.DOTALL) - match = regex.match(self.code, self.pos) - if match: - if name != 'SPACE' and name != 'COMMENT': - self.tokens.append((name, match.group(0))) - self.pos = match.end() - break - if not match: - self.pos += 1 - return self.tokens - -class Parser: - def __init__(self, tokens): - self.tokens = tokens - self.pos = 0 - - def peek(self): - return self.tokens[self.pos] if self.pos < len(self.tokens) else (None, None) - - def consume(self, expected_type=None): - token = self.peek() - if expected_type and token[0] != expected_type: - return None - self.pos += 1 - return token - - def parse(self): - nodes = [] - while self.pos < len(self.tokens): - node = self.parse_statement() - if node: - nodes.append(node) - else: - self.pos += 1 - return nodes - - def parse_statement(self): - token = self.peek() - if token[0] == 'IDENT': - ident = self.consume()[1] - next_token = self.peek() - if next_token[1] == '(': - self.consume() - args = [] - while self.peek()[1] != ')': - args.append(self.peek()[1]) - self.consume() - if self.peek()[1] == ',': - self.consume() - self.consume() - return {'type': 'call', 'name': ident, 'args': args} - elif next_token[1] == '=': - self.consume() - value = self.consume()[1] - return {'type': 'assign', 'name': ident, 'value': value} - return None -""" -with open("core/parser.py", "w") as f: - f.write(content) - diff --git a/test_obfuscator.py b/test_obfuscator.py deleted file mode 100644 index 832877c..0000000 --- a/test_obfuscator.py +++ /dev/null @@ -1,10 +0,0 @@ -from core.obfuscator import obfuscate - -test_code = """ -local x = 10 -print(x) -print("Hello, Luau!") -""" - -result = obfuscate(test_code) -print(result)