diff --git a/core/__pycache__/obfuscator.cpython-311.pyc b/core/__pycache__/obfuscator.cpython-311.pyc index 11ea2ea..cea59a1 100644 Binary files a/core/__pycache__/obfuscator.cpython-311.pyc and b/core/__pycache__/obfuscator.cpython-311.pyc differ diff --git a/core/__pycache__/parser.cpython-311.pyc b/core/__pycache__/parser.cpython-311.pyc index 5d032f5..9b9da3c 100644 Binary files a/core/__pycache__/parser.cpython-311.pyc and b/core/__pycache__/parser.cpython-311.pyc differ diff --git a/core/obfuscator.py b/core/obfuscator.py index 04e90f4..16400f0 100644 --- a/core/obfuscator.py +++ b/core/obfuscator.py @@ -20,7 +20,8 @@ class LuauVMObfuscator: def encrypt_constant(self, value): if isinstance(value, str): - if (value.startswith('"') and value.endswith('"')) or (value.startswith("'") and value.endswith("'")): + # 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: @@ -30,13 +31,9 @@ class LuauVMObfuscator: return {"type": "string", "value": base64.b64encode(str(value).encode()).decode()} def generate_vm_source(self, bytecode_json): - # Extremely hardened VM for Roblox executors - + # Hardened VM for Luau vm_lua = f""" --- [[ VM-LUAU v2.5 HARDENED ]] --- Target: UNC-Compatible Executors --- Anti-Debug & Identity Resilience Active - +-- [[ VM-LUAU HARDENED ]] local _ENV = getfenv() local _B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' local _D = function(data) @@ -56,53 +53,37 @@ end local _BYTECODE = [[{bytecode_json}]] local function _VM_LIFECYCLE() - -- Anti-Debug: Check for common debug hooks - if debug and debug.getinfo then - local info = debug.getinfo(1) - if info.what == "C" then - -- Potentially hooked - end - end - 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 -- Virtual Instruction Pointer - - -- Indirect Threaded Dispatch Table (Hardened) + local vip = 1 local dispatch = {{}} - -- Register accessors with XOR masking simulation local function set_r(i, v) registers[i] = v end local function get_r(i) return registers[i] end - -- MOVE dispatch[op_map[{self.opcodes.index("MOVE")+1}]] = function(a, b) set_r(a, get_r(b)) end - -- LOADK 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 - -- GETGLOBAL dispatch[op_map[{self.opcodes.index("GETGLOBAL")+1}]] = function(a, b) local n = _D(consts[b+1].value) set_r(a, _ENV[n]) end - -- SETGLOBAL dispatch[op_map[{self.opcodes.index("SETGLOBAL")+1}]] = function(a, b) local n = _D(consts[b+1].value) _ENV[n] = get_r(a) end - -- CALL dispatch[op_map[{self.opcodes.index("CALL")+1}]] = function(a, b, c) local f = get_r(a) local args = {{}} @@ -111,35 +92,27 @@ local function _VM_LIFECYCLE() if c > 1 then for i=1, c-1 do set_r(a+i-1, res[i]) end end end - -- RETURN dispatch[op_map[{self.opcodes.index("RETURN")+1}]] = function() vip = #insts + 1 end - -- Execution Loop while vip <= #insts do local inst = insts[vip] local f = dispatch[inst[1]] - if f then - f(inst[2], inst[3], inst[4]) - end + if f then f(inst[2], inst[3], inst[4]) end vip = vip + 1 end end --- Stealth Execution -task.spawn(function() - local s, e = pcall(_VM_LIFECYCLE) - if not s and _ENV.warn then - -- _ENV.warn("VM Critical Failure: " .. tostring(e)) - 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) @@ -148,18 +121,42 @@ end) 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': - const_idx = add_const(node['name']) - instructions.append([self.op_to_id["GETGLOBAL"], 0, const_idx]) - reg_idx = 1 - for arg in node['args']: - instructions.append([self.op_to_id["LOADK"], reg_idx, add_const(arg)]) - reg_idx += 1 - instructions.append([self.op_to_id["CALL"], 0, len(node['args']) + 1, 1]) + 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': - instructions.append([self.op_to_id["LOADK"], 0, add_const(node['value'])]) - instructions.append([self.op_to_id["SETGLOBAL"], 0, add_const(node['name'])]) + 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} @@ -175,7 +172,8 @@ end) bytecode = self.compile_to_bytecode(ast) return self.generate_vm_source(json.dumps(bytecode)) except Exception as e: - return f"-- Error: {str(e)}" + import traceback + return f"-- Error: {str(e)}\n{traceback.format_exc()}" def obfuscate(code): - return LuauVMObfuscator().obfuscate(code) \ No newline at end of file + return LuauVMObfuscator().obfuscate(code) diff --git a/core/parser.py b/core/parser.py index 67f7e63..3676e12 100644 --- a/core/parser.py +++ b/core/parser.py @@ -5,14 +5,25 @@ class Lexer: self.code = code self.tokens = [] self.pos = 0 - # Correctly formatted rules with escaped backslashes for regex character classes + + # Using chr() to build regex parts and avoid tool-induced corruption. + LB = chr(91) + RB = chr(93) + DQ = chr(34) + SQ = chr(39) + BS = chr(92) + + # List every operator individually to avoid character set issues. + OP_LIST = '==|~=|<=|>=|\.\.\.|\.\.|>>|<<|\+|\-|\*|/|%|\^|#|=|\<|\>|\(|\)|\{|\}|' + BS + LB + '|' + BS + RB + '|;|:|,|\.' + self.rules = [ - ('COMMENT', re.compile(r'--\[\[.*?\].*?|--.*', re.DOTALL)), - ('STRING', re.compile(r'"(?:\\.|[^"\\])*"|\'(?:\\.|[^\\\])*\'|\[\[.*?\].*?\]', re.DOTALL)), + ('COMMENT', re.compile('--' + LB + LB + '.*?' + RB + RB + '|--.*', re.DOTALL)), + ('STRING', re.compile(DQ + '(?:' + BS + BS + '.|[^' + DQ + BS + BS + '])*' + DQ + '|' + SQ + '(?:' + BS + BS + '.|[^' + SQ + BS + BS + '])*' + SQ + '|' + LB + LB + '.*?' + RB + RB, re.DOTALL)), ('NUMBER', re.compile(r'\d+\.?\d*')), ('KEYWORD', re.compile(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', re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*')), - ('OP', re.compile(r'==|~=|<=|>=|\.\.\.|\.|>>|<<|[\+\-\*/%^#=\<>\(\)\{\}\[\];:, ]')) + ('OP', re.compile(OP_LIST)), + ('SPACE', re.compile(r'\s+')) ] def tokenize(self): @@ -26,7 +37,6 @@ class Lexer: self.pos = match.end() break if not match: - # Skip unknown characters self.pos += 1 return self.tokens @@ -41,7 +51,7 @@ class Parser: def consume(self, expected_type=None, expected_value=None): token = self.peek() - if not token[0]: return None + if not token or not token[0]: return None if expected_type and token[0] != expected_type: return None if expected_value and token[1] != expected_value: return None self.pos += 1 @@ -59,15 +69,16 @@ class Parser: def parse_statement(self): token = self.peek() - if not token[0]: return None + if not token or not token[0]: return None if token[1] == 'local': self.consume() ident = self.consume('IDENT') - if ident and self.peek()[1] == '=': - self.consume() - val = self.parse_expression() - return {'type': 'assign', 'name': ident[1], 'value': val, 'local': True} + if ident: + if self.peek()[1] == '=': + self.consume() + val = self.parse_expression() + return {'type': 'assign', 'name': ident[1], 'value': val, 'local': True} return None if token[0] == 'IDENT': @@ -92,4 +103,4 @@ class Parser: def parse_expression(self): token = self.consume() if not token: return None - return token[1] \ No newline at end of file + return {'type': token[0], 'value': token[1]} diff --git a/parser_b64.txt b/parser_b64.txt deleted file mode 100644 index 2302f20..0000000 --- a/parser_b64.txt +++ /dev/null @@ -1 +0,0 @@ -aW1wb3J0IHJlCgpjbGFzcyBMZXhlcjoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBjb2RlKToKICAgICAgICBzZWxmLmNvZGUgPSBjb2RlCiAgICAgICAgc2VsZi50b2tlbnMgPSBbXQogICAgICAgIHNlbGYucG9zID0gMAogICAgICAgIHNlbGYucnVsZXMgPSBbCiAgICAgICAgICAgICgnQ09NTUVOVCcsIHInLS1cW1xbLio/XF1cXXwtLS4qJyksCiAgICAgICAgICAgICgnU1RSSU5HJywgciciKD86XC58W14iXF0pKiJ8XCcoPzpcLnxbXlwnXF0pKlwnfFxbXFsuKj9cXVxdJyksCiAgICAgICAgICAgICgnTlVNQkVSJywgcidcZCtcLj9cZConKSwKICAgICAgICAgICAgKCdLRVlXT1JEJywgcidcYihhbmR8YnJlYWt8ZG98ZWxzZXxlbHNlaWZ8ZW5kfGZhbHNlfGZvcnxmdW5jdGlvbnxpZnxpbnxsb2NhbHxuaWx8bm90fG9yfHJlcGVhdHxyZXR1cm58dGhlbnx0cnVlfHVudGlsfHdoaWxlKVxiJyksCiAgICAgICAgICAgICgnSURFTlQnLCByJ1thLXpBLVpfXVthLXpBLVowLTlfXSonKSwKICAgICAgICAgICAgKCdPUCcsIHInPT18fj18PD18Pj18XC5cLlwufFwuXC58Pj58PDx8W1wrXC1cKi8lXiM9XDw+XChcKVx7XH1cW1xdOzosXC5dJyksCiAgICAgICAgICAgICgnU1BBQ0UnLCByJ1xzKycpCiAgICAgICAgXQoKICAgIGRlZiB0b2tlbml6ZShzZWxmKToKICAgICAgICB3aGlsZSBzZWxmLnBvcyA8IGxlbihzZWxmLmNvZGUpOgogICAgICAgICAgICBtYXRjaCA9IE5vbmUKICAgICAgICAgICAgZm9yIG5hbWUsIHBhdHRlcm4gaW4gc2VsZi5ydWxlczoKICAgICAgICAgICAgICAgIHJlZ2V4ID0gcmUuY29tcGlsZShwYXR0ZXJuLCByZS5ET1RBTEwpCiAgICAgICAgICAgICAgICBtYXRjaCA9IHJlZ2V4Lm1hdGNoKHNlbGYuY29kZSwgc2VsZi5wb3MpCiAgICAgICAgICAgICAgICBpZiBtYXRjaDoKICAgICAgICAgICAgICAgICAgICBpZiBuYW1lICE9ICdTUEFDRScgYW5kIG5hbWUgIT0gJ0NPTU1FTlQnOgogICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnRva2Vucy5hcHBlbmQoKG5hbWUsIG1hdGNoLmdyb3VwKDApKSkKICAgICAgICAgICAgICAgICAgICBzZWxmLnBvcyA9IG1hdGNoLmVuZCgpCiAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgaWYgbm90IG1hdGNoOgogICAgICAgICAgICAgICAgc2VsZi5wb3MgKz0gMQogICAgICAgIHJldHVybiBzZWxmLnRva2VucwoKY2xhc3MgUGFyc2VyOgogICAgZGVmIF9faW5pdF9fKHNlbGYsIHRva2Vucyk6CiAgICAgICAgc2VsZi50b2tlbnMgPSB0b2tlbnMKICAgICAgICBzZWxmLnBvcyA9IDAKCiAgICBkZWYgcGVlayhzZWxmKToKICAgICAgICByZXR1cm4gc2VsZi50b2tlbnNbc2VsZi5wb3NdIGlmIHNlbGYucG9zIDwgbGVuKHNlbGYudG9rZW5zKSBlbHNlIChOb25lLCBOb25lKQoKICAgIGRlZiBjb25zdW1lKHNlbGYsIGV4cGVjdGVkX3R5cGU9Tm9uZSk6CiAgICAgICAgdG9rZW4gPSBzZWxmLnBlZWsoKQogICAgICAgIGlmIGV4cGVjdGVkX3R5cGUgYW5kIHRva2VuWzBdICE9IGV4cGVjdGVkX3R5cGU6CiAgICAgICAgICAgIHJldHVybiBOb25lCiAgICAgICAgc2VsZi5wb3MgKz0gMQogICAgICAgIHJldHVybiB0b2tlbgoKICAgIGRlZiBwYXJzZShzZWxmKToKICAgICAgICBub2RlcyA9IFtdCiAgICAgICAgd2hpbGUgc2VsZi5wb3MgPCBsZW4oc2VsZi50b2tlbnMpOgogICAgICAgICAgICBub2RlID0gc2VsZi5wYXJzZV9zdGF0ZW1lbnQoKQogICAgICAgICAgICBpZiBub2RlOgogICAgICAgICAgICAgICAgbm9kZXMuYXBwZW5kKG5vZGUpCiAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICBzZWxmLnBvcyArPSAxCiAgICAgICAgcmV0dXJuIG5vZGVzCgogICAgZGVmIHBhcnNlX3N0YXRlbWVudChzZWxmKToKICAgICAgICB0b2tlbiA9IHNlbGYucGVlaygpCiAgICAgICAgaWYgdG9rZW5bMF0gPT0gJ0lERU5UJzoKICAgICAgICAgICAgaWRlbnQgPSBzZWxmLmNvbnN1bWUoKVsxXQogICAgICAgICAgICBuZXh0X3Rva2VuID0gc2VsZi5wZWVrKCkKICAgICAgICAgICAgaWYgbmV4dF90b2tlblsxXSA9PSAnKCc6CiAgICAgICAgICAgICAgICBzZWxmLmNvbnN1bWUoKQogICAgICAgICAgICAgICAgYXJncyA9IFtdCiAgICAgICAgICAgICAgICB3aGlsZSBzZWxmLnBlZWsoKVsxXSAhPSAnKSc6CiAgICAgICAgICAgICAgICAgICAgYXJncy5hcHBlbmQoc2VsZi5wZWVrKClbMV0pCiAgICAgICAgICAgICAgICAgICAgc2VsZi5jb25zdW1lKCkKICAgICAgICAgICAgICAgICAgICBpZiBzZWxmLnBlZWsoKVsxXSA9PSAnLCc6CiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuY29uc3VtZSgpCiAgICAgICAgICAgICAgICBzZWxmLmNvbnN1bWUoKQogICAgICAgICAgICAgICAgcmV0dXJuIHsndHlwZSc6ICdjYWxsJywgJ25hbWUnOiBpZGVudCwgJ2FyZ3MnOiBhcmdzfQogICAgICAgICAgICBlbGlmIG5leHRfdG9rZW5bMV0gPT0gJz0nOgogICAgICAgICAgICAgICAgc2VsZi5jb25zdW1lKCkKICAgICAgICAgICAgICAgIHZhbHVlID0gc2VsZi5jb25zdW1lKClbMV0KICAgICAgICAgICAgICAgIHJldHVybiB7J3R5cGUnOiAnYXNzaWduJywgJ25hbWUnOiBpZGVudCwgJ3ZhbHVlJzogdmFsdWV9CiAgICAgICAgcmV0dXJuIE5vbmUK