From: Brendan Hansen Date: Fri, 1 May 2020 17:18:42 +0000 (-0500) Subject: Bug fixes and working on decompiling X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=c04dd51cf816d0f48b70632030124949c9743503;p=wasm-analyzer.git Bug fixes and working on decompiling --- diff --git a/README b/README new file mode 100644 index 0000000..513db4a --- /dev/null +++ b/README @@ -0,0 +1,87 @@ +Class Project for CSC-720: Theory of Computation + + WebAssembly 32-bit Disassembler and Analyzer + Brendan Hansen, Collin Rumpca, Jarod Keene + +DESCRIPTION: + This project was made to assist in the analyzing of WebAssembly 32-bit (WASM) + binaries. WASM is a new technology for executing performance driven applications, + primarily targeted towards the web. WASM uses a simple stack machine for performing + computations. For example, to add 3 and 4 together in WebAssembly, the following + is done: + + i32.const 3 + i32.const 4 + i32.add + + This computation pushes the signed 32-bit integer 3 onto the stack. Then pushes 4 + onto the stack. Then executes i32.add which pops off two values from the stack and + adds them together, pushing the result back onto the stack. This model of computation + is very simple and is hardware-independent, since it does not reply on specific + registers or instructions. If you are curious about more of the details of WASM, + feel free to read about it at https://webassembly.org + + Since WASM is becoming a big part of the web, security researchers will want + to be able to analyze exact what a WASM binary is doing on the web page. While + WASM was designed to be completely sandboxed and safe, there is no harm in + deeply analyzing a binary to ensure no security vulnerabilities exist. + + This program create a visual representation of the WASM binary, allowing you + to look at the functions, and the call graphs between functions to analyze their + behaviour. + +DEPENDENCIES: + This project uses the Love2D game engine to provide the visuals. Love2D is available + on almost all binary repositories on Linux or can be downloaded from love2d.org. + + Because of the limitations of Lua, the language used to create applications in + Love2D, some aspects of the program needed to be written in C. While we were unable + to get the C code to compile on Windows, feel free to give it a try. + + For the project to run in its current state, you need: + + * Love2D (>= 11.3), available in Ubuntu repos. + $ sudo apt-get install love + + * Lua Source Headers (== 5.1) + $ sudo apt-get install liblua5.1-dev + + * GCC (>= 9.0.0), should already be installed. + * Make (>= 4.3), should also already be installed. + +COMPILING: + Simply run: + $ make + + This should compile the parse_helper.so file in the clib/ directory. + +RUNNING: + To run the application when all dependencies are installed and all files are made, + ensure you are in the project directory and simply run: + $ love . + + To run on the provided binary, run: + $ love . ./data/main.wasm + + This should be all you need to get the program running. + +USING THE SOFTWARE: + There will be a list of all the functions used in the program. + Simply select one with the mouse and drag it out. + You should be able to see the code for that function. + + Resizing: To resize a function, hold right click anywhere on it and drag the mouse around. + + Scrolling: To scroll in a function, click on the function and either: + * Scroll with the mouse wheel + * Or, scroll with the arrow keys + + Zooming: To zoom in/out, click on the background so no function is selected and either: + * Scroll with the mouse wheel + * Or, use "q" to zoom in and "a" to zoom out + + Panning: To pan around the functions, click on the background and either: + * Click and drag the mouse, + * Or, use the arrow keys + + Quitting: Either use the "X" on the window, or press the "Escape" key. diff --git a/app.lua b/app.lua index 8b34609..2da02f4 100644 --- a/app.lua +++ b/app.lua @@ -65,7 +65,7 @@ function open_file(path) ui.insert_child( globals.ui.function_blocks_container, with(ui.make_element("function_block", funcs[i], globals.wasm_module)) { - rect = Rectangle(math.random() * 5000, math.random() * 5000, 400, 400); + rect = Rectangle(0, 24 * i, 400, 400); layer = #funcs - i }) diff --git a/conf.lua b/conf.lua index 1848af3..be7239b 100644 --- a/conf.lua +++ b/conf.lua @@ -50,5 +50,5 @@ end return { COLOR_SCHEMES = COLOR_SCHEMES; - COLOR_SCHEME = COLOR_SCHEMES.LIGHT; + COLOR_SCHEME = COLOR_SCHEMES.DARK; } diff --git a/src/ui/components.lua b/src/ui/components.lua index 7471cf7..7215ef3 100644 --- a/src/ui/components.lua +++ b/src/ui/components.lua @@ -427,6 +427,8 @@ end local function_ref_lines = {} function function_ref_lines:init() self.layer = -1 + self.visible = true + self.btn_down = false local func_blocks = globals.ui.function_blocks_container.children local funcs = {} @@ -450,16 +452,25 @@ function function_ref_lines:init() end end +function function_ref_lines:update(dt) + local btn_down = love.keyboard.isDown "l" + if not btn_down and self.btn_down then + self.visible = not self.visible + end + + self.btn_down = btn_down +end + function function_ref_lines:predraw() + if not self.visible then return end + for _, arr in ipairs(self.arrows) do - if arr.from and arr.to then - local from = arr.from.rect - local to = arr.to.rect - love.graphics.setColor(arr.color) - draw_arrow( - from.x + from.w / 2, from.y + from.h / 2, - to.x + to.w / 2, to.y + to.h / 2) - end + local from = arr.from.rect + local to = arr.to.rect + love.graphics.setColor(arr.color) + draw_arrow( + from.x + from.w / 2, from.y + from.h / 2, + to.x + to.w / 2, to.y + to.h / 2) end end diff --git a/src/ui/text.lua b/src/ui/text.lua index 78d6f96..ec859f4 100644 --- a/src/ui/text.lua +++ b/src/ui/text.lua @@ -94,10 +94,14 @@ class "TextBuilder" { end; append = function(self, other_builder) - for _, line in ipairs(other_builder.lines) do + self:append_lines(other_builder.lines) + end; + + append_lines = function(self, lines) + for _, line in ipairs(lines) do table.insert(self.lines, line) end - end; + end } return module { diff --git a/src/wasm/decompile.lua b/src/wasm/decompile.lua index 2ebf669..daa137a 100644 --- a/src/wasm/decompile.lua +++ b/src/wasm/decompile.lua @@ -1,4 +1,5 @@ import { + TextBuilder = "src.ui.text:TextBuilder"; Stack = "src.utils:Stack"; pprint = "lualib.pprint"; } @@ -15,6 +16,7 @@ i32.add would translate to i32.add (i32.mul (i32.const 1) (i32.const 3)) (i32.const 8). +(1 * 3) + 8 Every instruction has some number of inputs and outputs to the stack. inputs outputs @@ -32,99 +34,136 @@ This should be quick, just need to look up all the instructions local build_instr_list function binop(op) - return function(stack, instr) + return function(tb, stack, instr) local right = stack:pop() local left = stack:pop() - stack:push("(" .. left .. " " .. op .. " " .. right .. ")") + + local newtab = { { "(" } } + table.append(newtab, left) + table.insert(newtab, { op }) + table.append(newtab, right) + table.insert(newtab, { ")" }) + stack:push(newtab) end end function cast(to) - return function(stack, instr) + return function(tb, stack, instr) local thing = stack:pop() - stack:push("<" .. to .. ">" .. thing) + + local newtab = { { "<" .. to .. ">" } } + table.append(newtab, thing) + stack:push(newtab) end end function func_call(name, nparams, nreturns) if nparams == nil then nparams = 1 end if nreturns == nil then nreturns = 1 end - return function(stack, instr) - local str = name .. "(" - local args = "" - for i=1,nparams do + return function(tb, stack, instr) + local newtab = { { name .. " (" } } + + for i=1, nparams do local val = stack:pop() - args = ", " .. val .. args + if i ~= 1 then tabel.insert(newtab, { ", " }) end + table.append(newtab, val) end - str = str .. args:sub(3) .. ")" + if nreturns == 0 then - return str + tb:append_lines(newtab) else - stack:push(str) + stack:push(newtab) end end end function const(t) - return function(stack, instr) - stack:push(tostring(instr.x) .. t) + return function(tb, stack, instr) + stack:push { + { tostring(instr.x) }; + { t } + } end end -function expr_return(stack, instr) +function expr_return(tb, stack, instr) if stack:size() >= 1 then - return "return " .. stack:pop() + tb:text("return ") + tb:append_lines(stack:pop()) else - return "return" + tb:text("return ") end end -function expr_br(stack, instr) - return "br <" .. instr.x.block.label .. ">" +function expr_br(tb, stack, instr) + tb:text "br <" + tb:text (instr.x.block.label) + tb:text ">" end -function expr_br_if(stack, instr) - return "br_if <" .. instr.x.block.label .. "> (" .. stack:pop() .. ")" +function expr_br_if(tb, stack, instr) + tb:text "br_if <" + tb:text (instr.x.block.label) + tb:text "> (" + tb:append_lines(stack:pop()) + tb:text ")" end -function expr_br_table(stack, instr) - local lines = {} - table.insert(lines, { "br_table (" .. stack:pop() .. ") {" }) - local mappings = {} - for i, lab in ipairs(instr.labels) do - table.insert(mappings, { tostring(i - 1) .. " => <" .. lab.block.label .. ">" }) - end - table.insert(mappings, { "default => <" .. instr.labeln.block.label .. ">" }) - for _, line in ipairs(indent(mappings, " ")) do - table.insert(lines, line) - end - table.insert(lines, { "}" }) - return lines +function expr_br_table(tb, stack, instr) + -- local lines = {} + -- table.insert(lines, { "br_table (" .. stack:pop() .. ") {" }) + -- local mappings = {} + -- for i, lab in ipairs(instr.labels) do + -- table.insert(mappings, { tostring(i - 1) .. " => <" .. lab.block.label .. ">" }) + -- end + -- table.insert(mappings, { "default => <" .. instr.labeln.block.label .. ">" }) + -- for _, line in ipairs(indent(mappings, " ")) do + -- table.insert(lines, line) + -- end + -- table.insert(lines, { "}" }) + -- return lines + tb:text "BRANCH TABLE" end -function expr_drop(stack, instr) - return "drop (" .. stack:pop() .. ")" +function expr_drop(tb, stack, instr) + tb:text "drop (" + tb:append_lines (stack:pop()) + tb:text ")" end -function expr_select(stack, instr) - stack:push("select (" .. stack:pop() .. [[) { - 0: ]] .. stack:pop() .. [[ - 1: ]] .. stack:pop() .. [[ -}]]) +function expr_select(tb, stack, instr) + local ntb = TextBuilder() + ntb:text "select (" + ntb:append_lines(stack:pop()) + ntb:text [[) { + 0: ]] + ntb:append_lines(stack:pop()) + ntb:text [[ + 1: ]] + ntb:append_lines(stack:pop()) + ntb:text [[ +}]] + stack:push(ntb.lines) end -function expr_local_get(stack, instr, func) - stack:push(func.locals[instr.x + 1].name) +function expr_local_get(tb, stack, instr, func) + stack:push { + { func.locals[instr.x + 1].name } + } end -function expr_local_set(stack, instr, func) +function expr_local_set(tb, stack, instr, func) local varname = func.locals[instr.x + 1].name - return varname .. " <- " .. stack:pop() + tb:text(varname .. " <- ") + tb:append_lines(stack:pop()) end -function expr_local_tee(stack, instr, func) +function expr_local_tee(tb, stack, instr, func) local varname = func.locals[instr.x + 1].name - stack:push(varname .. " <- " .. stack:pop()) + local ntb = TextBuilder() + ntb:text(varname .. " <- ") + ntb:append_lines(stack:pop()) + stack:push(ntb.lines) end function expr_global_get(stack, instr) @@ -418,25 +457,25 @@ local expr_generators = { } function build_instr_list(instrs, func, mod) - local lines = {} + local tb = TextBuilder() local stack = Stack() for _, instr in ipairs(instrs) do - local res = expr_generators[instr[1]](stack, instr, func, mod) - if type(res) == "string" then - table.insert(lines, { res }) - elseif type(res) == "table" then - for _, line in ipairs(res) do table.insert(lines, line) end - end + expr_generators[instr[1]](tb, stack, instr, func, mod) + -- if type(res) == "string" then + -- table.insert(lines, { res }) + -- elseif type(res) == "table" then + -- for _, line in ipairs(res) do table.insert(lines, line) end + -- end end - if stack:size() ~= 0 then - while not stack:empty() do - table.insert(lines, stack:pop()) - end - end + -- if stack:size() ~= 0 then + -- while not stack:empty() do + -- table.insert(lines, stack:pop()) + -- end + -- end - return lines + return tb end function decompile_func(wasm_func, wasm_mod) diff --git a/src/wasm/text.lua b/src/wasm/text.lua index ddd1144..9b89faa 100644 --- a/src/wasm/text.lua +++ b/src/wasm/text.lua @@ -130,11 +130,13 @@ end function build_body(ctx) + local tb = TextBuilder() if ctx.func.imported then - return { { "Imported function", fg = ctx.colors.code } } + tb:text("Imported Function", ctx.colors.code) + tb:newline(1) + return tb, { lines = {} } end - local tb = TextBuilder() tb:text(" Locals:", ctx.colors.keyword) tb:newline(1) for _, loc in ipairs(ctx.func.locals) do @@ -164,13 +166,7 @@ function generate_text_format(func, mod, colors, line_start) colors = colors; } local heading = build_header(ctx) - local body_preface, body - if func.imported then - body_preface = {} - body = { { "Imported function" } } - else - body_preface, body = build_body(ctx) - end + local body_preface, body = build_body(ctx) return { heading }, body_preface.lines, body.lines end