diff --git a/core/__pycache__/obfuscator.cpython-311.pyc b/core/__pycache__/obfuscator.cpython-311.pyc index 0913f67..4e0e921 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 95de243..52926f7 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 ce867d4..8996663 100644 --- a/core/obfuscator.py +++ b/core/obfuscator.py @@ -46,7 +46,7 @@ class LuauVMObfuscator: def minify(self, code): code = re.sub(r'--.*', '', code) code = re.sub(r'\s+', ' ', code) - for op in ['==', '~=', '<=', '>=', '=', r'\+', r'\*', '/', '>', '<', ',', ';', ':']: + for op in ['==', '~=', '<=', '>=', '=', r'\+', r'\*', '/', '>', '<', ',', ';', ':', '-']: code = re.sub(r'\s*' + op + r'\s*', op.replace('\\', ''), code) return code.strip() @@ -81,7 +81,6 @@ class LuauVMObfuscator: "CHARS": self.get_var("chars"), "LOOKUP": self.get_var("lookup"), "GETC": self.get_var("getc") } - # Using triple single quotes to avoid confusion vm_lua = f'''local {v['ENV']}=setmetatable({{}},{{__index=getfenv()}}); local {v['CHARS']}='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; local {v['LOOKUP']}={{}}; @@ -137,8 +136,10 @@ local {v['EXEC']}=function() if a then a[{v['REGS']}[{v['B']}] or {v['GETC']}({v['B']})]={v['REGS']}[{v['C']}] end elseif {v['OP']}=={self.opcodes.index('SELF')} then local b={v['REGS']}[{v['B']}]; - {v['REGS']}[{v['A']}+1]=b; - {v['REGS']}[{v['A']}]=b[{v['GETC']}({v['C']})]; + if b then + {v['REGS']}[{v['A']}+1]=b; + {v['REGS']}[{v['A']}]=b[{v['GETC']}({v['C']})]; + end elseif {v['OP']}=={self.opcodes.index('ADD')} then {v['REGS']}[{v['A']}]=({v['REGS']}[{v['B']}] or 0)+({v['REGS']}[{v['C']}] or 0) elseif {v['OP']}=={self.opcodes.index('SUB')} then {v['REGS']}[{v['A']}]=({v['REGS']}[{v['B']}] or 0)-({v['REGS']}[{v['C']}] or 0) elseif {v['OP']}=={self.opcodes.index('MUL')} then {v['REGS']}[{v['A']}]=({v['REGS']}[{v['B']}] or 0)*({v['REGS']}[{v['C']}] or 0) @@ -157,7 +158,7 @@ local {v['EXEC']}=function() elseif {v['OP']}=={self.opcodes.index('RETURN')} then break end; end; end; -{v['SPW']}({v['EXEC']});''' +({v['SPW']})({v['EXEC']});''' return self.minify(vm_lua) def compile_to_bytecode(self, ast): @@ -232,9 +233,15 @@ end; kr = self.next_reg; self.next_reg += 1; gen_expr(t['key'], kr) emit("SETTABLE", br, kr, vr); self.next_reg -= 1 self.next_reg -= 1 + elif t['type'] == 'variable': + emit("SETGLOBAL", vr, add_const(t['name'])) else: emit("SETGLOBAL", vr, add_const(node['name'])) elif node['type'] == 'call': gen_call(node, self.next_reg) elif node['type'] == 'method_call': gen_method_call(node, self.next_reg) + elif node['type'] == 'return': + vr = self.next_reg; self.next_reg += 1; gen_expr(node['value'], vr) + emit("RETURN", vr, 2) + self.next_reg -= 1 emit("RETURN") return {"instructions": instructions, "constants": constants} diff --git a/core/parser.py b/core/parser.py index 2b977fb..6f33e9c 100644 --- a/core/parser.py +++ b/core/parser.py @@ -6,7 +6,6 @@ class Lexer: self.tokens = [] self.pos = 0 LB, RB, DQ, SQ, BS = chr(91), chr(93), chr(34), chr(39), chr(92) - # Added : to OP_LIST OP_LIST = r'==|~=|<=|>=|\.\.\.|\.\.|>>|<<|\+|\-|\*|/|%|\^|#|=|\<|\>|\(|\)|\{|\}|' + BS + LB + '|' + BS + RB + '|;|:|,|\.' self.rules = [ ('COMMENT', re.compile('--' + LB + LB + '.*?' + RB + RB + '|--.*', re.DOTALL)), @@ -44,7 +43,7 @@ class Parser: while self.pos < len(self.tokens): node = self.parse_statement() if node: nodes.append(node) - else: self.pos += 1 # Still skipping, but hopefully parse_statement is better + else: self.pos += 1 return nodes def parse_statement(self): tk = self.peek() @@ -56,6 +55,11 @@ class Parser: self.consume(); return {'type': 'assign', 'name': ident[1], 'value': self.parse_expression(), 'local': True} return {'type': 'assign', 'name': ident[1], 'value': {'type': 'KEYWORD', 'value': 'nil'}, 'local': True} return None + if tk[1] == 'return': + self.consume() + return {'type': 'return', 'value': self.parse_expression()} + if tk[1] == ';': + self.consume(); return None start = self.pos; expr = self.parse_prefix_expression() if expr: if self.peek()[1] == '=': @@ -75,15 +79,13 @@ class Parser: elif nt[1] == ':': self.consume(); m = self.consume('IDENT') if m: - # Method call if self.peek()[1] == '(': self.consume(); args = [] if self.peek()[1] != ')': args.append(self.parse_expression()) while self.peek()[1] == ',': self.consume(); args.append(self.parse_expression()) self.consume('OP', ')'); expr = {'type': 'method_call', 'base': expr, 'method': m[1], 'args': args} - else: # Not a call, just a colon access? (Rare in Lua, usually an error, but let's be safe) - expr = {'type': 'index', 'base': expr, 'key': {'type': 'STRING', 'value': '"'+m[1]+'"'}} + else: expr = {'type': 'index', 'base': expr, 'key': {'type': 'STRING', 'value': '"'+m[1]+'"'}} else: break elif nt[1] == '[': self.consume(); key = self.parse_expression(); self.consume('OP', ']'); expr = {'type': 'index', 'base': expr, 'key': key} @@ -112,4 +114,4 @@ class Parser: if tk[0] in ['STRING', 'NUMBER']: v = tk[1]; self.consume(); return {'type': tk[0], 'value': v} if tk[1] in ['true', 'false', 'nil']: return {'type': 'KEYWORD', 'value': self.consume()[1]} if tk[1] == '(': self.consume(); e = self.parse_expression(); self.consume('OP', ')'); return e - return self.parse_prefix_expression() + return self.parse_prefix_expression() \ No newline at end of file diff --git a/verify_syntax.py b/verify_syntax.py new file mode 100644 index 0000000..88b0289 --- /dev/null +++ b/verify_syntax.py @@ -0,0 +1,56 @@ +from core.obfuscator import obfuscate +import re + +test_cases = [ + """ + local a = 10 + print(a) + """, + """ + game:GetService("Players").LocalPlayer:Kick("Test") + """, + """ + return 123 + """, + """ + x = 5 + x = x + 1 + print(x) + """, + """ + local t = {a = 1} + t.a = 2 + print(t.a) + """ +] + +def check_syntax(lua_code): + # Basic check for balanced quotes and parens in the generated code + # Also check for the specific patterns we fixed + if "''" in lua_code and "(''" not in lua_code: # Check for the fixed inst_b64 part + # Note: ('' is fine for DEC function call if it was meant to be that, but we changed it to (') + pass + + # Check for "task and task.spawn or spawn(" without surrounding parens + # Our fix: (task and task.spawn or spawn)(... + if "task and task.spawn or spawn(" in lua_code: + if "(task and task.spawn or spawn)(" not in lua_code: + return False, "Missing parentheses around SPW" + + # Check for empty string (failed obfuscation) + if not lua_code.strip(): + return False, "Empty output" + + # Check for error comments + if "-- Obfuscation Error" in lua_code: + return False, "Obfuscation Error detected" + + return True, "OK" + +for i, code in enumerate(test_cases): + obfuscated = obfuscate(code) + success, msg = check_syntax(obfuscated) + print(f"Test {i}: {success} - {msg}") + if not success: + print(f"Code: {code}") + print(f"Obfuscated snippet: {obfuscated[:200]}...")