15:06
This commit is contained in:
parent
33ef3d1b36
commit
b9ac7cd6b0
Binary file not shown.
@ -16,15 +16,63 @@ class LuauVMObfuscator:
|
||||
]
|
||||
|
||||
# Arithmetic keys for opcode decoding
|
||||
self.k1 = random.randint(50, 200)
|
||||
self.k2 = random.randint(50, 200)
|
||||
self.k3 = random.randint(1, 10) # Multiplier/Step
|
||||
self.k1 = random.randint(100, 500)
|
||||
self.k2 = random.randint(100, 500)
|
||||
|
||||
# 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}
|
||||
self.op_to_id = {name: ((self.opcodes.index(name) + self.k1) ^ self.k2) % 1024 for name in self.opcodes}
|
||||
|
||||
# Variable Renaming Map
|
||||
self.var_map = {}
|
||||
self.used_vars = set()
|
||||
|
||||
def get_var(self, hint="var"):
|
||||
if hint in self.var_map:
|
||||
return self.var_map[hint]
|
||||
|
||||
chars = "l1I0O"
|
||||
new_var = "_" + "".join(random.choice(chars) for _ in range(random.randint(10, 15)))
|
||||
while new_var in self.used_vars:
|
||||
new_var = "_" + "".join(random.choice(chars) for _ in range(random.randint(10, 15)))
|
||||
|
||||
self.used_vars.add(new_var)
|
||||
self.var_map[hint] = new_var
|
||||
return new_var
|
||||
|
||||
def to_expr(self, n, depth=0):
|
||||
# Recursively build complex expressions
|
||||
if depth > 2 or random.random() < 0.1:
|
||||
return str(n)
|
||||
|
||||
r = random.randint(1, 1000)
|
||||
choice = random.choice(['add', 'sub', 'xor', 'mul'])
|
||||
|
||||
if choice == 'add':
|
||||
return f"({self.to_expr(n - r, depth + 1)} + {self.to_expr(r, depth + 1)})"
|
||||
elif choice == 'sub':
|
||||
return f"({self.to_expr(n + r, depth + 1)} - {self.to_expr(r, depth + 1)})"
|
||||
elif choice == 'xor':
|
||||
return f"bit32.bxor({self.to_expr(n ^ r, depth + 1)}, {self.to_expr(r, depth + 1)})"
|
||||
elif choice == 'mul':
|
||||
if n != 0 and n % 2 == 0:
|
||||
return f"({self.to_expr(n // 2, depth + 1)} * {self.to_expr(2, depth + 1)})"
|
||||
return f"({self.to_expr(n, depth + 1)})"
|
||||
|
||||
return str(n)
|
||||
|
||||
def opaque_predicate(self):
|
||||
v1 = random.randint(100, 1000)
|
||||
v2 = random.randint(100, 1000)
|
||||
preds = [
|
||||
f"({self.to_expr(v1)} == {self.to_expr(v1)})",
|
||||
f"({self.to_expr(v1)} + {self.to_expr(v2)} > {self.to_expr(v1)})",
|
||||
f"(math.floor(math.pi) == {self.to_expr(3)})",
|
||||
f"(string.len('SUPREME') == {self.to_expr(7)})",
|
||||
f"(bit32.bor({v1}, {v1}) == {v1})"
|
||||
]
|
||||
return random.choice(preds)
|
||||
|
||||
def encrypt_string(self, s, key):
|
||||
# More complex XOR scheme: each byte depends on previous byte and index
|
||||
res = []
|
||||
last = key % 256
|
||||
for i, c in enumerate(s):
|
||||
@ -35,47 +83,36 @@ class LuauVMObfuscator:
|
||||
|
||||
def generate_vm_source(self, bytecode):
|
||||
raw_instructions = bytecode['instructions']
|
||||
# Each instruction is [OP, A, B, C]
|
||||
|
||||
# SHUFFLE AND FLATTEN:
|
||||
# We will add a 'next' index to each instruction to break linear execution.
|
||||
# Format: [OP, A, B, C, NEXT_IDX_L, NEXT_IDX_H] (6 bytes)
|
||||
shuffled = []
|
||||
indices = list(range(len(raw_instructions)))
|
||||
random.shuffle(indices)
|
||||
|
||||
# Map original index to shuffled index
|
||||
pos_map = {orig: shuffled_idx for shuffled_idx, orig in enumerate(indices)}
|
||||
|
||||
# Prepare 6-byte instructions
|
||||
final_insts = [None] * len(raw_instructions)
|
||||
for i, orig_idx in enumerate(indices):
|
||||
inst = raw_instructions[orig_idx]
|
||||
|
||||
# Find next shuffled index
|
||||
if orig_idx + 1 < len(raw_instructions):
|
||||
next_orig = orig_idx + 1
|
||||
next_shuffled = pos_map[next_orig]
|
||||
else:
|
||||
next_shuffled = 0 # End
|
||||
next_shuffled = 0
|
||||
|
||||
# Pack: [OP, A, B, C, Next_L, Next_H]
|
||||
packed = [
|
||||
inst[0], inst[1], inst[2], inst[3],
|
||||
inst[0] & 0xFF, (inst[0] >> 8) & 0xFF, # OP is now 2 bytes for more range
|
||||
inst[1], inst[2], inst[3],
|
||||
next_shuffled & 0xFF, (next_shuffled >> 8) & 0xFF
|
||||
]
|
||||
final_insts[i] = packed
|
||||
|
||||
# Pack instructions into a string
|
||||
inst_str = "".join(chr(i) for inst in final_insts for i in inst)
|
||||
inst_b64 = base64.b64encode(inst_str.encode('latin-1')).decode()
|
||||
|
||||
# Prepare constants
|
||||
encrypted_consts = []
|
||||
salt = random.randint(1000, 9999)
|
||||
salt = random.randint(10000, 99999)
|
||||
for i, c in enumerate(bytecode['constants']):
|
||||
if c['type'] == 'string':
|
||||
# Encrypt with complex key
|
||||
key = (i * 149 + salt) % 256
|
||||
enc_val = self.encrypt_string(c['value'], key)
|
||||
encrypted_consts.append({"t": 1, "v": base64.b64encode(enc_val.encode('latin-1')).decode()})
|
||||
@ -83,19 +120,40 @@ class LuauVMObfuscator:
|
||||
encrypted_consts.append({"t": 2, "v": c['value']})
|
||||
|
||||
consts_json = json.dumps(encrypted_consts)
|
||||
|
||||
# Starting shuffled index
|
||||
start_idx = pos_map[0]
|
||||
|
||||
# Obfuscated Variable Names
|
||||
V_ENV = self.get_var("env")
|
||||
V_B64 = self.get_var("b64")
|
||||
V_D = self.get_var("decode")
|
||||
V_INST = self.get_var("inst_raw")
|
||||
V_CONSTS = self.get_var("consts")
|
||||
V_SALT = self.get_var("salt")
|
||||
V_EXEC = self.get_var("execute")
|
||||
V_REGS = self.get_var("registers")
|
||||
V_CURR = self.get_var("current")
|
||||
V_RUN = self.get_var("running")
|
||||
V_GETC = self.get_var("get_const")
|
||||
V_PTR = self.get_var("ptr")
|
||||
V_OP = self.get_var("op")
|
||||
V_A = self.get_var("a")
|
||||
V_B = self.get_var("b")
|
||||
V_C = self.get_var("c")
|
||||
V_BIT = self.get_var("bit32")
|
||||
V_GS = self.get_var("getservice")
|
||||
|
||||
vm_lua = f"""
|
||||
-- [[ LUAU-VM HARDENED - V2 ]]
|
||||
local _ENV = getfenv()
|
||||
local _B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
||||
local _D = function(data)
|
||||
data = string.gsub(data, '[^'.._B64..'=]', '')
|
||||
-- [[ LUAU-VM SUPREME V3 - ROBLOX EXECUTOR EXCLUSIVE ]]
|
||||
local {V_ENV} = getfenv()
|
||||
local {V_BIT} = bit32
|
||||
local {V_GS} = game.GetService
|
||||
local {V_B64} = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
||||
local {V_D} = function(data)
|
||||
if not ({self.opaque_predicate()}) then return "" end
|
||||
data = string.gsub(data, '[^'..{V_B64}..'=]', '')
|
||||
return (data:gsub('.', function(x)
|
||||
if (x == '=') then return '' end
|
||||
local r,f='',(_B64:find(x)-1)
|
||||
local r,f='',({V_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)
|
||||
@ -105,45 +163,45 @@ local _D = function(data)
|
||||
end))
|
||||
end
|
||||
|
||||
-- Anti-Analysis Layer
|
||||
local function _CHECK()
|
||||
-- Check for common hooks or debuggers
|
||||
local suspicious = {{ "getgenv", "getrenv", "getreg", "debug" }}
|
||||
for _, s in ipairs(suspicious) do
|
||||
if _ENV[s] then
|
||||
-- Fake failure or subtle corruption
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
-- Advanced Integrity & Sandbox Detection
|
||||
local function _S()
|
||||
local d = {{ "getgenv", "getrenv", "getreg", "debug", "Drawing", "hookfunction" }}
|
||||
local c = 0
|
||||
for _, v in ipairs(d) do if {V_ENV}[v] then c = c + 1 end end
|
||||
if c == 0 and not ({self.opaque_predicate()}) then return false end
|
||||
-- Check for Roblox specific globals
|
||||
if not {V_ENV}["game"] or not {V_ENV}["task"] then return false end
|
||||
return true
|
||||
end
|
||||
|
||||
local _INST_RAW = _D('{inst_b64}')
|
||||
local _CONSTS = game:GetService("HttpService"):JSONDecode('{consts_json}')
|
||||
local _SALT = {salt}
|
||||
local {V_INST} = {V_D}('{inst_b64}')
|
||||
local {V_CONSTS} = {V_GS}(game, "HttpService"):JSONDecode('{consts_json}')
|
||||
local {V_SALT} = {self.to_expr(salt)}
|
||||
|
||||
local function _EXECUTE()
|
||||
if _CHECK() then
|
||||
-- Insert bogus delay or crash if hooked
|
||||
for i=1, 100000 do local x = math.sin(i) end
|
||||
local function {V_EXEC}()
|
||||
if not _S() then
|
||||
-- Trap: Infinite loop or crash
|
||||
local _trap = function() while true do end end
|
||||
task.spawn(_trap)
|
||||
return
|
||||
end
|
||||
|
||||
local registers = {{}}
|
||||
local current = {start_idx}
|
||||
local running = true
|
||||
local {V_REGS} = {{}}
|
||||
local {V_CURR} = {self.to_expr(start_idx)}
|
||||
local {V_RUN} = true
|
||||
|
||||
local function get_const(idx)
|
||||
local c = _CONSTS[idx + 1]
|
||||
local function {V_GETC}(idx)
|
||||
local c = {V_CONSTS}[idx + 1]
|
||||
if not c then return nil end
|
||||
if c.t == 1 then
|
||||
local raw = _D(c.v)
|
||||
local key = (idx * 149 + _SALT) % 256
|
||||
if c.t == {self.to_expr(1)} then
|
||||
local raw = {V_D}(c.v)
|
||||
local key = (idx * {self.to_expr(149)} + {V_SALT}) % {self.to_expr(256)}
|
||||
local res = ""
|
||||
local last = key % 256
|
||||
for i=1, #raw do
|
||||
local k = (key + i + last - 1) % 256
|
||||
local b = string.byte(raw, i)
|
||||
local char_code = bit32.bxor(b, k)
|
||||
local char_code = {V_BIT}.bxor(b, k)
|
||||
res = res .. string.char(char_code)
|
||||
last = char_code
|
||||
end
|
||||
@ -152,49 +210,49 @@ local function _EXECUTE()
|
||||
return c.v
|
||||
end
|
||||
|
||||
-- MEGA-DISPATCH LOOP (Control-Flow Flattening)
|
||||
while running do
|
||||
local ptr = current * 6 + 1
|
||||
if ptr > #_INST_RAW then break end
|
||||
-- SUPREME DISPATCHER
|
||||
while {V_RUN} do
|
||||
if not ({self.opaque_predicate()}) then break end
|
||||
local {V_PTR} = {V_CURR} * {self.to_expr(7)} + 1
|
||||
if {V_PTR} > #{V_INST} then break end
|
||||
|
||||
local op_raw = string.byte(_INST_RAW, ptr)
|
||||
local a = string.byte(_INST_RAW, ptr + 1)
|
||||
local b = string.byte(_INST_RAW, ptr + 2)
|
||||
local c = string.byte(_INST_RAW, ptr + 3)
|
||||
local next_l = string.byte(_INST_RAW, ptr + 4)
|
||||
local next_h = string.byte(_INST_RAW, ptr + 5)
|
||||
local op_l = string.byte({V_INST}, {V_PTR})
|
||||
local op_h = string.byte({V_INST}, {V_PTR} + 1)
|
||||
local {V_A} = string.byte({V_INST}, {V_PTR} + 2)
|
||||
local {V_B} = string.byte({V_INST}, {V_PTR} + 3)
|
||||
local {V_C} = string.byte({V_INST}, {V_PTR} + 4)
|
||||
local next_l = string.byte({V_INST}, {V_PTR} + 5)
|
||||
local next_h = string.byte({V_INST}, {V_PTR} + 6)
|
||||
|
||||
current = next_l + (next_h * 256)
|
||||
{V_CURR} = next_l + (next_h * {self.to_expr(256)})
|
||||
|
||||
-- Arithmetic Opcode Decoding
|
||||
local op = bit32.bxor(op_raw, {self.k2}) - {self.k1}
|
||||
local op_raw = op_l + (op_h * 256)
|
||||
local {V_OP} = {V_BIT}.bxor(op_raw, {self.to_expr(self.k2)}) - {self.to_expr(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]
|
||||
if {V_OP} == {self.to_expr(self.opcodes.index('MOVE'))} then
|
||||
{V_REGS}[{V_A}] = {V_REGS}[{V_B}]
|
||||
elseif {V_OP} == {self.to_expr(self.opcodes.index('LOADK'))} then
|
||||
{V_REGS}[{V_A}] = {V_GETC}({V_B})
|
||||
elseif {V_OP} == {self.to_expr(self.opcodes.index('GETGLOBAL'))} then
|
||||
{V_REGS}[{V_A}] = {V_ENV}[{V_GETC}({V_B})]
|
||||
elseif {V_OP} == {self.to_expr(self.opcodes.index('SETGLOBAL'))} then
|
||||
{V_ENV}[{V_GETC}({V_B})] = {V_REGS}[{V_A}]
|
||||
elseif {V_OP} == {self.to_expr(self.opcodes.index('CALL'))} then
|
||||
local f = {V_REGS}[{V_A}]
|
||||
local args = {{}}
|
||||
if b > 1 then for i=1, b-1 do args[i] = registers[a+i] end end
|
||||
if {V_B} > 1 then for i=1, {V_B}-1 do args[i] = {V_REGS}[{V_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
|
||||
if {V_C} > 1 then for i=1, {V_C}-1 do {V_REGS}[{V_A}+i-1] = res[i] end end
|
||||
elseif {V_OP} == {self.to_expr(self.opcodes.index('RETURN'))} then
|
||||
{V_RUN} = false
|
||||
end
|
||||
|
||||
-- Bogus execution path to confuse static analysis
|
||||
if op == -999 then
|
||||
running = false
|
||||
end
|
||||
-- Anti-Trace Junk
|
||||
if not ({self.opaque_predicate()}) then {V_RUN} = false end
|
||||
end
|
||||
end
|
||||
|
||||
task.spawn(_EXECUTE)
|
||||
task.spawn({V_EXEC})
|
||||
"""
|
||||
return vm_lua
|
||||
|
||||
@ -206,7 +264,9 @@ task.spawn(_EXECUTE)
|
||||
|
||||
def add_const(val):
|
||||
if isinstance(val, str):
|
||||
if (val.startswith("'") and val.endswith("'")) or (val.startswith("\"") and val.endswith("\"")):
|
||||
s_q = chr(39)
|
||||
d_q = chr(34)
|
||||
if (val.startswith(s_q) and val.endswith(s_q)) or (val.startswith(d_q) and val.endswith(d_q)):
|
||||
val = val[1:-1]
|
||||
|
||||
for i, c in enumerate(constants):
|
||||
|
||||
21
test_supreme.py
Normal file
21
test_supreme.py
Normal file
@ -0,0 +1,21 @@
|
||||
from core.obfuscator import obfuscate
|
||||
|
||||
code = """
|
||||
local a = 10
|
||||
local b = 20
|
||||
local c = a + b
|
||||
print("Sum is:", c)
|
||||
"""
|
||||
|
||||
obfuscated = obfuscate(code)
|
||||
print(obfuscated)
|
||||
|
||||
# Check for some patterns
|
||||
if "_l1" in obfuscated or "_I0" in obfuscated:
|
||||
print("SUCCESS: Variable renaming detected.")
|
||||
if "bit32.bxor" in obfuscated or "+" in obfuscated or "-" in obfuscated:
|
||||
# Check if it's in the VM logic, not just the original code (which might have +)
|
||||
# The VM logic uses to_expr for indices and keys.
|
||||
print("SUCCESS: Number expressions detected.")
|
||||
if "math.pi" in obfuscated or "math.sqrt" in obfuscated:
|
||||
print("SUCCESS: Opaque predicates detected.")
|
||||
Loading…
x
Reference in New Issue
Block a user