├── src ├── lua51.lua └── disassembler │ ├── settings.lua │ ├── textstream.lua │ └── disassembler.lua └── README.md /src/lua51.lua: -------------------------------------------------------------------------------- 1 | local disassembler = require("disassembler\\disassembler") 2 | local settings = require("disassembler\\settings") 3 | 4 | disassembler:load_settings(settings) 5 | 6 | --[[disassembler:disassemble( 7 | "\27\76\117\97\81\0\1\4\4\4\8\0\67\0\0\0\105\102\32\50\32\126\61\32\49\32\116\104\101\110\32\10\32\32\112\114\105\110\116\40\39\104\105\39\41\10\101\108\115\101\10\32\32\112\114\105\110\116\40\39\121\111\117\32\97\114\101\32\115\111\32\99\111\111\108\39\41\10\101\110\100\10\0\0\0\0\0\0\0\0\0\0\0\2\2\10\0\0\0\87\64\64\128\22\192\0\128\5\128\0\0\65\192\0\0\28\64\0\1\22\128\0\128\5\128\0\0\65\0\1\0\28\64\0\1\30\0\128\0\5\0\0\0\3\0\0\0\0\0\0\0\64\3\0\0\0\0\0\0\240\63\4\6\0\0\0\112\114\105\110\116\0\4\3\0\0\0\104\105\0\4\16\0\0\0\121\111\117\32\97\114\101\32\115\111\32\99\111\111\108\0\0\0\0\0\10\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\4\0\0\0\4\0\0\0\4\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0" 8 | , 'dis.out')--]] 9 | 10 | disassembler:disassemble_file('C:\\Users\\morph\\Desktop\\Stuff\\LuaBinaries\\luac.out', 'dis.out') 11 | -------------------------------------------------------------------------------- /src/disassembler/settings.lua: -------------------------------------------------------------------------------- 1 | local op_signature = { 2 | sig0 = {"sBx"}, 3 | sig1 = {"A"}, 4 | sig2 = {"A", "B"}, 5 | sig3 = {"A", "Bx"}, 6 | sig4 = {"A", "C"}, 7 | sig5 = {"A", "sBx"}, 8 | sig6 = {"A", "B", "C"} 9 | } 10 | 11 | local instructions = { 12 | {opcode = 0, opname = "move", opargs = op_signature.sig1}, 13 | {opcode = 1, opname = "loadk", opargs = op_signature.sig3}, 14 | {opcode = 2, opname = "loadbool", opargs = op_signature.sig5}, 15 | {opcode = 3, opname = "loadnil", opargs = op_signature.sig1}, 16 | {opcode = 4, opname = "getupval", opargs = op_signature.sig1}, 17 | {opcode = 5, opname = "getglobal", opargs = op_signature.sig3}, 18 | {opcode = 6, opname = "gettable", opargs = op_signature.sig5}, 19 | {opcode = 7, opname = "setglobal", opargs = op_signature.sig2}, 20 | {opcode = 8, opname = "setupval", opargs = op_signature.sig1}, 21 | {opcode = 9, opname = "settable", opargs = op_signature.sig5}, 22 | {opcode = 10, opname = "newtable", opargs = op_signature.sig5}, 23 | {opcode = 11, opname = "self", opargs = op_signature.sig5}, 24 | {opcode = 12, opname = "add", opargs = op_signature.sig5}, 25 | {opcode = 13, opname = "sub", opargs = op_signature.sig5}, 26 | {opcode = 14, opname = "mul", opargs = op_signature.sig5}, 27 | {opcode = 15, opname = "div", opargs = op_signature.sig5}, 28 | {opcode = 16, opname = "mod", opargs = op_signature.sig5}, 29 | {opcode = 17, opname = "pow", opargs = op_signature.sig5}, 30 | {opcode = 18, opname = "unm", opargs = op_signature.sig1}, 31 | {opcode = 19, opname = "not", opargs = op_signature.sig1}, 32 | {opcode = 20, opname = "len", opargs = op_signature.sig1}, 33 | {opcode = 21, opname = "concat", opargs = op_signature.sig5}, 34 | {opcode = 22, opname = "jmp", opargs = op_signature.sig0}, 35 | {opcode = 23, opname = "eq", opargs = op_signature.sig6}, 36 | {opcode = 24, opname = "lt", opargs = op_signature.sig5}, 37 | {opcode = 25, opname = "le", opargs = op_signature.sig5}, 38 | {opcode = 26, opname = "test", opargs = op_signature.sig3}, 39 | {opcode = 27, opname = "testset", opargs = op_signature.sig5}, 40 | {opcode = 28, opname = "call", opargs = op_signature.sig6}, 41 | {opcode = 29, opname = "tailcall", opargs = op_signature.sig5}, 42 | {opcode = 30, opname = "return", opargs = op_signature.sig2}, 43 | {opcode = 31, opname = "forloop", opargs = op_signature.sig4}, 44 | {opcode = 32, opname = "forprep", opargs = op_signature.sig4}, 45 | {opcode = 33, opname = "tforloop", opargs = op_signature.sig3}, 46 | {opcode = 34, opname = "setlist", opargs = op_signature.sig5}, 47 | {opcode = 35, opname = "close", opargs = op_signature.sig0}, 48 | {opcode = 36, opname = "closure", opargs = op_signature.sig2}, 49 | {opcode = 37, opname = "vararg", opargs = op_signature.sig1} 50 | } 51 | 52 | local settings = { 53 | deserialization = { 54 | instruction = { 55 | opcode = {0, 5}, -- {0+1, 5+1} 56 | reg_a = {6, 13}, -- {6+1, 13+1} 57 | reg_b = {23, 31}, -- {23+1, 31+1} 58 | reg_c = {14, 22}, -- {14+1, 22+1} 59 | reg_bx = {14, 31}, -- {14+1, 31+1} 60 | sub_sbx = 131071 61 | }, 62 | constant = { 63 | constant_type = "table" 64 | } 65 | }, 66 | tables = { 67 | op_sigs = op_signature 68 | }, 69 | get_opname = function(opcode) 70 | for k,v in pairs(instructions) do 71 | if v.opcode == opcode then 72 | return v.opname 73 | end 74 | end 75 | return "unknown" 76 | end, 77 | get_instruction = function(opcode) 78 | for k,v in pairs(instructions) do 79 | if v.opcode == opcode then 80 | return v 81 | end 82 | end 83 | return nil 84 | end 85 | } 86 | 87 | return settings 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # reddisassembler v3 [rebirth] 2 | rewritten again, was bored and forget about the rewrite so this kinda just sat around on my desktop for 2-3 months. 3 | 4 | # changes from original 5 | deserializer: 6 | - deserialization was actually added (original depended on lbi.lua) 7 | - deserialization is mainly based from lbi.lua with some modifications. 8 | - deserialization now has settings for things like instruction bits. (lua obfuscators tend to use lbi and change the bytecode around) 9 | disassembler: 10 | - disassembler now has an instruction lookup table. 11 | - disassembler has register field sorting (so it displays only the used registers). 12 | - disassembler now supports jump lines -> 13 | - jump lines support going backward (this took me a while until i realized i was dumb. 14 | - added instruction addresses. 15 | - prototypes will be referenced by addresses as prototype names are almost never in the bytecode. 16 | textstream: 17 | - textstreams were finally implemented allowing me to do column text properly without any hastle. 18 | - this allows for organized output from the disassembler with equal spacing all the way through. 19 | - sadly the old jump line characters were not working kindly with the textstream so i had to compromise on the 20 | '+---', 'o-->' and '|' lines for showing jump lines. 21 | - added some new stuff to the textstream to allow support for backwards jumping also made a hacky method in the 22 | disassembler. 23 | 24 | # output 25 | feeding the disassembler the bytecode of: 26 | ```lua 27 | if 1 == 2 then 28 | print('hi') 29 | end 30 | print('noob') 31 | ``` 32 | will reward you with the following disassembled code formatted beautifully. 33 | ```asm 34 | func_00000044[NUPV 0, NARG 0] 35 | 36 | .constant 1 37 | .constant 2 38 | .constant print 39 | .constant hi 40 | .constant noob 41 | 0x000058 [23] eq 0, 0, 1 ; if ((1 == 2) ~= false) PC++ 42 | 0x00005C [22] +--- jmp 3 43 | 0x000060 [5] | getglobal 0, 2 ; pushes global: print onto the stack 44 | 0x000064 [1] | loadk 1, 3 ; pushes constant: hi onto the stack 45 | 0x000068 [28] | call 0, 2, 1 46 | 0x00006C [5] o--> getglobal 0, 2 ; pushes global: print onto the stack 47 | 0x000070 [1] loadk 1, 4 ; pushes constant: noob onto the stack 48 | 0x000074 [28] call 0, 2, 1 49 | 0x000078 [30] return 0, 1 50 | ``` 51 | 52 | # ways to use 53 | ## files 54 | ```lua 55 | local disassembler = require("disassembler\\disassembler") 56 | local settings = require("disassembler\\settings") 57 | 58 | disassembler:load_settings(settings) 59 | disassembler:disassemble_file('C:\\Users\\morph\\Desktop\\Stuff\\LuaBinaries\\luac.out', 'dis.out') 60 | ``` 61 | ## bytecode 62 | ```lua 63 | local disassembler = require("disassembler\\disassembler") 64 | local settings = require("disassembler\\settings") 65 | 66 | disassembler:load_settings(settings) 67 | disassembler:disassemble( 68 | "\27\76\117\97\81\0\1\4\4\4\8\0\67\0\0\0\105\102\32\50\32\126\61\32\49\32\116\104\101\110\32\10\32\32\112\114\105\110\116\40\39\104\105\39\41\10\101\108\115\101\10\32\32\112\114\105\110\116\40\39\121\111\117\32\97\114\101\32\115\111\32\99\111\111\108\39\41\10\101\110\100\10\0\0\0\0\0\0\0\0\0\0\0\2\2\10\0\0\0\87\64\64\128\22\192\0\128\5\128\0\0\65\192\0\0\28\64\0\1\22\128\0\128\5\128\0\0\65\0\1\0\28\64\0\1\30\0\128\0\5\0\0\0\3\0\0\0\0\0\0\0\64\3\0\0\0\0\0\0\240\63\4\6\0\0\0\112\114\105\110\116\0\4\3\0\0\0\104\105\0\4\16\0\0\0\121\111\117\32\97\114\101\32\115\111\32\99\111\111\108\0\0\0\0\0\10\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\4\0\0\0\4\0\0\0\4\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0" 69 | , 'dis.out') 70 | ``` 71 | -------------------------------------------------------------------------------- /src/disassembler/textstream.lua: -------------------------------------------------------------------------------- 1 | local textstream = {} 2 | 3 | function textstream:new() 4 | local stream = { 5 | columns = {}, 6 | rows = {}, 7 | columnMargin = 4 8 | } 9 | 10 | function stream:newRow() 11 | stream.rows[#stream.rows+1] = { 12 | data = {} 13 | } 14 | end 15 | 16 | function stream:getMaxColumnCount() 17 | local highestColumn = 0 18 | for _,__ in pairs(stream.rows) do 19 | for k,v in pairs(__.data) do 20 | if v.cIndex > highestColumn then 21 | highestColumn = v.cIndex 22 | end 23 | end 24 | end 25 | return highestColumn 26 | end 27 | 28 | function stream:columnExists(index, columnIndex) 29 | local rows = stream.rows 30 | 31 | local row = rows[index] 32 | if not row then 33 | rows[index] = { 34 | data = {} 35 | } 36 | row = rows[index] 37 | end 38 | local data = row.data 39 | 40 | for k,v in pairs(data) do 41 | if v.cIndex == columnIndex then return true end 42 | end 43 | return false 44 | end 45 | 46 | function stream:addToRow(index, columnIndex, text) 47 | local columns = stream.columns 48 | local rows = stream.rows 49 | local column = columns[columnIndex] 50 | 51 | local row = rows[index] 52 | if not row then 53 | rows[index] = { 54 | data = {} 55 | } 56 | row = rows[index] 57 | end 58 | local data = row.data 59 | 60 | if not column then 61 | columns[columnIndex] = { 62 | largestLength = #text, 63 | columnMargin = stream.columnMargin 64 | } 65 | else 66 | if column.largestLength < #text then 67 | column.largestLength = #text 68 | end 69 | end 70 | 71 | if not stream:columnExists(index, columnIndex) then 72 | data[#data+1] = {value = text, cIndex = columnIndex} 73 | end 74 | end 75 | 76 | function stream:changeColumn(row, columnIndex, text) 77 | local columns = stream.columns 78 | local rows = stream.rows 79 | local column = columns[columnIndex] 80 | 81 | if rows[row] == nil then 82 | stream:addToRow(row, columnIndex, '') 83 | end 84 | 85 | local row = rows[row] 86 | for _,data in pairs(row.data) do 87 | if data.cIndex == columnIndex then 88 | data.value = text 89 | if column.largestLength < #text then 90 | column.largestLength = #text 91 | end 92 | end 93 | end 94 | end 95 | 96 | function stream:getColumnText(index, columnIndex) 97 | local rows = stream.rows 98 | 99 | local row = rows[index] 100 | if not row then 101 | rows[index] = { 102 | data = {} 103 | } 104 | row = rows[index] 105 | end 106 | 107 | for _,data in pairs(row.data) do 108 | if data.cIndex == columnIndex then 109 | return data.value 110 | end 111 | end 112 | return '' 113 | end 114 | 115 | 116 | function stream:setColumnMargin(column, margin) 117 | if stream.columns[column] then 118 | stream.columns[column].columnMargin = margin 119 | end 120 | end 121 | 122 | function stream:toString() 123 | local text = "" 124 | 125 | for x = 1, #stream.rows do 126 | for y = 1, stream:getMaxColumnCount() do 127 | local data = nil 128 | if stream.rows[x] ~= nil then 129 | for k,v in pairs(stream.rows[x].data) do 130 | if v.cIndex == y then 131 | data = v 132 | end 133 | end 134 | end 135 | 136 | local column = stream.columns[y] 137 | if data then 138 | text = text .. data.value 139 | if #data.value < column.largestLength then 140 | local extraSpaces = column.largestLength - #data.value 141 | for i = 1, extraSpaces do 142 | text = text .. ' ' 143 | end 144 | end 145 | for i = 1, column.columnMargin do 146 | text = text .. ' ' 147 | end 148 | else 149 | for i = 1, column.largestLength do 150 | text = text .. ' ' 151 | end 152 | for i = 1, column.columnMargin do 153 | text = text .. ' ' 154 | end 155 | end 156 | end 157 | text = text .. '\n' 158 | end 159 | 160 | return text 161 | end 162 | 163 | return stream 164 | end 165 | 166 | return textstream 167 | -------------------------------------------------------------------------------- /src/disassembler/disassembler.lua: -------------------------------------------------------------------------------- 1 | local disassembler = { 2 | loaded_settings = {}, 3 | disassembled = "" 4 | } 5 | 6 | -- REQUIRES 7 | local TextStream = require("disassembler\\textstream") 8 | 9 | function disassembler:load_settings(settings) 10 | disassembler.loaded_settings = settings 11 | end 12 | 13 | function disassembler:reverse_indexes(t) 14 | local a, b = 1, #t 15 | while a < b do 16 | t[a], t[b] = t[b], t[a] 17 | a = b + 1 18 | a = b - 1 19 | end 20 | end 21 | 22 | function disassembler:get_bits(input, n, n2) 23 | if n2 then 24 | local total = 0 25 | local digitn = 0 26 | for i = n, n2 do 27 | total = total + 2^digitn*disassembler:get_bits(input, i) 28 | digitn = digitn + 1 29 | end 30 | return total 31 | else 32 | local pn = 2^(n-1) 33 | return (input % (pn + pn) >= pn) and 1 or 0 34 | end 35 | end 36 | 37 | function disassembler:disassemble_file(file, fileloc) 38 | local file = assert(io.open(file, 'rb')) 39 | local str = file:read("*a") 40 | disassembler:disassemble(str, fileloc) 41 | end 42 | 43 | function disassembler:disassemble(bytecode, fileloc) 44 | local index = 1 45 | local big_endian = false 46 | local int_size; 47 | local size_t; 48 | 49 | -- Actual binary decoding functions. Dependant on the bytecode. 50 | local get_int, get_size_t; 51 | 52 | -- Binary decoding helper functions 53 | local get_int8, get_int32, get_int64, get_float64, get_string; 54 | do 55 | function get_int8() 56 | local a = bytecode:byte(index, index); 57 | index = index + 1 58 | return a 59 | end 60 | function get_int32() 61 | local a, b, c, d = bytecode:byte(index, index + 3); 62 | index = index + 4; 63 | return d * 16777216 + c * 65536 + b * 256 + a 64 | end 65 | function get_int64() 66 | local a = get_int32(); 67 | local b = get_int32(); 68 | return b * 4294967296 + a; 69 | end 70 | 71 | function get_float64() 72 | local a = get_int32() 73 | local b = get_int32() 74 | return (-2 * disassembler:get_bits(b, 32) + 1) * (2 ^ (disassembler:get_bits(b, 21, 31) - 1023)) * 75 | ((disassembler:get_bits(b, 1, 20) * (2 ^ 32) + a) / (2 ^ 52)+1) 76 | end 77 | 78 | function get_string(len) 79 | local str; 80 | if len then 81 | str = bytecode:sub(index, index + len - 1); 82 | index = index + len; 83 | else 84 | len = get_size_t(); 85 | if len == 0 then return; end 86 | str = bytecode:sub(index, index + len - 1); 87 | index = index + len; 88 | end 89 | return str; 90 | end 91 | end 92 | 93 | local function decode_chunk() 94 | local chunk_info = TextStream:new() 95 | local constant_info = TextStream:new() 96 | local instruction_info = TextStream:new() 97 | constant_info.columnMargin = 1 98 | 99 | local chunk; 100 | local instructions = {}; 101 | local constants = {}; 102 | local prototypes = {}; 103 | local debug = { 104 | lines = {}; 105 | }; 106 | 107 | chunk = { 108 | instructions = instructions; 109 | constants = constants; 110 | prototypes = prototypes; 111 | debug = debug; 112 | }; 113 | 114 | -- big brain hack 115 | local function applyJumpLines(stream, jumpTo, jumpFrom) 116 | local step = 1 117 | if jumpTo < jumpFrom then step = -1 end 118 | for i = jumpFrom, jumpTo, step do 119 | local jumpText = "" 120 | if i == jumpFrom then 121 | jumpText = '+---' 122 | elseif i == jumpTo then 123 | jumpText = 'o-->' 124 | else 125 | if step == -1 then 126 | if i < jumpFrom and i > jumpTo then jumpText = '|' end 127 | else 128 | if i > jumpFrom and i < jumpTo then jumpText = '|' end 129 | end 130 | end 131 | stream:changeColumn(i, 3, jumpText) 132 | end 133 | end 134 | 135 | local num; 136 | 137 | get_string();-- Function name 138 | local func_offset = index 139 | chunk.first_line = get_int(); -- First line 140 | chunk.last_line = get_int(); -- Last line 141 | 142 | if chunk.name then chunk.name = chunk.name:sub(1, -2); end 143 | 144 | chunk.upvalues = get_int8(); 145 | chunk.arguments = get_int8(); 146 | chunk.varg = get_int8(); 147 | chunk.stack = get_int8(); 148 | 149 | chunk_info:addToRow(1, 1, "func_" .. string.format("%4.8X", func_offset) .. '[NUPV ' .. chunk.upvalues .. ', ' .. 'NARG ' .. chunk.arguments .. ']') 150 | -- TODO: realign lists to 1 151 | -- Decode instructions 152 | do 153 | num = get_int(); 154 | for i = 1, num do 155 | local instruction = { 156 | -- opcode = opcode number; 157 | -- type = [ABC, ABx, AsBx] 158 | -- A, B, C, Bx, or sBx depending on type 159 | }; 160 | 161 | local data = get_int32(); 162 | local opcode = disassembler:get_bits(data, 163 | disassembler.loaded_settings.deserialization.instruction.opcode[1]+1, 164 | disassembler.loaded_settings.deserialization.instruction.opcode[2]+1); 165 | 166 | instruction.address = index 167 | instruction.opcode = opcode; 168 | 169 | instruction.A = disassembler:get_bits(data, 170 | disassembler.loaded_settings.deserialization.instruction.reg_a[1]+1, 171 | disassembler.loaded_settings.deserialization.instruction.reg_a[1]+1); 172 | instruction.B = disassembler:get_bits(data, 173 | disassembler.loaded_settings.deserialization.instruction.reg_b[1]+1, 174 | disassembler.loaded_settings.deserialization.instruction.reg_b[2]+1); 175 | instruction.C = disassembler:get_bits(data, 176 | disassembler.loaded_settings.deserialization.instruction.reg_c[1]+1, 177 | disassembler.loaded_settings.deserialization.instruction.reg_c[2]+1); 178 | instruction.Bx = disassembler:get_bits(data, 179 | disassembler.loaded_settings.deserialization.instruction.reg_bx[1]+1, 180 | disassembler.loaded_settings.deserialization.instruction.reg_bx[2]+1); 181 | instruction.sBx = disassembler:get_bits(data, 182 | disassembler.loaded_settings.deserialization.instruction.reg_bx[1]+1, 183 | disassembler.loaded_settings.deserialization.instruction.reg_bx[2]+1) - disassembler.loaded_settings.deserialization.instruction.sub_sbx; 184 | 185 | instructions[i] = instruction; 186 | end 187 | end 188 | 189 | -- Decode constants 190 | do 191 | num = get_int(); 192 | for i = 1, num do 193 | local constant = { 194 | -- type = constant type; 195 | -- data = constant data; 196 | }; 197 | local type = get_int8(); 198 | constant.type = type; 199 | 200 | if type == 1 then 201 | constant.data = (get_int8() ~= 0); 202 | elseif type == 3 then 203 | constant.data = get_float64(); 204 | elseif type == 4 then 205 | constant.data = get_string():sub(1, -2); 206 | end 207 | 208 | constant_info:addToRow(i+1, 1, '.constant') 209 | constant_info:addToRow(i+1, 2, tostring(constant.data)) 210 | 211 | constants[i-1] = constant; 212 | end 213 | end 214 | 215 | -- Decode Prototypes 216 | do 217 | num = get_int(); 218 | for i = 1, num do 219 | prototypes[i-1] = decode_chunk(); 220 | end 221 | end 222 | 223 | -- Decode debug info 224 | -- Not all of which is used yet. 225 | do 226 | -- line numbers 227 | local data = debug.lines 228 | num = get_int(); 229 | for i = 1, num do 230 | data[i] = get_int32(); 231 | end 232 | 233 | -- locals 234 | num = get_int(); 235 | for i = 1, num do 236 | get_string():sub(1, -2); -- local name 237 | get_int32(); -- local start PC 238 | get_int32(); -- local end PC 239 | end 240 | 241 | -- upvalues 242 | num = get_int(); 243 | for i = 1, num do 244 | get_string(); -- upvalue name 245 | end 246 | end 247 | 248 | disassembler.disassembled = disassembler.disassembled .. chunk_info:toString() 249 | disassembler.disassembled = disassembler.disassembled .. constant_info:toString() 250 | for i,instruction in pairs(chunk.instructions) do 251 | local opname = disassembler.loaded_settings.get_opname(instruction.opcode) 252 | local info = disassembler.loaded_settings.get_instruction(instruction.opcode) 253 | instruction_info:addToRow(i, 1, string.format("0x%4.6X", instruction.address)) 254 | instruction_info:addToRow(i, 2, "[" .. instruction.opcode .. "]") 255 | instruction_info:addToRow(i, 3, '') 256 | instruction_info:addToRow(i, 4, opname) 257 | 258 | if opname == 'jmp' then 259 | applyJumpLines(instruction_info, i + instruction.sBx + 1, i) 260 | elseif opname == 'loadk' then 261 | instruction_info:addToRow(i, 6, '; pushes constant: ' .. chunk.constants[instruction.Bx].data .. ' onto the stack') 262 | elseif opname == 'eq' then 263 | local A, B, C = instruction.A, instruction.B, instruction.C 264 | A = A ~= 0 265 | B = B > 255 and chunk.constants[B-256].data or 'stack[' .. B .. ']' 266 | C = C > 255 and chunk.constants[C-256].data or 'stack[' .. C .. ']' 267 | instruction_info:addToRow(i, 6, '; if ((' .. B .. ' == ' .. C ..') ~= ' .. tostring(A) .. ') PC++') 268 | elseif opname == 'lt' then 269 | local A, B, C = instruction.A, instruction.B, instruction.C 270 | A = A ~= 0 271 | B = B > 255 and chunk.constants[B-256].data or 'stack[' .. B .. ']' 272 | C = C > 255 and chunk.constants[C-256].data or 'stack[' .. C .. ']' 273 | instruction_info:addToRow(i, 6, '; if ((' .. B .. ' < ' .. C ..') ~= ' .. tostring(A) .. ') PC++') 274 | elseif opname == 'le' then 275 | local A, B, C = instruction.A, instruction.B, instruction.C 276 | A = A ~= 0 277 | B = B > 255 and chunk.constants[B-256].data or 'stack[' .. B .. ']' 278 | C = C > 255 and chunk.constants[C-256].data or 'stack[' .. C .. ']' 279 | instruction_info:addToRow(i, 6, '; if ((' .. B .. ' <= ' .. C ..') ~= ' .. tostring(A) .. ') PC++') 280 | elseif opname == 'getglobal' then 281 | instruction_info:addToRow(i, 6, '; pushes global: ' .. chunk.constants[instruction.Bx].data .. ' onto the stack') 282 | end 283 | 284 | local args = "" 285 | local opsig = info.opargs 286 | for i = 1, #opsig do 287 | args = args .. (function() 288 | if instruction[opsig[i]] > 255 then 289 | return instruction[opsig[i]] - 256 290 | end 291 | return instruction[opsig[i]] 292 | end)() 293 | if i ~= #opsig then 294 | args = args .. ", " 295 | end 296 | end 297 | 298 | instruction_info:addToRow(i, 5, args) 299 | end 300 | 301 | disassembler.disassembled = disassembler.disassembled .. instruction_info:toString() 302 | 303 | return chunk; 304 | end 305 | 306 | -- get bytecode header 307 | do 308 | assert(get_string(4) == "\27Lua", "Lua bytecode expected."); 309 | assert(get_int8() == 0x51, "Only Lua 5.1 is supported."); 310 | get_int8(); -- Oficial bytecode 311 | big_endian = (get_int8() == 0); 312 | int_size = get_int8(); 313 | size_t = get_int8(); 314 | 315 | if int_size == 4 then 316 | get_int = get_int32; 317 | elseif int_size == 8 then 318 | get_int = get_int64; 319 | else 320 | error("Unsupported bytecode target platform"); 321 | end 322 | 323 | if size_t == 4 then 324 | get_size_t = get_int32; 325 | elseif size_t == 8 then 326 | get_size_t = get_int64; 327 | else 328 | error("Unsupported bytecode target platform"); 329 | end 330 | 331 | assert(get_string(3) == "\4\8\0", 332 | "Unsupported bytecode target platform"); 333 | end 334 | 335 | local mainchunk = decode_chunk() 336 | print("[reddisassembler] disassembling main chunk...") 337 | print("[reddisassembler] main chunk:") 338 | print("\tchunk->sizep: " .. #mainchunk.prototypes) 339 | print("\tchunk->sizek: " .. #mainchunk.constants) 340 | print("\tchunk->sizecode: " .. #mainchunk.instructions) 341 | 342 | print('\n') 343 | local file = io.open(fileloc, 'w') 344 | io.output(file) 345 | io.write('') 346 | io.close(file) 347 | file = io.open(fileloc, 'w') 348 | io.output(file) 349 | io.write(disassembler.disassembled) 350 | io.close(file) 351 | end 352 | 353 | return disassembler 354 | --------------------------------------------------------------------------------