Advertisement
zqozr

king zq disassembler

Oct 4th, 2022
730
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 43.67 KB | None | 0 0
  1. local function deserialize(bytecode)
  2.     local reader do
  3.         reader = {}
  4.         pos = 1
  5.         function reader:pos() return pos end
  6.         function reader:nextByte()
  7.             local v = bytecode:byte(pos, pos)
  8.             pos = pos + 1
  9.             return v
  10.         end
  11.         function reader:nextChar()
  12.             return string.char(reader:nextByte());
  13.         end
  14.         function reader:nextInt()
  15.             local b = { reader:nextByte(), reader:nextByte(), reader:nextByte(), reader:nextByte() }
  16.             return (
  17.                 bit32.bor(bit32.lshift(b[4], 24),
  18.                 bit32.bor(bit32.lshift(b[3], 16),
  19.                 bit32.bor(bit32.lshift(b[2], 8),
  20.                 b[1])))
  21.             )
  22.         end
  23.         function reader:nextVarInt()
  24.             local c1, c2, b, r = 0, 0, 0, 0
  25.             repeat
  26.                 c1 = reader:nextByte()
  27.                 c2 = bit32.band(c1, 0x7F)
  28.                 r = bit32.bor(r, bit32.lshift(c2, b))
  29.                 b += 7
  30.             until not bit32.btest(c1, 0x80)
  31.             return r;
  32.         end
  33.         function reader:nextString()
  34.             local result = ""
  35.             local len = reader:nextVarInt();
  36.             for i = 1, len do
  37.                 result = result .. reader:nextChar();
  38.             end
  39.             return result;
  40.         end
  41.         function reader:nextDouble()
  42.             local b = {};
  43.             for i = 1, 8 do
  44.                 table.insert(b, reader:nextByte());
  45.             end
  46.             local str = '';
  47.             for i = 1, 8 do
  48.                 str = str .. string.char(b[i]);
  49.             end
  50.             return string.unpack("<d", str)
  51.         end
  52.     end
  53.  
  54.     local status = reader:nextByte()
  55.     if (status ~= 0) then
  56.         local protoTable = {}
  57.         local stringTable = {}
  58.        
  59.         local sizeStrings = reader:nextVarInt()
  60.         for i = 1,sizeStrings do
  61.             stringTable[i] = reader:nextString()
  62.         end
  63.        
  64.         local sizeProtos = reader:nextVarInt();
  65.         for i = 1,sizeProtos do
  66.             protoTable[i] = {} -- pre-initialize an entry
  67.             protoTable[i].codeTable = {}
  68.             protoTable[i].kTable = {}
  69.             protoTable[i].pTable = {}
  70.             protoTable[i].smallLineInfo = {}
  71.             protoTable[i].largeLineInfo = {}
  72.         end
  73.        
  74.         for i = 1,sizeProtos do
  75.             local proto = protoTable[i]
  76.             proto.maxStackSize = reader:nextByte()
  77.             proto.numParams = reader:nextByte()
  78.             proto.numUpValues = reader:nextByte()
  79.             proto.isVarArg = reader:nextByte()
  80.            
  81.             proto.sizeCode = reader:nextVarInt()
  82.             for j = 1,proto.sizeCode do
  83.                 proto.codeTable[j] = reader:nextInt()
  84.             end
  85.            
  86.             proto.sizeConsts = reader:nextVarInt();
  87.             for j = 1,proto.sizeConsts do
  88.                 local k = {};
  89.                 k.type = reader:nextByte();
  90.                 if k.type == 1 then -- boolean
  91.                     k.value = (reader:nextByte() == 1 and true or false)
  92.                 elseif k.type == 2 then -- number
  93.                     k.value = reader:nextDouble()
  94.                 elseif k.type == 3 then -- string
  95.                     k.value = stringTable[reader:nextVarInt()]
  96.                 elseif k.type == 4 then -- cache
  97.                     k.value = reader:nextInt()
  98.                 elseif k.type == 5 then -- table
  99.                     k.value = { ["size"] = reader:nextVarInt(), ["ids"] = {} }
  100.                     for s = 1,k.value.size do
  101.                         table.insert(k.value.ids, reader:nextVarInt() + 1)
  102.                     end
  103.                 elseif k.type == 6 then -- closure
  104.                     k.value = reader:nextVarInt() + 1 -- closure id
  105.                 elseif k.type ~= 0 then
  106.                     error(string.format("Unrecognized constant type: %i", k.type))
  107.                 end
  108.                 proto.kTable[j] = k
  109.             end
  110.            
  111.             proto.sizeProtos = reader:nextVarInt();
  112.             for j = 1,proto.sizeProtos do
  113.                 proto.pTable[j] = protoTable[reader:nextVarInt() + 1]
  114.             end
  115.            
  116.             proto.lineDefined = reader:nextVarInt()
  117.            
  118.             local protoSourceId = reader:nextVarInt()
  119.             proto.source = stringTable[protoSourceId]
  120.            
  121.             if (reader:nextByte() == 1) then -- Has Line info?
  122.                 local compKey = reader:nextVarInt()
  123.                 for j = 1,proto.sizeCode do
  124.                     proto.smallLineInfo[j] = reader:nextByte()
  125.                 end
  126.                
  127.                 local n = bit32.band(proto.sizeCode + 3, -4)
  128.                 local intervals = bit32.rshift(proto.sizeCode - 1, compKey) + 1
  129.                
  130.                 for j = 1,intervals do
  131.                     proto.largeLineInfo[j] = reader:nextInt()
  132.                 end
  133.             end
  134.            
  135.             if (reader:nextByte() == 1) then -- Has Debug info?
  136.                 error'disassemble() can only be called on ROBLOX scripts'
  137.             end
  138.         end
  139.        
  140.         local mainProtoId = reader:nextVarInt()
  141.         return protoTable[mainProtoId + 1], protoTable, stringTable;
  142.     else
  143.         error(string.format("Invalid bytecode (version: %i)", status))
  144.         return nil;
  145.     end
  146. end
  147.  
  148. local function getluauoptable()
  149.     return {
  150.         -- I could use case multiplier, but that depends only on how accurate
  151.         -- our ordering of the opcodes are -- so if we really want to rely on
  152.         -- the latest updated luau source, then we could do it that way.
  153.         { ["name"] = "NOP", ["type"] = "none", ["case"] = 0, ["number"] = 0x00 },
  154.         { ["name"] = "BREAK", ["type"] = "none", ["case"] = 1, ["number"] = 0xE3 },
  155.         { ["name"] = "LOADNIL", ["type"] = "iA", ["case"] = 2, ["number"] = 0xC6 },
  156.         { ["name"] = "LOADB", ["type"] = "iABC", ["case"] = 3, ["number"] = 0xA9 },
  157.         { ["name"] = "LOADN", ["type"] = "iABx", ["case"] = 4, ["number"] = 0x8C },
  158.         { ["name"] = "LOADK", ["type"] = "iABx", ["case"] = 5, ["number"] = 0x6F },
  159.         { ["name"] = "MOVE", ["type"] = "iAB", ["case"] = 6, ["number"] = 0x52 },
  160.         { ["name"] = "GETGLOBAL", ["type"] = "iAC", ["case"] = 7, ["number"] = 0x35, ["aux"] = true },
  161.         { ["name"] = "SETGLOBAL", ["type"] = "iAC", ["case"] = 8, ["number"] = 0x18, ["aux"] = true },
  162.         { ["name"] = "GETUPVAL", ["type"] = "iAB", ["case"] = 9, ["number"] = 0xFB },
  163.         { ["name"] = "SETUPVAL", ["type"] = "iAB", ["case"] = 10, ["number"] = 0xDE },
  164.         { ["name"] = "CLOSEUPVALS", ["type"] = "iA", ["case"] = 11, ["number"] = 0xC1 },
  165.         { ["name"] = "GETIMPORT", ["type"] = "iABx", ["case"] = 12, ["number"] = 0xA4, ["aux"] = true },
  166.         { ["name"] = "GETTABLE", ["type"] = "iABC", ["case"] = 13, ["number"] = 0x87 },
  167.         { ["name"] = "SETTABLE", ["type"] = "iABC", ["case"] = 14, ["number"] = 0x6A },
  168.         { ["name"] = "GETTABLEKS", ["type"] = "iABC", ["case"] = 15, ["number"] = 0x4D, ["aux"] = true },
  169.         { ["name"] = "SETTABLEKS", ["type"] = "iABC", ["case"] = 16, ["number"] = 0x30, ["aux"] = true },
  170.         { ["name"] = "GETTABLEN", ["type"] = "iABC", ["case"] = 17, ["number"] = 0x13 },
  171.         { ["name"] = "SETTABLEN", ["type"] = "iABC", ["case"] = 18, ["number"] = 0xF6 },
  172.         { ["name"] = "NEWCLOSURE", ["type"] = "iABx", ["case"] = 19, ["number"] = 0xD9 },
  173.         { ["name"] = "NAMECALL", ["type"] = "iABC", ["case"] = 20, ["number"] = 0xBC, ["aux"] = true },
  174.         { ["name"] = "CALL", ["type"] = "iABC", ["case"] = 21, ["number"] = 0x9F },
  175.         { ["name"] = "RETURN", ["type"] = "iAB", ["case"] = 22, ["number"] = 0x82 },
  176.         { ["name"] = "JUMP", ["type"] = "isBx", ["case"] = 23, ["number"] = 0x65 },
  177.         { ["name"] = "JUMPBACK", ["type"] = "isBx", ["case"] = 24, ["number"] = 0x48 },
  178.         { ["name"] = "JUMPIF", ["type"] = "iAsBx", ["case"] = 25, ["number"] = 0x2B },
  179.         { ["name"] = "JUMPIFNOT", ["type"] = "iAsBx", ["case"] = 26, ["number"] = 0x0E },
  180.         { ["name"] = "JUMPIFEQ", ["type"] = "iAsBx", ["case"] = 27, ["number"] = 0xF1, ["aux"] = true },
  181.         { ["name"] = "JUMPIFLE", ["type"] = "iAsBx", ["case"] = 28, ["number"] = 0xD4, ["aux"] = true },
  182.         { ["name"] = "JUMPIFLT", ["type"] = "iAsBx", ["case"] = 29, ["number"] = 0xB7, ["aux"] = true },
  183.         { ["name"] = "JUMPIFNOTEQ", ["type"] = "iAsBx", ["case"] = 30, ["number"] = 0x9A, ["aux"] = true },
  184.         { ["name"] = "JUMPIFNOTLE", ["type"] = "iAsBx", ["case"] = 31, ["number"] = 0x7D, ["aux"] = true },
  185.         { ["name"] = "JUMPIFNOTLT", ["type"] = "iAsBx", ["case"] = 32, ["number"] = 0x60, ["aux"] = true },
  186.         { ["name"] = "ADD", ["type"] = "iABC", ["case"] = 33, ["number"] = 0x43 },
  187.         { ["name"] = "SUB", ["type"] = "iABC", ["case"] = 34, ["number"] = 0x26 },
  188.         { ["name"] = "MUL", ["type"] = "iABC", ["case"] = 35, ["number"] = 0x09 },
  189.         { ["name"] = "DIV", ["type"] = "iABC", ["case"] = 36, ["number"] = 0xEC },
  190.         { ["name"] = "MOD", ["type"] = "iABC", ["case"] = 37, ["number"] = 0xCF },
  191.         { ["name"] = "POW", ["type"] = "iABC", ["case"] = 38, ["number"] = 0xB2 },
  192.         { ["name"] = "ADDK", ["type"] = "iABC", ["case"] = 39, ["number"] = 0x95 },
  193.         { ["name"] = "SUBK", ["type"] = "iABC", ["case"] = 40, ["number"] = 0x78 },
  194.         { ["name"] = "MULK", ["type"] = "iABC", ["case"] = 41, ["number"] = 0x5B },
  195.         { ["name"] = "DIVK", ["type"] = "iABC", ["case"] = 42, ["number"] = 0x3E },
  196.         { ["name"] = "MODK", ["type"] = "iABC", ["case"] = 43, ["number"] = 0x21 },
  197.         { ["name"] = "POWK", ["type"] = "iABC", ["case"] = 44, ["number"] = 0x04 },
  198.         { ["name"] = "AND", ["type"] = "iABC", ["case"] = 45, ["number"] = 0xE7 },
  199.         { ["name"] = "OR", ["type"] = "iABC", ["case"] = 46, ["number"] = 0xCA },
  200.         { ["name"] = "ANDK", ["type"] = "iABC", ["case"] = 47, ["number"] = 0xAD },
  201.         { ["name"] = "ORK", ["type"] = "iABC", ["case"] = 48, ["number"] = 0x90 },
  202.         { ["name"] = "CONCAT", ["type"] = "iABC", ["case"] = 49, ["number"] = 0x73 },
  203.         { ["name"] = "NOT", ["type"] = "iAB", ["case"] = 50, ["number"] = 0x56 },
  204.         { ["name"] = "UNM", ["type"] = "iAB", ["case"] = 51, ["number"] = 0x39 },
  205.         { ["name"] = "LEN", ["type"] = "iAB", ["case"] = 52, ["number"] = 0x1C },
  206.         { ["name"] = "NEWTABLE", ["type"] = "iAB", ["case"] = 53, ["number"] = 0xFF, ["aux"] = true },
  207.         { ["name"] = "DUPTABLE", ["type"] = "iABx", ["case"] = 54, ["number"] = 0xE2 },
  208.         { ["name"] = "SETLIST", ["type"] = "iABC", ["case"] = 55, ["number"] = 0xC5, ["aux"] = true },
  209.         { ["name"] = "NFORPREP", ["type"] = "iABx", ["case"] = 56, ["number"] = 0xA8 },
  210.         { ["name"] = "NFORLOOP", ["type"] = "iABx", ["case"] = 57, ["number"] = 0x8B },
  211.         { ["name"] = "TFORLOOP", ["type"] = "iABx", ["case"] = 58, ["number"] = 0x6E, ["aux"] = true },
  212.         { ["name"] = "IPAIRSPREP", ["type"] = "none", ["case"] = 59, ["number"] = 0x51 },
  213.         { ["name"] = "IPAIRSLOOP", ["type"] = "none", ["case"] = 60, ["number"] = 0x34 },
  214.         { ["name"] = "PAIRSPREP", ["type"] = "none", ["case"] = 61, ["number"] = 0x17 },
  215.         { ["name"] = "PAIRSLOOP", ["type"] = "none", ["case"] = 62, ["number"] = 0xFA },
  216.         { ["name"] = "GETVARARGS", ["type"] = "iAB", ["case"] = 63, ["number"] = 0xDD },
  217.         { ["name"] = "DUPCLOSURE", ["type"] = "iABx", ["case"] = 64, ["number"] = 0xC0 },
  218.         { ["name"] = "PREPVARARGS", ["type"] = "iA", ["case"] = 65, ["number"] = 0xA3 },
  219.         { ["name"] = "LOADKX", ["type"] = "iA", ["case"] = 66, ["number"] = 0x86 },
  220.         { ["name"] = "JUMPX", ["type"] = "isAx", ["case"] = 67, ["number"] = 0x69 },
  221.         { ["name"] = "FASTCALL", ["type"] = "iAC", ["case"] = 68, ["number"] = 0x4C },
  222.         { ["name"] = "COVERAGE", ["type"] = "isAx", ["case"] = 69, ["number"] = 0x2F },
  223.         { ["name"] = "CAPTURE", ["type"] = "iAB", ["case"] = 70, ["number"] = 0x12 },
  224.         { ["name"] = "JUMPIFEQK", ["type"] = "iABx", ["case"] = 71, ["number"] = 0xF5, ["aux"] = true  },
  225.         { ["name"] = "JUMPIFNOTEQK", ["type"] = "iABx", ["case"] = 72, ["number"] = 0xD8, ["aux"] = true  },
  226.         { ["name"] = "FASTCALL1", ["type"] = "iABC", ["case"] = 73, ["number"] = 0xBB },
  227.         { ["name"] = "FASTCALL2", ["type"] = "iABC", ["case"] = 74, ["number"] = 0x9E, ["aux"] = true },
  228.         { ["name"] = "FASTCALL2K", ["type"] = "iABC", ["case"] = 75, ["number"] = 0x81, ["aux"] = true },
  229.         { ["name"] = "COUNT", ["type"] = "none", ["case"] = 76, ["number"] = 0x64 }
  230.     };
  231. end
  232.  
  233. local luau = {};
  234. luau.SIZE_A = 8
  235. luau.SIZE_C = 8
  236. luau.SIZE_B = 8
  237. luau.SIZE_Bx = (luau.SIZE_C + luau.SIZE_B)
  238. luau.SIZE_OP = 8
  239. luau.POS_OP = 0
  240. luau.POS_A = (luau.POS_OP + luau.SIZE_OP)
  241. luau.POS_B = (luau.POS_A + luau.SIZE_A)
  242. luau.POS_C = (luau.POS_B + luau.SIZE_B)
  243. luau.POS_Bx = luau.POS_B
  244. luau.MAXARG_A = (bit32.lshift(1, luau.SIZE_A) - 1)
  245. luau.MAXARG_B = (bit32.lshift(1, luau.SIZE_B) - 1)
  246. luau.MAXARG_C = (bit32.lshift(1, luau.SIZE_C) - 1)
  247. luau.MAXARG_Bx = (bit32.lshift(1, luau.SIZE_Bx) - 1)
  248. luau.MAXARG_sBx = bit32.rshift(luau.MAXARG_Bx, 1)
  249. luau.BITRK = bit32.lshift(1, (luau.SIZE_B - 1))
  250. luau.MAXINDEXRK = (luau.BITRK - 1)
  251. luau.ISK = function(x) return bit32.band(x, luau.BITRK) end
  252. luau.INDEXK = function(x) return bit32.band(x, bit32.bnot(luau.BITRK)) end
  253. luau.RKASK = function(x) return bit32.bor(x, luau.BITRK) end
  254. luau.MASK1 = function(n,p) return bit32.lshift(bit32.bnot(bit32.lshift(bit32.bnot(0), n)), p) end
  255. luau.MASK0 = function(n,p) return bit32.bnot(luau.MASK1(n, p)) end
  256. luau.GETARG_A = function(i) return bit32.band(bit32.rshift(i, luau.POS_A), luau.MASK1(luau.SIZE_A, 0)) end
  257. luau.GETARG_B = function(i) return bit32.band(bit32.rshift(i, luau.POS_B), luau.MASK1(luau.SIZE_B, 0)) end
  258. luau.GETARG_C = function(i) return bit32.band(bit32.rshift(i, luau.POS_C), luau.MASK1(luau.SIZE_C, 0)) end
  259. luau.GETARG_Bx = function(i) return bit32.band(bit32.rshift(i, luau.POS_Bx), luau.MASK1(luau.SIZE_Bx, 0)) end
  260. luau.GETARG_sBx = function(i) local Bx = luau.GETARG_Bx(i) local sBx = Bx + 1; if Bx > 0x7FFF and Bx <= 0xFFFF then sBx = -(0xFFFF - Bx); sBx = sBx - 1; end return sBx end
  261. luau.GETARG_sAx = function(i) return bit32.rshift(i, 8) end
  262. luau.GET_OPCODE = function(i) return bit32.band(bit32.rshift(i, luau.POS_OP), luau.MASK1(luau.SIZE_OP, 0)) end
  263.  
  264. local function disassemble(a1, showOps)
  265.     if (typeof(a1):lower() == "instance") then
  266.         if not getscriptbytecode then error("Executor does not support getscriptbytecode") end
  267.         a1 = getscriptbytecode(a1);
  268.     end
  269.    
  270.     if type(a1) == "table" then
  271.         -- I just prefer bytecode strings
  272.         local t = a1;
  273.         at = "";
  274.         for i = 1,#t do
  275.             a1 = a1 .. string.char(t[i]);
  276.         end
  277.     end
  278.    
  279.     local output = ""
  280.     local mainProto, protoTable, stringTable = deserialize(a1)
  281.     local luauOpTable = getluauoptable();
  282.    
  283.     local function getOpCode(opName)
  284.         for _,v in pairs(luauOpTable) do
  285.             if v.name == opName then
  286.                 return v.number;
  287.             end
  288.         end
  289.         return 0;
  290.     end
  291.  
  292.     mainProto.source = "main"
  293.     mainScope = {}; -- scope control, coming soon
  294.    
  295.    
  296.     local function readProto(proto, depth)
  297.         local output = "";
  298.        
  299.         local function addTabSpace(depth)
  300.             output = output .. string.rep("    ", depth)
  301.         end
  302.        
  303.         -- using function name (this will be removed & done outside of readProto)
  304.         if proto.source then
  305.             output = output .. proto.source .. " = function("
  306.         else
  307.             output = output .. "function("
  308.         end
  309.        
  310.         for i = 1,proto.numParams do
  311.             output = output .. "arg" .. (i - 1) -- args coincide with stack index
  312.             if i < proto.numParams then
  313.                 output = output .. ", "
  314.             end
  315.         end
  316.        
  317.         if proto.isVarArg ~= 0 then
  318.             if proto.numParams > 0 then
  319.                 output = output .. ", "
  320.             end
  321.             output = output .. "..."
  322.         end
  323.        
  324.         output = output .. ")\n"
  325.  
  326.         depth = depth + 1
  327.        
  328.         for i = 1,proto.numParams do
  329.             addTabSpace(depth);
  330.             output = output .. string.format("local var%i = arg%i\n", i - 1, i - 1);
  331.         end
  332.        
  333.         local refData = {}
  334.         local nameCall = nil
  335.         local markedAux = false
  336.         local codeIndex = 1
  337.         while codeIndex < proto.sizeCode do
  338.             local i = proto.codeTable[codeIndex]
  339.             local opc = luau.GET_OPCODE(i)
  340.             local A = luau.GETARG_A(i)
  341.             local B = luau.GETARG_B(i)
  342.             local Bx = luau.GETARG_Bx(i)
  343.             local C = luau.GETARG_C(i)
  344.             local sBx = luau.GETARG_sBx(i)
  345.             local sAx = luau.GETARG_sAx(i)
  346.             local aux = proto.codeTable[codeIndex + 1]
  347.            
  348.             if markedAux then
  349.                 markedAux = false
  350.             else
  351.                 addTabSpace(depth);
  352.                
  353.                 local opinfo;
  354.                
  355.                 for _,v in pairs(luauOpTable) do
  356.                     if v.number == opc then
  357.                         opinfo = v
  358.                         break;
  359.                     end
  360.                 end
  361.                
  362.                 output = output .. tostring(codeIndex) .. ".   "
  363.                
  364.                 if showOps and opinfo then
  365.                     local str = opinfo.name .. string.rep(" ", 16 - string.len(opinfo.name))
  366.                    
  367.                     if opinfo.type == "iA" then
  368.                         str = str .. string.format("%i", A)
  369.                     elseif opinfo.type == "iAB" then
  370.                         str = str .. string.format("%i %i", A, B)
  371.                     elseif opinfo.type == "iAC" then
  372.                         str = str .. string.format("%i %i", A, C)
  373.                     elseif opinfo.type == "iABx" then
  374.                         str = str .. string.format("%i %i", A, Bx)
  375.                     elseif opinfo.type == "iAsBx" then
  376.                         str = str .. string.format("%i %i", A, sBx)
  377.                     elseif opinfo.type == "isBx" then
  378.                         str = str .. string.format("%i", sBx)
  379.                     elseif opinfo.type == "iABC" then
  380.                         str = str .. string.format("%i %i %i", A, B, C)
  381.                     end
  382.                    
  383.                     if opinfo.aux then
  384.                         str = str .. " [aux]";
  385.                         markedAux = true
  386.                     end
  387.                    
  388.                     output = output .. str .. string.rep(" ", 40 - string.len(str))
  389.                 else
  390.                     if opinfo then
  391.                         if opinfo.aux then
  392.                             markedAux = true;
  393.                         end
  394.                     end
  395.                 end
  396.                
  397.                 -- continue with disassembly (rough decompilation -- no scope/flow control)
  398.                 --
  399.                 local varsDefined = {};
  400.                
  401.                 local function defineVar(index, name)
  402.                     table.insert(varsDefined, { ["name"] = name, ["stackIndex"] = index });
  403.                 end
  404.                
  405.                 local function isVarDefined(index)
  406.                     return true;
  407.                     --[[for _,v in pairs(varsDefined) do
  408.                         if v.stackIndex == index then
  409.                             return true
  410.                         end
  411.                     end
  412.                     return false;
  413.                     ]]
  414.                 end
  415.                
  416.                 local function addReference(refStart, refEnd)
  417.                     for _,v in pairs(refData) do
  418.                         if v.codeIndex == refEnd then
  419.                             table.insert(v.refs, refStart);
  420.                             return;
  421.                         end
  422.                     end
  423.                     table.insert(refData, { ["codeIndex"] = refEnd, ["refs"] = { refStart } });
  424.                 end
  425.                
  426.                 local nilValue = { ["type"] = "nil", ["value"] = "nil" }
  427.                
  428.                
  429.                 --[[ TO-DO: we could make getOpCode faster by using the opcode
  430.                 numbers directly, or just getting it by table index and the
  431.                 case-to-opcode multiplier (op * 227)
  432.                 but tbh this runs just fine
  433.                 ]]
  434.                 if opc == getOpCode("LOADNIL") then
  435.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = nil", A)
  436.                 elseif opc == getOpCode("BREAK") then
  437.                     output = output .. "break"
  438.                 elseif opc == getOpCode("LOADK") then
  439.                     local k = proto.kTable[Bx + 1] or nilValue;
  440.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = %s", A, (type(k.value) == "string") and ("\"" .. k.value .. "\"") or tostring(k.value))
  441.                 elseif opc == getOpCode("LOADKX") then
  442.                     local k = proto.kTable[aux + 1] or nilValue;
  443.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = %s", A, (type(k.value) == "string") and ("\"" .. k.value .. "\"") or tostring(k.value))
  444.                 elseif opc == getOpCode("LOADB") then
  445.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = %s", A, tostring(B == 1))
  446.                 elseif opc == getOpCode("LOADN") then
  447.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = %s", A, tostring(Bx))
  448.                 elseif opc == getOpCode("GETUPVAL") then
  449.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = upvalues[%i]", A, B)
  450.                 elseif opc == getOpCode("SETUPVAL") then
  451.                     output = output .. string.format("upvalues[%i] = var%i", B, A)
  452.                 elseif opc == getOpCode("MOVE") then
  453.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i", A, B)
  454.                 elseif opc == getOpCode("LEN") then
  455.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = #var%i", A, B)
  456.                 elseif opc == getOpCode("UNM") then
  457.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = -var%i", A, B)
  458.                 elseif opc == getOpCode("NOT") then
  459.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = not var%i", A, B)
  460.                 elseif opc == getOpCode("GETVARARGS") then
  461.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = ...", A)
  462.                 elseif opc == getOpCode("CONCAT") then
  463.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i .. var%i", A, B, C)
  464.                 elseif opc == getOpCode("AND") then
  465.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i and var%i", A, B, C)
  466.                 elseif opc == getOpCode("OR") then
  467.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i or var%i", A, B, C)
  468.                 elseif opc == getOpCode("ANDK") then
  469.                     local k = proto.kTable[C + 1] or nilValue;
  470.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i and %s", A, B, tostring(k.value))
  471.                 elseif opc == getOpCode("ORK") then
  472.                     local k = proto.kTable[C + 1] or nilValue;
  473.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i or %s", A, B, tostring(k.value))
  474.                 elseif opc == getOpCode("FASTCALL") then
  475.                     output = output .. string.format("FASTCALL[id=%i]()", A, B);
  476.                 elseif opc == getOpCode("FASTCALL2") then
  477.                     output = output .. string.format("FASTCALL[id=%i]()", A, B);
  478.                 elseif opc == getOpCode("FASTCALL2K") then
  479.                     output = output .. string.format("FASTCALL[id=%i]()", A, B);
  480.                 elseif opc == getOpCode("GETIMPORT") then
  481.                     local indexCount = bit32.band(bit32.rshift(aux, 30), 0x3FF) -- 0x40000000 --> 1, 0x80000000 --> 2
  482.                     local cacheIndex1 = bit32.band(bit32.rshift(aux, 20), 0x3FF)
  483.                     local cacheIndex2 = bit32.band(bit32.rshift(aux, 10), 0x3FF)
  484.                     local cacheIndex3 = bit32.band(bit32.rshift(aux, 0), 0x3FF)
  485.                    
  486.                     if indexCount == 1 then
  487.                         local k1 = proto.kTable[cacheIndex1 + 1];
  488.                        
  489.                         output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = %s", A, tostring(k1.value))
  490.                     elseif indexCount == 2 then
  491.                         local k1 = proto.kTable[cacheIndex1 + 1];
  492.                         local k2 = proto.kTable[cacheIndex2 + 1];
  493.                        
  494.                         output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = %s[\"%s\"]", A, k1.value, tostring(k2.value))
  495.                     elseif indexCount == 3 then
  496.                         local k1 = proto.kTable[cacheIndex1 + 1];
  497.                         local k2 = proto.kTable[cacheIndex2 + 1];
  498.                         local k3 = proto.kTable[cacheIndex3 + 1];
  499.                        
  500.                         output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = %s[\"%s\"][\"%s\"]", A, k1.value, tostring(k2.value), tostring(k3.value))
  501.                     else
  502.                         error("[GETIMPORT] Too many entries");
  503.                     end
  504.                 elseif opc == getOpCode("GETGLOBAL") then
  505.                     local k = proto.kTable[aux + 1] or nilValue;
  506.                     output = output .. string.format("var%i = %s", A, tostring(k.value))
  507.                 elseif opc == getOpCode("SETGLOBAL") then
  508.                     local k = proto.kTable[aux + 1] or nilValue;
  509.                     output = output .. string.format("%s = var%i", tostring(k.value), A)
  510.                 elseif opc == getOpCode("GETTABLE") then
  511.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i[var%i]", A, B, C)
  512.                 elseif opc == getOpCode("SETTABLE") then
  513.                     output = output .. string.format("var%i[var%i] = var%i", B, C, A)
  514.                 elseif opc == getOpCode("GETTABLEN") then
  515.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i[%i]", A, B, C - 1)
  516.                 elseif opc == getOpCode("SETTABLEN") then
  517.                     output = output .. string.format("var%i[%i] = var%i", B, C - 1, A)
  518.                 elseif opc == getOpCode("GETTABLEKS") then
  519.                     local k = proto.kTable[aux + 1] or nilValue;
  520.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i[%s]", A, B, (type(k.value) == "string") and ("\"" .. k.value .. "\"") or tostring(k.value))
  521.                 elseif opc == getOpCode("SETTABLEKS") then
  522.                     local k = proto.kTable[aux + 1] or nilValue;
  523.                     output = output .. string.format("var%i[%s] = var%i", B, (type(k.value) == "string") and ("\"" .. k.value .. "\"") or tostring(k.value), A)
  524.                 elseif opc == getOpCode("NAMECALL") then
  525.                     local k = proto.kTable[aux + 1] or nilValue;
  526.                     nameCall = string.format("var%i:%s", B, tostring(k.value))
  527.                     markedAux = true;
  528.                 elseif opc == getOpCode("NFORPREP") then
  529.                     output = output .. string.format("nforprep start - [escape at #%i] -- var%i = iterator", (codeIndex + sBx) + 1, A + 3);
  530.                 elseif opc == getOpCode("NFORLOOP") then
  531.                     output = output .. string.format("nforloop end - iterate + goto #%i", codeIndex + sBx);
  532.                 elseif opc == getOpCode("PAIRSPREP") then
  533.                     output = output .. string.format("pairsprep start - [escape at #%i] -- var%i = key, var%i = value", (codeIndex + sBx) + 1, A + 3, A + 4);
  534.                 elseif opc == getOpCode("PAIRSLOOP") then
  535.                     output = output .. string.format("pairsloop end - iterate + goto #%i", codeIndex + sBx);
  536.                 elseif opc == getOpCode("IPAIRSPREP") then
  537.                     output = output .. string.format("ipairsprep start [escape at #%i] -- var%i = key, var%i = value", (codeIndex + sBx) + 1, A + 3, A + 4);
  538.                 elseif opc == getOpCode("IPAIRSLOOP") then
  539.                     output = output .. string.format("ipairsloop end - iterate + goto #%i", codeIndex + sBx);
  540.                 elseif opc == getOpCode("TFORLOOP") then
  541.                     output = output .. string.format("gforloop - iterate + goto #%i", codeIndex + aux);
  542.                 elseif opc == getOpCode("JUMP") then
  543.                     addReference(codeIndex, codeIndex + sBx);
  544.                     output = output .. string.format("goto #%i", codeIndex + sBx);
  545.                 elseif opc == getOpCode("JUMPBACK") then
  546.                     addReference(codeIndex, codeIndex + sBx);
  547.                     output = output .. string.format("goto #%i", codeIndex + sBx);
  548.                 elseif opc == getOpCode("JUMPX") then
  549.                     addReference(codeIndex, codeIndex + sBx);
  550.                     output = output .. string.format("goto #%i", codeIndex + sAx);
  551.                 elseif opc == getOpCode("JUMPIFEQK") then
  552.                     local k = proto.kTable[aux + 1] or nilValue;
  553.                     addReference(codeIndex, codeIndex + sBx);
  554.                     output = output .. string.format("goto #%i if var%i == %s", codeIndex + sBx, A, (type(k.value) == "string") and ("\"" .. k.value .. "\"") or tostring(k.value));
  555.                 elseif opc == getOpCode("JUMPIFNOTEQK") then
  556.                     local k = proto.kTable[aux + 1] or nilValue;
  557.                     addReference(codeIndex, codeIndex + sBx);
  558.                     output = output .. string.format("goto #%i if var%i ~= %s", codeIndex + sBx, A, (type(k.value) == "string") and ("\"" .. k.value .. "\"") or tostring(k.value));
  559.                 elseif opc == getOpCode("JUMPIF") then
  560.                     addReference(codeIndex, codeIndex + sBx);
  561.                     output = output .. string.format("goto #%i if var%i", codeIndex + sBx, A);
  562.                 elseif opc == getOpCode("JUMPIFNOT") then
  563.                     addReference(codeIndex, codeIndex + sBx);
  564.                     output = output .. string.format("goto #%i if not var%i", codeIndex + sBx, A);
  565.                 elseif opc == getOpCode("JUMPIFEQ") then
  566.                     addReference(codeIndex, codeIndex + sBx);
  567.                     output = output .. string.format("goto #%i if var%i == var%i", codeIndex + sBx, A, aux);
  568.                 elseif opc == getOpCode("JUMPIFNOTEQ") then
  569.                     addReference(codeIndex, codeIndex + sBx);
  570.                     output = output .. string.format("goto #%i if var%i ~= var%i", codeIndex + sBx, A, aux);
  571.                 elseif opc == getOpCode("JUMPIFLE") then
  572.                     addReference(codeIndex, codeIndex + sBx);
  573.                     output = output .. string.format("goto #%i if var%i <= var%i", codeIndex + sBx, A, aux);
  574.                 elseif opc == getOpCode("JUMPIFNOTLE") then
  575.                     addReference(codeIndex, codeIndex + sBx);
  576.                     output = output .. string.format("goto #%i if var%i > var%i", codeIndex + sBx, A, aux);
  577.                 elseif opc == getOpCode("JUMPIFLT") then
  578.                     addReference(codeIndex, codeIndex + sBx);
  579.                     output = output .. string.format("goto #%i if var%i < var%i", codeIndex + sBx, A, aux);
  580.                 elseif opc == getOpCode("JUMPIFNOTLT") then
  581.                     addReference(codeIndex, codeIndex + sBx);
  582.                     output = output .. string.format("goto #%i if var%i >= var%i", codeIndex + sBx, A, aux);
  583.                 elseif opc == getOpCode("ADD") then
  584.                     output = output .. string.format("var%i = var%i + var%i", A, B, C);
  585.                 elseif opc == getOpCode("ADDK") then
  586.                     local k = proto.kTable[C + 1] or nilValue;
  587.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i + %s", A, B, tostring(k.value));
  588.                 elseif opc == getOpCode("SUB") then
  589.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i - var%i", A, B, C);
  590.                 elseif opc == getOpCode("SUBK") then
  591.                     local k = proto.kTable[C + 1] or nilValue;
  592.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i - %s", A, B, tostring(k.value));
  593.                 elseif opc == getOpCode("MUL") then
  594.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i * var%i", A, B, C);
  595.                 elseif opc == getOpCode("MULK") then
  596.                     local k = proto.kTable[C + 1] or nilValue;
  597.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i * %s", A, B, tostring(k.value));
  598.                 elseif opc == getOpCode("DIV") then
  599.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i / var%i", A, B, C);
  600.                 elseif opc == getOpCode("DIVK") then
  601.                     local k = proto.kTable[C + 1] or nilValue;
  602.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i / %s", A, B, tostring(k.value));
  603.                 elseif opc == getOpCode("MOD") then
  604.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i % var%i", A, B, C);
  605.                 elseif opc == getOpCode("MODK") then
  606.                     local k = proto.kTable[C + 1] or nilValue;
  607.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i % %s", A, B, tostring(k.value));
  608.                 elseif opc == getOpCode("POW") then
  609.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i ^ var%i", A, B, C);
  610.                 elseif opc == getOpCode("POWK") then
  611.                     local k = proto.kTable[C + 1] or nilValue;
  612.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = var%i ^ %s", A, B, tostring(k.value));
  613.                 elseif opc == getOpCode("CALL") then
  614.                     if C > 1 then
  615.                         for j = 1, C - 1 do
  616.                             output = output .. string.format("var%i", A + j - 1)
  617.                             if j < C - 1 then output = output .. ", " end
  618.                         end
  619.                         output = output .. " = "
  620.                     elseif C == 0 then
  621.                         output = output .. string.format("var%i, ...", A);
  622.                         output = output .. " = "
  623.                         --for j = 1, proto.maxStackSize do
  624.                         --    output = output .. string.format("var%i", A + j - 1)
  625.                         --    if j < proto.maxStackSize - 1 then output = output .. ", " end
  626.                         --end
  627.                     end
  628.                     if nameCall then
  629.                         output = output .. nameCall .. "(";
  630.                     else
  631.                         output = output .. string.format("var%i(", A)
  632.                     end
  633.                     if B > 1 then
  634.                         if nameCall then
  635.                             for j = 1, B - 2 do
  636.                                 output = output .. string.format("var%i", A + 1 + j) -- exclude self
  637.                             end
  638.                         else
  639.                             for j = 1, B - 1 do
  640.                                 output = output .. string.format("var%i", A + j)
  641.                                 if j < B - 1 then output = output .. ", " end
  642.                             end
  643.                         end
  644.                     elseif B == 0 then
  645.                         output = output .. string.format("var%i, ...", A + 1);
  646.                         --for j = 1, proto.maxStackSize do
  647.                         --    if nameCall then
  648.                         --        output = output .. string.format("var%i", A + 1 + j) -- exclude self
  649.                         --    else
  650.                         --        output = output .. string.format("var%i", A + j)
  651.                         --    end
  652.                         --    if j < proto.maxStackSize - 1 then output = output .. ", " end
  653.                         --end
  654.                     end
  655.                     nameCall = nil;
  656.                     output = output .. ")";
  657.                 elseif opc == getOpCode("NEWTABLE") then
  658.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = {}", A)
  659.                 elseif opc == getOpCode("DUPTABLE") then
  660.                     local t = proto.kTable[Bx + 1].value;
  661.                     output = output .. (isVarDefined(A) and "" or "local ") .. string.format("var%i = { ", A)
  662.                     for j = 1,t.size do
  663.                         local id = t.ids[j];
  664.                         local k = proto.kTable[id];
  665.                         output = output .. ((type(k.value) == "string") and ("\"" .. k.value .. "\"") or tostring(k.value))
  666.                         if j < t.size then
  667.                             output = output .. ", ";
  668.                         end
  669.                     end
  670.                     output = output .. "}";
  671.                 elseif opc == getOpCode("SETLIST") then
  672.                     local fieldSize = aux;
  673.                     output = output .. "\n"
  674.                     for j = 1, C do
  675.                         addTabSpace(depth);
  676.                         output = output .. string.format("var%i[%i] = var%i\n", A, j + fieldSize - 1, B + j - 1);
  677.                     end
  678.                 elseif opc == getOpCode("CAPTURE") then
  679.                     markedAux = true;
  680.                 elseif opc == getOpCode("NEWCLOSURE") then
  681.                     output = output .. "\n"
  682.                    
  683.                     local nCaptures = 0;
  684.                     for j = codeIndex + 1, proto.sizeCode do
  685.                         if luau.GET_OPCODE(proto.codeTable[j]) ~= getOpCode("CAPTURE") then
  686.                             break
  687.                         else
  688.                             local upvalueIndex = j - codeIndex - 1;
  689.                             local captureType = luau.GETARG_A(proto.codeTable[j]);
  690.                             local captureIndex = luau.GETARG_Bx(proto.codeTable[j]);
  691.                            
  692.                             nCaptures = nCaptures + 1;
  693.                            
  694.                             addTabSpace(depth);
  695.                             if captureType == 0 or captureType == 1 then
  696.                                 output = output .. string.format("-- V nested upvalues[%i] = var%i\n", upvalueIndex, captureIndex)
  697.                             elseif captureType == 2 then
  698.                                 output = output .. string.format("-- V nested upvalues[%i] = upvalues[%i]\n", upvalueIndex, captureIndex)
  699.                             else
  700.                                 error("[NEWCLOSURE] Invalid capture type");
  701.                             end
  702.                         end
  703.                     end
  704.                     codeIndex = codeIndex + nCaptures;
  705.                    
  706.                     addTabSpace(depth);
  707.                     local nextProto = proto.pTable[Bx + 1]
  708.                     if nextProto.source then
  709.                         output = output .. readProto(nextProto, depth)
  710.                         addTabSpace(depth);
  711.                         output = output .. string.format("var%i = ", A) .. nextProto.source
  712.                     else
  713.                         nextProto.source = nil;
  714.                         output = output .. string.format("var%i = ", A) .. readProto(nextProto, depth)
  715.                     end
  716.                 elseif opc == getOpCode("DUPCLOSURE") then
  717.                     output = output .. "\n"
  718.                    
  719.                     local nCaptures = 0;
  720.                     for j = codeIndex + 1, proto.sizeCode do
  721.                         if luau.GET_OPCODE(proto.codeTable[j]) ~= getOpCode("CAPTURE") then
  722.                             break
  723.                         else
  724.                             local upvalueIndex = j - codeIndex - 1;
  725.                             local captureType = luau.GETARG_A(proto.codeTable[j]);
  726.                             local captureIndex = luau.GETARG_Bx(proto.codeTable[j]);
  727.                            
  728.                             nCaptures = nCaptures + 1;
  729.                            
  730.                             addTabSpace(depth);
  731.                             if captureType == 0 or captureType == 1 then
  732.                                 output = output .. string.format("-- V nested upvalues[%i] = var%i\n", upvalueIndex, captureIndex)
  733.                             elseif captureType == 2 then
  734.                                 output = output .. string.format("-- V nested upvalues[%i] = upvalues[%i]\n", upvalueIndex, captureIndex)
  735.                             else
  736.                                 error("[DUPCLOSURE] Invalid capture type");
  737.                             end
  738.                         end
  739.                     end
  740.                     codeIndex = codeIndex + nCaptures;
  741.                    
  742.                     addTabSpace(depth);
  743.                     local nextProto = protoTable[proto.kTable[Bx + 1].value]
  744.                     if nextProto.source then
  745.                         output = output .. readProto(nextProto, depth)
  746.                         addTabSpace(depth);
  747.                         output = output .. string.format("var%i = ", A) .. nextProto.source
  748.                     else
  749.                         nextProto.source = nil;
  750.                         output = output .. string.format("var%i = ", A) .. readProto(nextProto, depth)
  751.                     end
  752.                 elseif opc == getOpCode("RETURN") then
  753.                     if B > 1 then
  754.                         output = output .. "return ";
  755.                         for j = 1, B - 1 do
  756.                             output = output .. string.format("var%i", A + j)
  757.                             if j < B - 1 then output = output .. ", " end
  758.                         end
  759.                     elseif B == 0 then
  760.                         output = output .. string.format("var%i, ...", A)
  761.                     end
  762.                 end
  763.                
  764.                 for _,v in pairs(refData) do
  765.                     if v.codeIndex == codeIndex then
  766.                         output = output .. " -- referenced by "
  767.                         for j = 1,#v.refs do
  768.                             output = output .. "#" .. v.refs[j]
  769.                             if j < #v.refs - 1 then
  770.                                 output = output .. ", "
  771.                             end
  772.                         end
  773.                     end
  774.                 end
  775.                
  776.                 output = output .. "\n"
  777.             end
  778.            
  779.             codeIndex = codeIndex + 1
  780.         end
  781.        
  782.         depth = depth - 1
  783.        
  784.         addTabSpace(depth)
  785.         output = output .. "end\n"
  786.         return output;
  787.     end
  788.    
  789.     local startDepth = 0;
  790.     output = output .. readProto(mainProto, startDepth)
  791.    
  792.     return output
  793. end
  794.  
  795. return disassemble
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement