Bug fixes and working on decompiling
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 1 May 2020 17:18:42 +0000 (12:18 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 1 May 2020 17:18:42 +0000 (12:18 -0500)
README [new file with mode: 0644]
app.lua
conf.lua
src/ui/components.lua
src/ui/text.lua
src/wasm/decompile.lua
src/wasm/text.lua

diff --git a/README b/README
new file mode 100644 (file)
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 . <path/to/wasm/binary>
+
+       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 8b34609978ae9137e88cd55e1f5477e71b8ba5f5..2da02f4cc4936ab6d798fdbdc279483f2577b4cd 100644 (file)
--- 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
                        })
 
index 1848af32ed24bb0481817592c4c9db27c8fd66ba..be7239be9643e850e6f1ab563590a59ee2cefa12 100644 (file)
--- 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;
 }
index 7471cf74ab629b7b5e9a1676f1ad428e314c1ec2..7215ef397e2fa7395823cb69be38f69150e21d0f 100644 (file)
@@ -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
 
index 78d6f965b3dd04f1e2cefda2e47d341ff5513bb1..ec859f40a3327a61a5031dbe45b396adca05f91e 100644 (file)
@@ -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 {
index 2ebf6699486dc2ada17923be49e10f5b560f918a..daa137a44e79f3b107e425cd3cf453b21316b25c 100644 (file)
@@ -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)
index ddd1144710531d9cbc25b24491db7ded940c3e11..9b89faa555d78124dc4503ccda6413a70045f069 100644 (file)
@@ -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