14:36
This commit is contained in:
parent
722fa03d5e
commit
bfe3f1d544
Binary file not shown.
Binary file not shown.
@ -20,7 +20,8 @@ class LuauVMObfuscator:
|
|||||||
|
|
||||||
def encrypt_constant(self, value):
|
def encrypt_constant(self, value):
|
||||||
if isinstance(value, str):
|
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]
|
value = value[1:-1]
|
||||||
return {"type": "string", "value": base64.b64encode(value.encode()).decode()}
|
return {"type": "string", "value": base64.b64encode(value.encode()).decode()}
|
||||||
try:
|
try:
|
||||||
@ -30,13 +31,9 @@ class LuauVMObfuscator:
|
|||||||
return {"type": "string", "value": base64.b64encode(str(value).encode()).decode()}
|
return {"type": "string", "value": base64.b64encode(str(value).encode()).decode()}
|
||||||
|
|
||||||
def generate_vm_source(self, bytecode_json):
|
def generate_vm_source(self, bytecode_json):
|
||||||
# Extremely hardened VM for Roblox executors
|
# Hardened VM for Luau
|
||||||
|
|
||||||
vm_lua = f"""
|
vm_lua = f"""
|
||||||
-- [[ VM-LUAU v2.5 HARDENED ]]
|
-- [[ VM-LUAU HARDENED ]]
|
||||||
-- Target: UNC-Compatible Executors
|
|
||||||
-- Anti-Debug & Identity Resilience Active
|
|
||||||
|
|
||||||
local _ENV = getfenv()
|
local _ENV = getfenv()
|
||||||
local _B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
local _B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
||||||
local _D = function(data)
|
local _D = function(data)
|
||||||
@ -56,53 +53,37 @@ end
|
|||||||
local _BYTECODE = [[{bytecode_json}]]
|
local _BYTECODE = [[{bytecode_json}]]
|
||||||
|
|
||||||
local function _VM_LIFECYCLE()
|
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 bc = game:GetService("HttpService"):JSONDecode(_BYTECODE)
|
||||||
local insts = bc.instructions
|
local insts = bc.instructions
|
||||||
local consts = bc.constants
|
local consts = bc.constants
|
||||||
local op_map = bc.op_map
|
local op_map = bc.op_map
|
||||||
|
|
||||||
local registers = {{}}
|
local registers = {{}}
|
||||||
local vip = 1 -- Virtual Instruction Pointer
|
local vip = 1
|
||||||
|
|
||||||
-- Indirect Threaded Dispatch Table (Hardened)
|
|
||||||
local dispatch = {{}}
|
local dispatch = {{}}
|
||||||
|
|
||||||
-- Register accessors with XOR masking simulation
|
|
||||||
local function set_r(i, v) registers[i] = v end
|
local function set_r(i, v) registers[i] = v end
|
||||||
local function get_r(i) return registers[i] end
|
local function get_r(i) return registers[i] end
|
||||||
|
|
||||||
-- MOVE
|
|
||||||
dispatch[op_map[{self.opcodes.index("MOVE")+1}]] = function(a, b)
|
dispatch[op_map[{self.opcodes.index("MOVE")+1}]] = function(a, b)
|
||||||
set_r(a, get_r(b))
|
set_r(a, get_r(b))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- LOADK
|
|
||||||
dispatch[op_map[{self.opcodes.index("LOADK")+1}]] = function(a, b)
|
dispatch[op_map[{self.opcodes.index("LOADK")+1}]] = function(a, b)
|
||||||
local c = consts[b+1]
|
local c = consts[b+1]
|
||||||
set_r(a, c.type == "string" and _D(c.value) or c.value)
|
set_r(a, c.type == "string" and _D(c.value) or c.value)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- GETGLOBAL
|
|
||||||
dispatch[op_map[{self.opcodes.index("GETGLOBAL")+1}]] = function(a, b)
|
dispatch[op_map[{self.opcodes.index("GETGLOBAL")+1}]] = function(a, b)
|
||||||
local n = _D(consts[b+1].value)
|
local n = _D(consts[b+1].value)
|
||||||
set_r(a, _ENV[n])
|
set_r(a, _ENV[n])
|
||||||
end
|
end
|
||||||
|
|
||||||
-- SETGLOBAL
|
|
||||||
dispatch[op_map[{self.opcodes.index("SETGLOBAL")+1}]] = function(a, b)
|
dispatch[op_map[{self.opcodes.index("SETGLOBAL")+1}]] = function(a, b)
|
||||||
local n = _D(consts[b+1].value)
|
local n = _D(consts[b+1].value)
|
||||||
_ENV[n] = get_r(a)
|
_ENV[n] = get_r(a)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- CALL
|
|
||||||
dispatch[op_map[{self.opcodes.index("CALL")+1}]] = function(a, b, c)
|
dispatch[op_map[{self.opcodes.index("CALL")+1}]] = function(a, b, c)
|
||||||
local f = get_r(a)
|
local f = get_r(a)
|
||||||
local args = {{}}
|
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
|
if c > 1 then for i=1, c-1 do set_r(a+i-1, res[i]) end end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- RETURN
|
|
||||||
dispatch[op_map[{self.opcodes.index("RETURN")+1}]] = function()
|
dispatch[op_map[{self.opcodes.index("RETURN")+1}]] = function()
|
||||||
vip = #insts + 1
|
vip = #insts + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Execution Loop
|
|
||||||
while vip <= #insts do
|
while vip <= #insts do
|
||||||
local inst = insts[vip]
|
local inst = insts[vip]
|
||||||
local f = dispatch[inst[1]]
|
local f = dispatch[inst[1]]
|
||||||
if f then
|
if f then f(inst[2], inst[3], inst[4]) end
|
||||||
f(inst[2], inst[3], inst[4])
|
|
||||||
end
|
|
||||||
vip = vip + 1
|
vip = vip + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Stealth Execution
|
task.spawn(_VM_LIFECYCLE)
|
||||||
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)
|
|
||||||
"""
|
"""
|
||||||
return vm_lua
|
return vm_lua
|
||||||
|
|
||||||
def compile_to_bytecode(self, ast):
|
def compile_to_bytecode(self, ast):
|
||||||
constants = []
|
constants = []
|
||||||
instructions = []
|
instructions = []
|
||||||
|
locals_map = {}
|
||||||
|
next_reg = 0
|
||||||
|
|
||||||
def add_const(val):
|
def add_const(val):
|
||||||
enc = self.encrypt_constant(val)
|
enc = self.encrypt_constant(val)
|
||||||
@ -148,18 +121,42 @@ end)
|
|||||||
constants.append(enc)
|
constants.append(enc)
|
||||||
return len(constants) - 1
|
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:
|
for node in ast:
|
||||||
if node['type'] == 'call':
|
if node['type'] == 'call':
|
||||||
const_idx = add_const(node['name'])
|
func_reg = next_reg
|
||||||
instructions.append([self.op_to_id["GETGLOBAL"], 0, const_idx])
|
# func_reg might be used, so we need to be careful if we have many locals
|
||||||
reg_idx = 1
|
# For this simple obfuscator, we use registers above the current locals
|
||||||
for arg in node['args']:
|
|
||||||
instructions.append([self.op_to_id["LOADK"], reg_idx, add_const(arg)])
|
# Load function
|
||||||
reg_idx += 1
|
if node['name'] in locals_map:
|
||||||
instructions.append([self.op_to_id["CALL"], 0, len(node['args']) + 1, 1])
|
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':
|
elif node['type'] == 'assign':
|
||||||
instructions.append([self.op_to_id["LOADK"], 0, add_const(node['value'])])
|
val_reg = next_reg
|
||||||
instructions.append([self.op_to_id["SETGLOBAL"], 0, add_const(node['name'])])
|
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])
|
instructions.append([self.op_to_id["RETURN"], 0, 0])
|
||||||
return {"instructions": instructions, "constants": constants, "op_map": self.op_map}
|
return {"instructions": instructions, "constants": constants, "op_map": self.op_map}
|
||||||
@ -175,7 +172,8 @@ end)
|
|||||||
bytecode = self.compile_to_bytecode(ast)
|
bytecode = self.compile_to_bytecode(ast)
|
||||||
return self.generate_vm_source(json.dumps(bytecode))
|
return self.generate_vm_source(json.dumps(bytecode))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return f"-- Error: {str(e)}"
|
import traceback
|
||||||
|
return f"-- Error: {str(e)}\n{traceback.format_exc()}"
|
||||||
|
|
||||||
def obfuscate(code):
|
def obfuscate(code):
|
||||||
return LuauVMObfuscator().obfuscate(code)
|
return LuauVMObfuscator().obfuscate(code)
|
||||||
@ -5,14 +5,25 @@ class Lexer:
|
|||||||
self.code = code
|
self.code = code
|
||||||
self.tokens = []
|
self.tokens = []
|
||||||
self.pos = 0
|
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 = [
|
self.rules = [
|
||||||
('COMMENT', re.compile(r'--\[\[.*?\].*?|--.*', re.DOTALL)),
|
('COMMENT', re.compile('--' + LB + LB + '.*?' + RB + RB + '|--.*', re.DOTALL)),
|
||||||
('STRING', re.compile(r'"(?:\\.|[^"\\])*"|\'(?:\\.|[^\\\])*\'|\[\[.*?\].*?\]', 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*')),
|
('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')),
|
('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_]*')),
|
('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):
|
def tokenize(self):
|
||||||
@ -26,7 +37,6 @@ class Lexer:
|
|||||||
self.pos = match.end()
|
self.pos = match.end()
|
||||||
break
|
break
|
||||||
if not match:
|
if not match:
|
||||||
# Skip unknown characters
|
|
||||||
self.pos += 1
|
self.pos += 1
|
||||||
return self.tokens
|
return self.tokens
|
||||||
|
|
||||||
@ -41,7 +51,7 @@ class Parser:
|
|||||||
|
|
||||||
def consume(self, expected_type=None, expected_value=None):
|
def consume(self, expected_type=None, expected_value=None):
|
||||||
token = self.peek()
|
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_type and token[0] != expected_type: return None
|
||||||
if expected_value and token[1] != expected_value: return None
|
if expected_value and token[1] != expected_value: return None
|
||||||
self.pos += 1
|
self.pos += 1
|
||||||
@ -59,15 +69,16 @@ class Parser:
|
|||||||
|
|
||||||
def parse_statement(self):
|
def parse_statement(self):
|
||||||
token = self.peek()
|
token = self.peek()
|
||||||
if not token[0]: return None
|
if not token or not token[0]: return None
|
||||||
|
|
||||||
if token[1] == 'local':
|
if token[1] == 'local':
|
||||||
self.consume()
|
self.consume()
|
||||||
ident = self.consume('IDENT')
|
ident = self.consume('IDENT')
|
||||||
if ident and self.peek()[1] == '=':
|
if ident:
|
||||||
self.consume()
|
if self.peek()[1] == '=':
|
||||||
val = self.parse_expression()
|
self.consume()
|
||||||
return {'type': 'assign', 'name': ident[1], 'value': val, 'local': True}
|
val = self.parse_expression()
|
||||||
|
return {'type': 'assign', 'name': ident[1], 'value': val, 'local': True}
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if token[0] == 'IDENT':
|
if token[0] == 'IDENT':
|
||||||
@ -92,4 +103,4 @@ class Parser:
|
|||||||
def parse_expression(self):
|
def parse_expression(self):
|
||||||
token = self.consume()
|
token = self.consume()
|
||||||
if not token: return None
|
if not token: return None
|
||||||
return token[1]
|
return {'type': token[0], 'value': token[1]}
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
aW1wb3J0IHJlCgpjbGFzcyBMZXhlcjoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBjb2RlKToKICAgICAgICBzZWxmLmNvZGUgPSBjb2RlCiAgICAgICAgc2VsZi50b2tlbnMgPSBbXQogICAgICAgIHNlbGYucG9zID0gMAogICAgICAgIHNlbGYucnVsZXMgPSBbCiAgICAgICAgICAgICgnQ09NTUVOVCcsIHInLS1cW1xbLio/XF1cXXwtLS4qJyksCiAgICAgICAgICAgICgnU1RSSU5HJywgciciKD86XC58W14iXF0pKiJ8XCcoPzpcLnxbXlwnXF0pKlwnfFxbXFsuKj9cXVxdJyksCiAgICAgICAgICAgICgnTlVNQkVSJywgcidcZCtcLj9cZConKSwKICAgICAgICAgICAgKCdLRVlXT1JEJywgcidcYihhbmR8YnJlYWt8ZG98ZWxzZXxlbHNlaWZ8ZW5kfGZhbHNlfGZvcnxmdW5jdGlvbnxpZnxpbnxsb2NhbHxuaWx8bm90fG9yfHJlcGVhdHxyZXR1cm58dGhlbnx0cnVlfHVudGlsfHdoaWxlKVxiJyksCiAgICAgICAgICAgICgnSURFTlQnLCByJ1thLXpBLVpfXVthLXpBLVowLTlfXSonKSwKICAgICAgICAgICAgKCdPUCcsIHInPT18fj18PD18Pj18XC5cLlwufFwuXC58Pj58PDx8W1wrXC1cKi8lXiM9XDw+XChcKVx7XH1cW1xdOzosXC5dJyksCiAgICAgICAgICAgICgnU1BBQ0UnLCByJ1xzKycpCiAgICAgICAgXQoKICAgIGRlZiB0b2tlbml6ZShzZWxmKToKICAgICAgICB3aGlsZSBzZWxmLnBvcyA8IGxlbihzZWxmLmNvZGUpOgogICAgICAgICAgICBtYXRjaCA9IE5vbmUKICAgICAgICAgICAgZm9yIG5hbWUsIHBhdHRlcm4gaW4gc2VsZi5ydWxlczoKICAgICAgICAgICAgICAgIHJlZ2V4ID0gcmUuY29tcGlsZShwYXR0ZXJuLCByZS5ET1RBTEwpCiAgICAgICAgICAgICAgICBtYXRjaCA9IHJlZ2V4Lm1hdGNoKHNlbGYuY29kZSwgc2VsZi5wb3MpCiAgICAgICAgICAgICAgICBpZiBtYXRjaDoKICAgICAgICAgICAgICAgICAgICBpZiBuYW1lICE9ICdTUEFDRScgYW5kIG5hbWUgIT0gJ0NPTU1FTlQnOgogICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnRva2Vucy5hcHBlbmQoKG5hbWUsIG1hdGNoLmdyb3VwKDApKSkKICAgICAgICAgICAgICAgICAgICBzZWxmLnBvcyA9IG1hdGNoLmVuZCgpCiAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgaWYgbm90IG1hdGNoOgogICAgICAgICAgICAgICAgc2VsZi5wb3MgKz0gMQogICAgICAgIHJldHVybiBzZWxmLnRva2VucwoKY2xhc3MgUGFyc2VyOgogICAgZGVmIF9faW5pdF9fKHNlbGYsIHRva2Vucyk6CiAgICAgICAgc2VsZi50b2tlbnMgPSB0b2tlbnMKICAgICAgICBzZWxmLnBvcyA9IDAKCiAgICBkZWYgcGVlayhzZWxmKToKICAgICAgICByZXR1cm4gc2VsZi50b2tlbnNbc2VsZi5wb3NdIGlmIHNlbGYucG9zIDwgbGVuKHNlbGYudG9rZW5zKSBlbHNlIChOb25lLCBOb25lKQoKICAgIGRlZiBjb25zdW1lKHNlbGYsIGV4cGVjdGVkX3R5cGU9Tm9uZSk6CiAgICAgICAgdG9rZW4gPSBzZWxmLnBlZWsoKQogICAgICAgIGlmIGV4cGVjdGVkX3R5cGUgYW5kIHRva2VuWzBdICE9IGV4cGVjdGVkX3R5cGU6CiAgICAgICAgICAgIHJldHVybiBOb25lCiAgICAgICAgc2VsZi5wb3MgKz0gMQogICAgICAgIHJldHVybiB0b2tlbgoKICAgIGRlZiBwYXJzZShzZWxmKToKICAgICAgICBub2RlcyA9IFtdCiAgICAgICAgd2hpbGUgc2VsZi5wb3MgPCBsZW4oc2VsZi50b2tlbnMpOgogICAgICAgICAgICBub2RlID0gc2VsZi5wYXJzZV9zdGF0ZW1lbnQoKQogICAgICAgICAgICBpZiBub2RlOgogICAgICAgICAgICAgICAgbm9kZXMuYXBwZW5kKG5vZGUpCiAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICBzZWxmLnBvcyArPSAxCiAgICAgICAgcmV0dXJuIG5vZGVzCgogICAgZGVmIHBhcnNlX3N0YXRlbWVudChzZWxmKToKICAgICAgICB0b2tlbiA9IHNlbGYucGVlaygpCiAgICAgICAgaWYgdG9rZW5bMF0gPT0gJ0lERU5UJzoKICAgICAgICAgICAgaWRlbnQgPSBzZWxmLmNvbnN1bWUoKVsxXQogICAgICAgICAgICBuZXh0X3Rva2VuID0gc2VsZi5wZWVrKCkKICAgICAgICAgICAgaWYgbmV4dF90b2tlblsxXSA9PSAnKCc6CiAgICAgICAgICAgICAgICBzZWxmLmNvbnN1bWUoKQogICAgICAgICAgICAgICAgYXJncyA9IFtdCiAgICAgICAgICAgICAgICB3aGlsZSBzZWxmLnBlZWsoKVsxXSAhPSAnKSc6CiAgICAgICAgICAgICAgICAgICAgYXJncy5hcHBlbmQoc2VsZi5wZWVrKClbMV0pCiAgICAgICAgICAgICAgICAgICAgc2VsZi5jb25zdW1lKCkKICAgICAgICAgICAgICAgICAgICBpZiBzZWxmLnBlZWsoKVsxXSA9PSAnLCc6CiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuY29uc3VtZSgpCiAgICAgICAgICAgICAgICBzZWxmLmNvbnN1bWUoKQogICAgICAgICAgICAgICAgcmV0dXJuIHsndHlwZSc6ICdjYWxsJywgJ25hbWUnOiBpZGVudCwgJ2FyZ3MnOiBhcmdzfQogICAgICAgICAgICBlbGlmIG5leHRfdG9rZW5bMV0gPT0gJz0nOgogICAgICAgICAgICAgICAgc2VsZi5jb25zdW1lKCkKICAgICAgICAgICAgICAgIHZhbHVlID0gc2VsZi5jb25zdW1lKClbMV0KICAgICAgICAgICAgICAgIHJldHVybiB7J3R5cGUnOiAnYXNzaWduJywgJ25hbWUnOiBpZGVudCwgJ3ZhbHVlJzogdmFsdWV9CiAgICAgICAgcmV0dXJuIE5vbmUK
|
|
||||||
Loading…
x
Reference in New Issue
Block a user