class Metasm::WasmFile
WebAssembly leb integer encoding taken from dex.rb
Constants
- EXTERNAL_KIND
- MAGIC
- OPCODE_IMM_COUNT
begin WTF
- SECTION_NAME
- TYPE
Attributes
code_info[RW]
data[RW]
element[RW]
endianness[RW]
export[RW]
function_body[RW]
function_signature[RW]
global[RW]
header[RW]
import[RW]
memory[RW]
modules[RW]
start_function_index[RW]
table[RW]
type[RW]
Public Class Methods
new(endianness=:little)
click to toggle source
Calls superclass method
Metasm::ExeFormat::new
# File metasm/exe_format/wasm.rb, line 95 def initialize(endianness=:little) @endianness = endianness @encoded = EncodedData.new super() end
Public Instance Methods
cpu_from_headers()
click to toggle source
# File metasm/exe_format/wasm.rb, line 366 def cpu_from_headers WebAsm.new(self) end
decode()
click to toggle source
# File metasm/exe_format/wasm.rb, line 182 def decode decode_header while @encoded.ptr < @encoded.length @modules << Module.decode(self) end @modules.each { |m| @encoded.add_export(new_label("module_#{m.id}"), m.raw_offset) f = "decode_module_#{m.id.to_s.downcase}" send(f, m) if respond_to?(f) } func_imports = @import.to_a.find_all { |i| i[:kind] == 'function' } export.to_a.each { |e| next if e[:kind] != 'function' # TODO resolve init_offset for globals etc? idx = e[:index] - func_imports.length next if not fb = function_body.to_a[idx] @encoded.add_export(new_label(e[:field]), fb[:init_offset], true) } # bytecode start addr => { :local_var => [], :params => [], :ret => [] } # :local_var absent for external code (imported funcs) @code_info = {} import.to_a.each { |i| next unless i[:kind] == 'function' @code_info["#{i[:module]}_#{i[:field]}"] = { :params => i[:type][:params], :ret => i[:type][:ret] } } function_body.to_a.each { |fb| @code_info[fb[:init_offset]] = { :local_var => fb[:local_var], :params => fb[:type][:params], :ret => fb[:type][:ret] } } global_idx = import.to_a.find_all { |i| i[:kind] == 'global' }.length - 1 global.to_a.each { |g| @code_info[g[:init_offset]] = { :local_var => [], :params => [], :ret => [g[:type]] } @encoded.add_export new_label("global_#{global_idx += 1}_init"), g[:init_offset] } element.to_a.each { |e| @code_info[e[:init_offset]] = { :local_var => [], :params => [], :ret => ['i32'] } } data.to_a.each { |d| @code_info[d[:init_offset]] = { :local_var => [], :params => [], :ret => ['i32'] } } end
decode_header()
click to toggle source
# File metasm/exe_format/wasm.rb, line 177 def decode_header @header = Header.decode(self) @modules = [] end
decode_limits(edata=@encoded)
click to toggle source
# File metasm/exe_format/wasm.rb, line 139 def decode_limits(edata=@encoded) flags = decode_uleb(edata) out = { :initial_size => decode_uleb(edata) } out[:maximum] = decode_uleb(edata) if flags & 1 out end
decode_module_0(m)
click to toggle source
# File metasm/exe_format/wasm.rb, line 354 def decode_module_0(m) # id == 0 for not well-known modules # the module name is encoded at start of payload (uleb name length + actual name) m.name = m.edata.read(decode_uleb(m.edata)) f = "decode_module_0_#{m.name.downcase}" send(f, m) if respond_to?(f) end
decode_module_0_name(m)
click to toggle source
# File metasm/exe_format/wasm.rb, line 362 def decode_module_0_name(m) # TODO parse stored names of local variables etc end
decode_module_code(m)
click to toggle source
# File metasm/exe_format/wasm.rb, line 314 def decode_module_code(m) @function_body = [] idx = 0 decode_uleb(m.edata).times { local_vars = [] body_size = decode_uleb(m.edata) # size of local defs + bytecode (in bytes) next_ptr = m.edata.ptr + body_size decode_uleb(m.edata).times { # nr of local vars types n_vars_of_this_type = decode_uleb(m.edata) # nr of local vars of this type type = decode_type(m.edata) # actual type n_vars_of_this_type.times { local_vars << type } } code_offset = m.raw_offset + m.edata.ptr # bytecode comes next m.edata.ptr = next_ptr @function_body << { :local_var => local_vars, :init_offset => code_offset } @function_body.last[:type] = @function_signature[idx] if function_signature @encoded.add_export new_label("function_#{@function_body.length-1}"), @function_body.last[:init_offset] idx += 1 } end
decode_module_data(m)
click to toggle source
# File metasm/exe_format/wasm.rb, line 337 def decode_module_data(m) @data = [] decode_uleb(m.edata).times { idx = decode_uleb(m.edata) initoff = read_code_until_end(m) data_len = decode_uleb(m.edata) data_start_ptr = m.raw_offset + m.edata.ptr data = m.edata.read(data_len) data_end_ptr = m.raw_offset + m.edata.ptr @data << { :index => idx, :init_offset => initoff, :data => data } @encoded.add_export new_label("data_#{@data.length-1}_init_addr"), initoff @encoded.add_export new_label("data_#{@data.length-1}_start"), data_start_ptr @encoded.add_export new_label("data_#{@data.length-1}_end"), data_end_ptr } end
decode_module_element(m)
click to toggle source
# File metasm/exe_format/wasm.rb, line 300 def decode_module_element(m) @element = [] decode_uleb(m.edata).times { seg = { :table_index => decode_uleb(m.edata), :init_offset => read_code_until_end(m), :elems => [] } decode_uleb(m.edata).times { seg[:elems] << decode_uleb(m.edata) } @element << seg @encoded.add_export new_label("element_#{@element.length-1}_init_addr"), @element.last[:init_offset] } end
decode_module_export(m)
click to toggle source
# File metasm/exe_format/wasm.rb, line 284 def decode_module_export(m) @export = [] decode_uleb(m.edata).times { flen = decode_uleb(m.edata) fld = m.edata.read(flen) kind = decode_uleb(m.edata) kind = EXTERNAL_KIND[kind] || kind index = decode_uleb(m.edata) @export << { :field => fld, :kind => kind, :index => index } } end
decode_module_function(m)
click to toggle source
# File metasm/exe_format/wasm.rb, line 253 def decode_module_function(m) @function_signature = [] idx = 0 decode_uleb(m.edata).times { @function_signature << @type[decode_uleb(m.edata)] @function_body[idx][:type] = @function_signature[idx] if function_body idx += 1 } end
decode_module_global(m)
click to toggle source
# File metasm/exe_format/wasm.rb, line 277 def decode_module_global(m) @global = [] decode_uleb(m.edata).times { @global << { :type => decode_type(m.edata), :mutable => decode_uleb(m.edata), :init_offset => read_code_until_end(m) } } end
decode_module_import(m)
click to toggle source
# File metasm/exe_format/wasm.rb, line 229 def decode_module_import(m) @import = [] decode_uleb(m.edata).times { i = {} i[:module] = m.edata.read(decode_uleb(m.edata)) i[:field] = m.edata.read(decode_uleb(m.edata)) kind = decode_uleb(m.edata) i[:kind] = EXTERNAL_KIND[kind] || kind case i[:kind] when 'function' i[:type] = @type[decode_uleb(m.edata)] # XXX keep index only, in case @type is not yet known ? when 'table' i[:type] = decode_type(m.edata) i[:limits] = decode_limits(m.edata) when 'memory' i[:limits] = decode_limits(m.edata) when 'global' i[:type] = decode_type(m.edata) i[:mutable] = decode_uleb(m.edata) end @import << i } end
decode_module_memory(m)
click to toggle source
# File metasm/exe_format/wasm.rb, line 270 def decode_module_memory(m) @memory = [] decode_uleb(m.edata).times { @memory << { :limits => decode_limits(m.edata) } } end
decode_module_start(m)
click to toggle source
# File metasm/exe_format/wasm.rb, line 296 def decode_module_start(m) @start_function_index = decode_uleb(m.edata) end
decode_module_table(m)
click to toggle source
# File metasm/exe_format/wasm.rb, line 263 def decode_module_table(m) @table = [] decode_uleb(m.edata).times { @table << { :type => decode_type(m.edata), :limits => decode_limits(m.edata) } } end
decode_module_type(m)
click to toggle source
# File metasm/exe_format/wasm.rb, line 222 def decode_module_type(m) @type = [] decode_uleb(m.edata).times { @type << decode_type(m.edata) } end
decode_sleb(ed = @encoded)
click to toggle source
# File metasm/exe_format/wasm.rb, line 90 def decode_sleb(ed = @encoded) decode_uleb(ed, true) end
decode_type(edata=@encoded)
click to toggle source
# File metasm/exe_format/wasm.rb, line 101 def decode_type(edata=@encoded) form = decode_sleb(edata) type = TYPE[form] || "unk_type_#{form}" if type == 'func' type = { :params => [], :ret => [] } decode_uleb(edata).times { type[:params] << decode_type(edata) } decode_uleb(edata).times { type[:ret] << decode_type(edata) } end type end
decode_u4(edata = @encoded)
click to toggle source
# File metasm/exe_format/wasm.rb, line 67 def decode_u4(edata = @encoded) edata.decode_imm(:u32, @endianness) end
decode_uleb(ed = @encoded, signed=false)
click to toggle source
# File metasm/exe_format/wasm.rb, line 78 def decode_uleb(ed = @encoded, signed=false) v = s = 0 while s < 10*7 b = ed.read(1).unpack('C').first.to_i v |= (b & 0x7f) << s s += 7 break if (b&0x80) == 0 end v = Expression.make_signed(v, s) if signed v end
each_section() { |encoded, 0| ... }
click to toggle source
# File metasm/exe_format/wasm.rb, line 391 def each_section yield @encoded, 0 end
encode_sleb(val)
click to toggle source
# File metasm/exe_format/wasm.rb, line 89 def encode_sleb(val) encode_uleb(val, true) end
encode_u4(val)
click to toggle source
# File metasm/exe_format/wasm.rb, line 66 def encode_u4(val) Expression[val].encode(:u32, @endianness) end
encode_uleb(val, signed=false)
click to toggle source
# File metasm/exe_format/wasm.rb, line 69 def encode_uleb(val, signed=false) v = val out = EncodedData.new while v > 0x7f or v < -0x40 or (signed and v > 0x3f) out << Expression[0x80 | (v&0x7f)].encode(:u8, @endianness) v >>= 7 end out << Expression[v & 0x7f].encode(:u8, @endianness) end
get_default_entrypoints()
click to toggle source
# File metasm/exe_format/wasm.rb, line 395 def get_default_entrypoints global.to_a.map { |g| g[:init_offset] } + element.to_a.map { |e| e[:init_offset] } + data.to_a.map { |d| d[:init_offset] } + function_body.to_a.map { |f| f[:init_offset] } end
get_function_nr(nr)
click to toggle source
return the nth function body use the @function_body array and the @import array
# File metasm/exe_format/wasm.rb, line 127 def get_function_nr(nr) func_imports = @import.to_a.find_all { |i| i[:kind] == 'function' } return func_imports[nr] if nr < func_imports.length nr -= func_imports.length @function_body[nr] end
get_global_nr(nr)
click to toggle source
return the nth global use the @global array and the @import array
# File metasm/exe_format/wasm.rb, line 118 def get_global_nr(nr) glob_imports = @import.to_a.find_all { |i| i[:kind] == 'global' } return glob_imports[nr] if nr < glob_imports.length nr -= glob_imports.length @global[nr] end
init_disassembler()
click to toggle source
Calls superclass method
Metasm::ExeFormat#init_disassembler
# File metasm/exe_format/wasm.rb, line 370 def init_disassembler dasm = super() function_body.to_a.each { |fb| v = [] fb[:local_var].map { |lv| type_to_s(lv) }.each { |lv| v.last && lv == v.last.last ? v.last << lv : v << [lv] } v.map! { |sublist| # i32 ; i32 ; i32 ; i32 ; i32 ; i32 ; i64 -> 5 * i32 ; i64 sublist.length > 3 ? "#{sublist.length} * #{sublist.first}" : sublist.join(' ; ') } dasm.add_comment fb[:init_offset], "proto: #{fb[:type] ? type_to_s(fb[:type]) : 'unknown'}" dasm.add_comment fb[:init_offset], "vars: #{v.join(' ; ')}" } global.to_a.each { |g| dasm.add_comment g[:init_offset], "type: #{type_to_s(g[:type])}" } dasm.function[:default] = @cpu.disassembler_default_func dasm end
read_code_until_end(m=nil)
click to toggle source
wtf read wasm bytecode until reaching the end opcode return the byte offset
# File metasm/exe_format/wasm.rb, line 149 def read_code_until_end(m=nil) if m raw_offset = m.raw_offset + m.edata.ptr edata = m.edata else edata = @encoded end while op = edata.decode_imm(:u8, @endianness) case op when 0xb # end opcode return raw_offset when 0xe # indirect branch wtf decode_uleb(edata).times { decode_uleb(edata) } decode_uleb(edata) when 0x43 edata.read(4) when 0x44 edata.read(8) else OPCODE_IMM_COUNT[op].times { decode_uleb(edata) } end end raw_offset end
sizeof_u4()
click to toggle source
# File metasm/exe_format/wasm.rb, line 68 def sizeof_u4 ; 4 ; end
type_to_s(t)
click to toggle source
# File metasm/exe_format/wasm.rb, line 134 def type_to_s(t) return t unless t.kind_of?(::Hash) (t[:ret].map { |tt| type_to_s(tt) }.join(', ') << ' f(' << t[:params].map { |tt| type_to_s(tt) }.join(', ') << ')').strip end