--- /dev/null
+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.
import {
+ TextBuilder = "src.ui.text:TextBuilder";
Stack = "src.utils:Stack";
pprint = "lualib.pprint";
}
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
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)
}
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)