14:56
This commit is contained in:
parent
a998fae926
commit
33ef3d1b36
Binary file not shown.
@ -14,31 +14,69 @@ class LuauVMObfuscator:
|
|||||||
"LT", "LE", "TEST", "TESTSET", "CALL", "TAILCALL", "RETURN",
|
"LT", "LE", "TEST", "TESTSET", "CALL", "TAILCALL", "RETURN",
|
||||||
"FORLOOP", "FORPREP", "TFORLOOP", "SETLIST", "CLOSE", "CLOSURE", "VARARG"
|
"FORLOOP", "FORPREP", "TFORLOOP", "SETLIST", "CLOSE", "CLOSURE", "VARARG"
|
||||||
]
|
]
|
||||||
# Randomize opcode mapping
|
|
||||||
self.op_order = list(range(len(self.opcodes)))
|
|
||||||
random.shuffle(self.op_order)
|
|
||||||
|
|
||||||
# Arithmetic keys for opcode decoding
|
# Arithmetic keys for opcode decoding
|
||||||
self.k1 = random.randint(10, 100)
|
self.k1 = random.randint(50, 200)
|
||||||
self.k2 = random.randint(10, 100)
|
self.k2 = random.randint(50, 200)
|
||||||
|
self.k3 = random.randint(1, 10) # Multiplier/Step
|
||||||
|
|
||||||
# Opcode to encoded ID: (real_index + k1) ^ k2
|
# 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) % 256 for name in self.opcodes}
|
||||||
|
|
||||||
def encrypt_string(self, s, key):
|
def encrypt_string(self, s, key):
|
||||||
return "".join(chr(ord(c) ^ ((key + i) % 256)) for i, c in enumerate(s))
|
# More complex XOR scheme: each byte depends on previous byte and index
|
||||||
|
res = []
|
||||||
|
last = key % 256
|
||||||
|
for i, c in enumerate(s):
|
||||||
|
k = (key + i + last) % 256
|
||||||
|
last = ord(c)
|
||||||
|
res.append(chr(ord(c) ^ k))
|
||||||
|
return "".join(res)
|
||||||
|
|
||||||
def generate_vm_source(self, bytecode):
|
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
|
||||||
|
|
||||||
|
# Pack: [OP, A, B, C, Next_L, Next_H]
|
||||||
|
packed = [
|
||||||
|
inst[0], inst[1], inst[2], inst[3],
|
||||||
|
next_shuffled & 0xFF, (next_shuffled >> 8) & 0xFF
|
||||||
|
]
|
||||||
|
final_insts[i] = packed
|
||||||
|
|
||||||
# Pack instructions into a string
|
# Pack instructions into a string
|
||||||
inst_str = "".join(chr(i) for inst in bytecode['instructions'] for i in inst)
|
inst_str = "".join(chr(i) for inst in final_insts for i in inst)
|
||||||
inst_b64 = base64.b64encode(inst_str.encode('latin-1')).decode()
|
inst_b64 = base64.b64encode(inst_str.encode('latin-1')).decode()
|
||||||
|
|
||||||
# Prepare constants
|
# Prepare constants
|
||||||
encrypted_consts = []
|
encrypted_consts = []
|
||||||
|
salt = random.randint(1000, 9999)
|
||||||
for i, c in enumerate(bytecode['constants']):
|
for i, c in enumerate(bytecode['constants']):
|
||||||
if c['type'] == 'string':
|
if c['type'] == 'string':
|
||||||
# Encrypt with index-based key
|
# Encrypt with complex key
|
||||||
key = (i * 137 + 42) % 256
|
key = (i * 149 + salt) % 256
|
||||||
enc_val = self.encrypt_string(c['value'], key)
|
enc_val = self.encrypt_string(c['value'], key)
|
||||||
encrypted_consts.append({"t": 1, "v": base64.b64encode(enc_val.encode('latin-1')).decode()})
|
encrypted_consts.append({"t": 1, "v": base64.b64encode(enc_val.encode('latin-1')).decode()})
|
||||||
else:
|
else:
|
||||||
@ -46,8 +84,11 @@ class LuauVMObfuscator:
|
|||||||
|
|
||||||
consts_json = json.dumps(encrypted_consts)
|
consts_json = json.dumps(encrypted_consts)
|
||||||
|
|
||||||
|
# Starting shuffled index
|
||||||
|
start_idx = pos_map[0]
|
||||||
|
|
||||||
vm_lua = f"""
|
vm_lua = f"""
|
||||||
-- [[ LUAU-VM PROTECTED ]]
|
-- [[ LUAU-VM HARDENED - V2 ]]
|
||||||
local _ENV = getfenv()
|
local _ENV = getfenv()
|
||||||
local _B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
local _B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
||||||
local _D = function(data)
|
local _D = function(data)
|
||||||
@ -64,12 +105,31 @@ local _D = function(data)
|
|||||||
end))
|
end))
|
||||||
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
|
||||||
|
end
|
||||||
|
|
||||||
local _INST_RAW = _D('{inst_b64}')
|
local _INST_RAW = _D('{inst_b64}')
|
||||||
local _CONSTS = game:GetService("HttpService"):JSONDecode('{consts_json}')
|
local _CONSTS = game:GetService("HttpService"):JSONDecode('{consts_json}')
|
||||||
|
local _SALT = {salt}
|
||||||
|
|
||||||
local function _EXECUTE()
|
local function _EXECUTE()
|
||||||
|
if _CHECK() then
|
||||||
|
-- Insert bogus delay or crash if hooked
|
||||||
|
for i=1, 100000 do local x = math.sin(i) end
|
||||||
|
end
|
||||||
|
|
||||||
local registers = {{}}
|
local registers = {{}}
|
||||||
local vip = 1
|
local current = {start_idx}
|
||||||
local running = true
|
local running = true
|
||||||
|
|
||||||
local function get_const(idx)
|
local function get_const(idx)
|
||||||
@ -77,22 +137,34 @@ local function _EXECUTE()
|
|||||||
if not c then return nil end
|
if not c then return nil end
|
||||||
if c.t == 1 then
|
if c.t == 1 then
|
||||||
local raw = _D(c.v)
|
local raw = _D(c.v)
|
||||||
local key = (idx * 137 + 42) % 256
|
local key = (idx * 149 + _SALT) % 256
|
||||||
local res = ""
|
local res = ""
|
||||||
|
local last = key % 256
|
||||||
for i=1, #raw do
|
for i=1, #raw do
|
||||||
res = res .. string.char(bit32.bxor(string.byte(raw, i), (key + i - 1) % 256))
|
local k = (key + i + last - 1) % 256
|
||||||
|
local b = string.byte(raw, i)
|
||||||
|
local char_code = bit32.bxor(b, k)
|
||||||
|
res = res .. string.char(char_code)
|
||||||
|
last = char_code
|
||||||
end
|
end
|
||||||
return res
|
return res
|
||||||
end
|
end
|
||||||
return c.v
|
return c.v
|
||||||
end
|
end
|
||||||
|
|
||||||
while running and vip <= #_INST_RAW do
|
-- MEGA-DISPATCH LOOP (Control-Flow Flattening)
|
||||||
local op_raw = string.byte(_INST_RAW, vip)
|
while running do
|
||||||
local a = string.byte(_INST_RAW, vip + 1)
|
local ptr = current * 6 + 1
|
||||||
local b = string.byte(_INST_RAW, vip + 2)
|
if ptr > #_INST_RAW then break end
|
||||||
local c = string.byte(_INST_RAW, vip + 3)
|
|
||||||
vip = vip + 4
|
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)
|
||||||
|
|
||||||
|
current = next_l + (next_h * 256)
|
||||||
|
|
||||||
-- Arithmetic Opcode Decoding
|
-- Arithmetic Opcode Decoding
|
||||||
local op = bit32.bxor(op_raw, {self.k2}) - {self.k1}
|
local op = bit32.bxor(op_raw, {self.k2}) - {self.k1}
|
||||||
@ -114,6 +186,11 @@ local function _EXECUTE()
|
|||||||
elseif op == {self.opcodes.index('RETURN')} then
|
elseif op == {self.opcodes.index('RETURN')} then
|
||||||
running = false
|
running = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Bogus execution path to confuse static analysis
|
||||||
|
if op == -999 then
|
||||||
|
running = false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -129,7 +206,7 @@ task.spawn(_EXECUTE)
|
|||||||
|
|
||||||
def add_const(val):
|
def add_const(val):
|
||||||
if isinstance(val, str):
|
if isinstance(val, str):
|
||||||
if (val.startswith('"') and val.endswith('"')) or (val.startswith("'" ) and val.endswith("'" )):
|
if (val.startswith("'") and val.endswith("'")) or (val.startswith("\"") and val.endswith("\"")):
|
||||||
val = val[1:-1]
|
val = val[1:-1]
|
||||||
|
|
||||||
for i, c in enumerate(constants):
|
for i, c in enumerate(constants):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user