-- protected by luraph_compiler (Luraph v14.9 instruction graph) local __VM = (function() --[[ Luraph v14.9-style register VM runtime. Executes a prototype graph produced by compiler.py. Opcode numbers and operand roles follow the recovered v14.9 layout (isa.py); the X* opcodes are compiler/runtime extensions for the encodings the sample did not expose. Graph: G = { enc = true, -- string constants are encoded root = , protos = { [pid] = P, ... }, } P = {params, vararg, stack, boxed={reg,...}, code={ {op, A=,B=,C=,K1=,K2=,K3=}, ... }} Prototype ids are 1-based, pcs are 1-based, a jump field F means "pc = F + 1". Upvalues are cells {v=...}; closures are real Luau functions. ]] local unpack = table.unpack local pack = table.pack local select = select local type = type local ENV do local ok, e = pcall(function() return getfenv() -- luacheck: ignore end) ENV = ok and type(e) == 'table' and e or _G end local V = {protos = {}, enc = false, key = 0, cache = {}} local function truthy(v) return v ~= nil and v ~= false end local function decode(s) if s:byte(1) == 1 then local n = #s - 1 local out = table.create(n) local seed = (57 + V.key * 17) % 256 for i = 1, n do out[i] = string.char(bit32.bxor(s:byte(i + 1), seed, 92)) seed = (141 * seed + 29) % 256 end return table.concat(out) end return s end local function K(v) if type(v) ~= 'string' or not V.enc then return v end local c = V.cache[v] if c ~= nil then return c end c = decode(v) V.cache[v] = c return c end local function cell(v) return {v = v} end local function mkclos(pid, ups) return function(...) return V.exec(pid, ups, ...) end end local function callwith(f, q, base, argc) local args = table.create(argc) for i = 1, argc do args[i] = q[base + i] end return pack(f(unpack(args, 1, argc))) end function V.exec(pid, ups, ...) local p = V.protos[pid] if not p then error('bad prototype ' .. tostring(pid), 0) end local q = {} local np = p.params local nargs = select('#', ...) for i = 1, np do q[i - 1] = (select(i, ...)) end local va, nva = {}, 0 if p.vararg then nva = nargs - np for i = 1, nva do va[i] = select(np + i, ...) end end local code = p.code local n = #code local pc = 1 local top = -1 while pc >= 1 and pc <= n do local ins = code[pc] local op = ins[1] if op == 16 then -- LOADK q[ins.B] = K(ins.K3) pc = pc + 1 elseif op == 81 then -- MOVE q[ins.B] = q[ins.A] pc = pc + 1 elseif op == 180 then -- LOADNIL q[ins.B] = nil pc = pc + 1 elseif op == 98 then -- GETGLOBAL q[ins.B] = ENV[K(ins.K2)] pc = pc + 1 elseif op == 0x102 then -- XSETGLOBAL ENV[K(ins.K2)] = q[ins.B] pc = pc + 1 elseif op == 117 then -- GETUPVAL q[ins.A] = ups[ins.B + 1].v pc = pc + 1 elseif op == 148 then -- SETUPVAL ups[ins.A + 1].v = q[ins.C] pc = pc + 1 elseif op == 0x105 then -- XGETCELL local c = q[ins.B] q[ins.A] = type(c) == 'table' and c.v or c pc = pc + 1 elseif op == 0x106 then -- XSETCELL local c = q[ins.A] if type(c) ~= 'table' then c = cell(nil) q[ins.A] = c end c.v = q[ins.B] pc = pc + 1 elseif op == 0x110 then -- XBOXCELL q[ins.A] = cell(q[ins.A]) pc = pc + 1 elseif op == 0x10A then -- XCLOSECELLS local from = ins.A for _, r in ipairs(p.boxed or {}) do if r >= from then local c = q[r] q[r] = cell(type(c) == 'table' and c.v or c) end end pc = pc + 1 elseif op == 0 then -- GETTABLE q[ins.A] = q[ins.C][K(ins.K1)] pc = pc + 1 elseif op == 119 then -- GETTABLE_RR q[ins.C] = q[ins.A][q[ins.B]] pc = pc + 1 elseif op == 25 then -- SETTABLE q[ins.C][K(ins.K3)] = q[ins.B] pc = pc + 1 elseif op == 41 then -- SETTABLE_RR q[ins.B][q[ins.C]] = q[ins.A] pc = pc + 1 elseif op == 35 then -- NEWTABLE q[ins.A] = {} pc = pc + 1 elseif op == 0x104 then -- XSETLIST local t = q[ins.A] local from, cnt, idx = ins.B, ins.C, ins.K1 or 1 if cnt < 0 then cnt = top - from + 1 end for i = 0, cnt - 1 do t[idx + i] = q[from + i] end pc = pc + 1 elseif op == 220 or op == 4 then -- ADD q[ins.A] = q[ins.B] + q[ins.C] pc = pc + 1 elseif op == 108 then -- SUB q[ins.A] = q[ins.B] - q[ins.C] pc = pc + 1 elseif op == 43 then -- MUL q[ins.B] = q[ins.C] * q[ins.A] pc = pc + 1 elseif op == 147 then -- DIV q[ins.C] = q[ins.A] / q[ins.B] pc = pc + 1 elseif op == 45 then -- MOD q[ins.C] = q[ins.A] % q[ins.B] pc = pc + 1 elseif op == 0x10B then -- IDIV_EXT q[ins.A] = math.floor(q[ins.B] / q[ins.C]) pc = pc + 1 elseif op == 0x111 then -- XPOW q[ins.A] = q[ins.B] ^ q[ins.C] pc = pc + 1 elseif op == 208 then -- CONCAT q[ins.B] = q[ins.A] .. q[ins.C] pc = pc + 1 elseif op == 110 then -- ADDK q[ins.B] = q[ins.A] + ins.K2 pc = pc + 1 elseif op == 126 then -- SUBK q[ins.C] = q[ins.B] - ins.K3 pc = pc + 1 elseif op == 158 then -- UNM q[ins.B] = -q[ins.A] pc = pc + 1 elseif op == 67 then -- NOT q[ins.C] = not truthy(q[ins.B]) pc = pc + 1 elseif op == 92 then -- LEN q[ins.A] = #q[ins.C] pc = pc + 1 elseif op == 165 then -- EQ q[ins.B] = q[ins.A] == q[ins.C] pc = pc + 1 elseif op == 127 then -- NE q[ins.B] = q[ins.A] ~= q[ins.C] pc = pc + 1 elseif op == 55 then -- EQNIL q[ins.A] = q[ins.B] == nil pc = pc + 1 elseif op == 94 then -- LE q[ins.A] = q[ins.C] <= q[ins.B] pc = pc + 1 elseif op == 141 then -- GT q[ins.A] = q[ins.B] > q[ins.C] pc = pc + 1 elseif op == 166 then -- GE q[ins.B] = q[ins.A] >= q[ins.C] pc = pc + 1 elseif op == 173 then -- JMP pc = ins.B + 1 elseif op == 14 then -- JMPIF pc = truthy(q[ins.B]) and ins.C + 1 or pc + 1 elseif op == 72 then -- JMPIFNOT pc = truthy(q[ins.B]) and pc + 1 or ins.A + 1 elseif op == 109 then -- JMPIFEQK pc = q[ins.B] == K(ins.K2) and ins.A + 1 or pc + 1 elseif op == 207 then -- JMPIFNEQ pc = q[ins.C] ~= q[ins.B] and ins.A + 1 or pc + 1 elseif op == 157 then -- CLOSE for i = ins.B, ins.A do q[i] = nil end pc = pc + 1 elseif op == 46 then -- CALL0 local r = pack(q[ins.A]()) q[ins.A] = r[1] top = ins.A pc = pc + 1 elseif op == 171 then -- CALL1 local r = pack(q[ins.A](q[ins.A + 1])) q[ins.A] = r[1] top = ins.A pc = pc + 1 elseif op == 106 then -- CALL2 local r = pack(q[ins.B](q[ins.B + 1], q[ins.B + 2])) q[ins.B] = r[1] top = ins.B pc = pc + 1 elseif op == 44 then -- CALLN local base, argc = ins.C, ins.B - 1 local r = callwith(q[base], q, base, argc) q[base] = r[1] top = base pc = pc + 1 elseif op == 69 then -- CALLM local base, argc = ins.A, ins.B - 1 local r = callwith(q[base], q, base, argc) for i = 1, r.n do q[base + i - 1] = r[i] end top = base + r.n - 1 pc = pc + 1 elseif op == 73 then -- CALLD local base = ins.C q[base](q[base + 1]) pc = pc + 1 elseif op == 0x107 then -- XCALL local base, argc, nres = ins.A, ins.B, ins.C if argc < 0 then argc = top - base if argc < 0 then argc = 0 end end local r = callwith(q[base], q, base, argc) if nres < 0 then for i = 1, r.n do q[base + i - 1] = r[i] end top = base + r.n - 1 else for i = 1, nres do q[base + i - 1] = r[i] end top = base + nres - 1 end pc = pc + 1 elseif op == 0x109 then -- XMOVETOP local dst, src = ins.A, ins.B local cnt = top - src + 1 if cnt < 0 then cnt = 0 end for i = 0, cnt - 1 do q[dst + i] = q[src + i] end top = dst + cnt - 1 pc = pc + 1 elseif op == 65 then -- RET0 return elseif op == 58 then -- RET1 return q[ins.A] elseif op == 0x100 then -- XRET return unpack(q, ins.A, ins.A + ins.B - 1) elseif op == 0x101 then -- XRETMULTI return unpack(q, ins.A, top >= ins.A and top or ins.A - 1) elseif op == 0x103 then -- XGETVARARG local dst, cnt = ins.A, ins.B if cnt < 0 then cnt = nva end for i = 1, cnt do q[dst + i - 1] = va[i] end top = dst + cnt - 1 pc = pc + 1 elseif op == 0x10C then -- XFORNPREP local base = ins.A local limit, step = tonumber(q[base]), tonumber(q[base + 1]) local idx = tonumber(q[base + 2]) q[base], q[base + 1], q[base + 2] = limit, step, idx local go = (step > 0 and idx <= limit) or (step <= 0 and limit <= idx) pc = go and pc + 1 or ins.C + 1 elseif op == 0x10D then -- XFORNLOOP local base = ins.A local limit, step = tonumber(q[base]), tonumber(q[base + 1]) local idx = tonumber(q[base + 2]) + step q[base + 2] = idx if (step > 0 and idx <= limit) or (step <= 0 and limit <= idx) then pc = ins.C + 1 else pc = pc + 1 end elseif op == 0x10E then -- XFORGPREP pc = pc + 1 elseif op == 0x10F then -- XFORGLOOP local base, nvars = ins.A, ins.B local r = pack(q[base](q[base + 1], q[base + 2])) if r[1] == nil then pc = pc + 1 else q[base + 2] = r[1] q[base + 3] = r[1] q[base + 4] = r[2] for i = 3, nvars do q[base + 2 + i] = r[i] end pc = ins.C + 1 end elseif op == 49 then -- CLOSURE local caps = ins.K2 or {} local children = {} for i = 1, #caps do local desc = caps[i] local kind, idx = desc[1], desc[2] if kind == 0 then children[i] = cell(q[idx]) elseif kind == 1 then local c = q[idx] if type(c) ~= 'table' then c = cell(c) q[idx] = c end children[i] = c else children[i] = ups[idx + 1] end end local newclos = mkclos(ins.C, children) q[ins.A] = newclos -- A local function captures itself by value (NEWCLOSURE then -- CAPTURE VAL R[a]); fill that cell with the new closure. for i = 1, #caps do local desc = caps[i] if desc[1] == 0 and desc[2] == ins.A then children[i].v = newclos end end pc = pc + 1 elseif op == 134 or op == 0x108 then -- PARAMCOPY / XNOP pc = pc + 1 else error('unimplemented opcode ' .. tostring(op) .. ' at pc ' .. pc, 0) end end end local M = {} function M.load(graph) V.protos = graph.protos V.enc = graph.enc or false V.key = graph.key or 0 V.cache = {} M.graph = graph return M end function M.run(...) return V.exec(M.graph.root, {}, ...) end function M.setprotos(protos) V.protos = protos end return M end)() local __G = {enc=true,key=77,root=4,protos={ [1]={params=2,vararg=false,stack=7,boxed={},code={ {0,A=3,C=0,K1="\001d"}, {220,A=2,B=3,C=1}, {58,A=2}, }}, [2]={params=3,vararg=false,stack=8,boxed={},code={ {220,A=3,B=1,C=2}, {43,A=2,B=4,C=1}, {256,A=3,B=2}, }}, [3]={params=0,vararg=false,stack=4,boxed={},code={ {117,A=0,B=0}, {58,A=0}, }}, [4]={params=0,vararg=true,stack=20,boxed={},code={ {35,A=0}, {25,B=0.0,C=0,K3="\001d"}, {16,B=1,K3="\001d"}, {16,B=2,K3=2.0}, {41,A=2,B=0,C=1}, {49,A=1,C=1,K2={}}, {25,B=1,C=0,K3="\001kC\228"}, {49,A=1,C=2,K2={}}, {25,B=1,C=0,K3="\001gR\236ag"}, {16,B=3,K3=2.0}, {16,B=4,K3=5.0}, {81,A=0,B=2}, {0,A=1,C=0,K1="\001gR\236ag"}, {81,A=1,B=13}, {81,A=2,B=14}, {81,A=3,B=15}, {81,A=4,B=16}, {263,A=13,B=3,C=2}, {81,A=13,B=1}, {81,A=14,B=2}, {98,B=3,K2="\001zU\233{z"}, {16,B=6,K3=3.0}, {81,A=0,B=5}, {0,A=4,C=0,K1="\001kC\228"}, {106,B=4}, {81,A=1,B=5}, {81,A=2,B=6}, {81,A=3,B=13}, {81,A=4,B=14}, {81,A=5,B=15}, {81,A=6,B=16}, {263,A=13,B=3,C=0}, {35,A=3}, {25,B=0.0,C=3,K3="\001r"}, {16,B=4,K3="\001r"}, {16,B=5,K3=10.0}, {41,A=5,B=3,C=4}, {49,A=4,C=3,K2={{0,3}}}, {98,B=5,K2="\001zU\233{z"}, {81,A=4,B=6}, {46,A=6}, {0,A=6,C=6,K1="\001r"}, {16,B=7,K3="\001/T\1730j"}, {16,B=9,K3="\001|"}, {16,B=10,K3=4.0}, {81,A=7,B=8}, {0,A=7,C=7,K1="\001lH\242xoo"}, {81,A=7,B=13}, {81,A=8,B=14}, {81,A=9,B=15}, {81,A=10,B=16}, {263,A=13,B=3,C=-1}, {265,A=7,B=13}, {81,A=5,B=13}, {265,A=14,B=6}, {263,A=13,B=-1,C=0}, {65,A=0}, }}, }} return __VM.load(__G).run()