metasm 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +3 -0
- data.tar.gz.sig +0 -0
- data/Gemfile +3 -2
- data/metasm.gemspec +3 -2
- data/metasm.rb +4 -1
- data/metasm/compile_c.rb +2 -2
- data/metasm/cpu/arc/decode.rb +0 -21
- data/metasm/cpu/arc/main.rb +4 -4
- data/metasm/cpu/arm/decode.rb +1 -5
- data/metasm/cpu/arm/main.rb +3 -3
- data/metasm/cpu/arm64/decode.rb +2 -6
- data/metasm/cpu/arm64/main.rb +5 -5
- data/metasm/cpu/bpf/decode.rb +3 -35
- data/metasm/cpu/bpf/main.rb +5 -5
- data/metasm/cpu/bpf/render.rb +1 -12
- data/metasm/cpu/cy16/decode.rb +0 -6
- data/metasm/cpu/cy16/main.rb +3 -3
- data/metasm/cpu/cy16/render.rb +0 -11
- data/metasm/cpu/dalvik/decode.rb +4 -26
- data/metasm/cpu/dalvik/main.rb +20 -2
- data/metasm/cpu/dalvik/opcodes.rb +3 -2
- data/metasm/cpu/{mips/compile_c.rb → ebpf.rb} +5 -2
- data/metasm/cpu/ebpf/debug.rb +61 -0
- data/metasm/cpu/ebpf/decode.rb +142 -0
- data/metasm/cpu/ebpf/main.rb +58 -0
- data/metasm/cpu/ebpf/opcodes.rb +97 -0
- data/metasm/cpu/ebpf/render.rb +36 -0
- data/metasm/cpu/ia32/debug.rb +39 -1
- data/metasm/cpu/ia32/decode.rb +111 -90
- data/metasm/cpu/ia32/decompile.rb +45 -37
- data/metasm/cpu/ia32/main.rb +10 -0
- data/metasm/cpu/ia32/parse.rb +6 -0
- data/metasm/cpu/mcs51/decode.rb +1 -1
- data/metasm/cpu/mcs51/main.rb +11 -0
- data/metasm/cpu/mips/decode.rb +8 -18
- data/metasm/cpu/mips/main.rb +3 -3
- data/metasm/cpu/mips/opcodes.rb +1 -1
- data/metasm/cpu/msp430/decode.rb +2 -6
- data/metasm/cpu/msp430/main.rb +3 -3
- data/metasm/cpu/openrisc.rb +11 -0
- data/metasm/cpu/openrisc/debug.rb +106 -0
- data/metasm/cpu/openrisc/decode.rb +182 -0
- data/metasm/cpu/openrisc/decompile.rb +350 -0
- data/metasm/cpu/openrisc/main.rb +70 -0
- data/metasm/cpu/openrisc/opcodes.rb +109 -0
- data/metasm/cpu/openrisc/render.rb +37 -0
- data/metasm/cpu/ppc/decode.rb +0 -25
- data/metasm/cpu/ppc/main.rb +6 -6
- data/metasm/cpu/ppc/opcodes.rb +3 -4
- data/metasm/cpu/python/decode.rb +0 -20
- data/metasm/cpu/python/main.rb +1 -1
- data/metasm/cpu/sh4/decode.rb +2 -6
- data/metasm/cpu/sh4/main.rb +25 -23
- data/metasm/cpu/st20/decode.rb +0 -7
- data/metasm/cpu/webasm.rb +11 -0
- data/metasm/cpu/webasm/debug.rb +31 -0
- data/metasm/cpu/webasm/decode.rb +321 -0
- data/metasm/cpu/webasm/decompile.rb +386 -0
- data/metasm/cpu/webasm/encode.rb +104 -0
- data/metasm/cpu/webasm/main.rb +81 -0
- data/metasm/cpu/webasm/opcodes.rb +214 -0
- data/metasm/cpu/x86_64/compile_c.rb +13 -9
- data/metasm/cpu/x86_64/parse.rb +1 -1
- data/metasm/cpu/z80/decode.rb +0 -27
- data/metasm/cpu/z80/main.rb +3 -3
- data/metasm/cpu/z80/render.rb +0 -11
- data/metasm/debug.rb +43 -8
- data/metasm/decode.rb +62 -14
- data/metasm/decompile.rb +793 -466
- data/metasm/disassemble.rb +188 -131
- data/metasm/disassemble_api.rb +30 -17
- data/metasm/dynldr.rb +2 -2
- data/metasm/encode.rb +8 -2
- data/metasm/exe_format/autoexe.rb +2 -0
- data/metasm/exe_format/coff.rb +21 -3
- data/metasm/exe_format/coff_decode.rb +12 -0
- data/metasm/exe_format/coff_encode.rb +6 -3
- data/metasm/exe_format/dex.rb +13 -3
- data/metasm/exe_format/elf.rb +12 -2
- data/metasm/exe_format/elf_decode.rb +59 -1
- data/metasm/exe_format/main.rb +2 -0
- data/metasm/exe_format/mz.rb +1 -0
- data/metasm/exe_format/pe.rb +25 -3
- data/metasm/exe_format/wasm.rb +402 -0
- data/metasm/gui/dasm_decomp.rb +171 -95
- data/metasm/gui/dasm_graph.rb +61 -2
- data/metasm/gui/dasm_hex.rb +2 -2
- data/metasm/gui/dasm_main.rb +45 -19
- data/metasm/gui/debug.rb +13 -4
- data/metasm/gui/gtk.rb +12 -4
- data/metasm/main.rb +108 -103
- data/metasm/os/emulator.rb +175 -0
- data/metasm/os/main.rb +11 -6
- data/metasm/parse.rb +23 -12
- data/metasm/parse_c.rb +189 -135
- data/metasm/preprocessor.rb +16 -1
- data/misc/openrisc-parser.rb +79 -0
- data/samples/dasm-plugins/scanxrefs.rb +6 -4
- data/samples/dasm-plugins/selfmodify.rb +8 -8
- data/samples/dbg-plugins/trace_func.rb +1 -1
- data/samples/disassemble-gui.rb +14 -3
- data/samples/emubios.rb +251 -0
- data/samples/emudbg.rb +127 -0
- data/samples/lindebug.rb +79 -78
- data/samples/metasm-shell.rb +8 -8
- data/tests/all.rb +1 -1
- data/tests/expression.rb +2 -0
- data/tests/graph_layout.rb +1 -1
- data/tests/ia32.rb +1 -0
- data/tests/mips.rb +1 -1
- data/tests/preprocessor.rb +18 -0
- metadata +124 -6
- metadata.gz.sig +0 -0
data/metasm/exe_format/main.rb
CHANGED
|
@@ -217,6 +217,8 @@ class ExeFormat
|
|
|
217
217
|
|
|
218
218
|
def shortname; self.class.name.split('::').last.downcase; end
|
|
219
219
|
|
|
220
|
+
def inspect; "<Metasm::ExeFormat %s @%x>" % [shortname, object_id]; end
|
|
221
|
+
|
|
220
222
|
module IntToHash
|
|
221
223
|
# converts a constant name to its numeric value using the hash
|
|
222
224
|
# {1 => 'toto', 2 => 'tata'}: 'toto' => 1, 42 => 42, 'tutu' => raise
|
data/metasm/exe_format/mz.rb
CHANGED
|
@@ -11,6 +11,7 @@ require 'metasm/decode'
|
|
|
11
11
|
module Metasm
|
|
12
12
|
class MZ < ExeFormat
|
|
13
13
|
MAGIC = 'MZ' # 0x4d5a
|
|
14
|
+
MAGIC.force_encoding('BINARY') if MAGIC.respond_to?(:force_encoding)
|
|
14
15
|
class Header < SerialStruct
|
|
15
16
|
mem :magic, 2, MAGIC
|
|
16
17
|
words :cblp, :cp, :crlc, :cparhdr, :minalloc, :maxalloc, :ss, :sp, :csum, :ip, :cs, :lfarlc, :ovno
|
data/metasm/exe_format/pe.rb
CHANGED
|
@@ -11,8 +11,9 @@ require 'metasm/exe_format/coff'
|
|
|
11
11
|
module Metasm
|
|
12
12
|
class PE < COFF
|
|
13
13
|
MAGIC = "PE\0\0" # 0x50450000
|
|
14
|
+
MAGIC.force_encoding('BINARY') if MAGIC.respond_to?(:force_encoding)
|
|
14
15
|
|
|
15
|
-
attr_accessor :coff_offset, :signature, :mz
|
|
16
|
+
attr_accessor :coff_offset, :signature, :mz, :productid
|
|
16
17
|
|
|
17
18
|
def initialize(*a)
|
|
18
19
|
super(*a)
|
|
@@ -26,7 +27,14 @@ class PE < COFF
|
|
|
26
27
|
def decode_header
|
|
27
28
|
@cursection ||= self
|
|
28
29
|
@encoded.ptr = 0x3c
|
|
29
|
-
|
|
30
|
+
lfanew = decode_word(@encoded)
|
|
31
|
+
|
|
32
|
+
try_rich_sz = [0x400, lfanew].min
|
|
33
|
+
rich_ary = []
|
|
34
|
+
rich_ary << decode_word(@encoded) while @encoded.ptr < try_rich_sz
|
|
35
|
+
@productid = decode_productid(rich_ary)
|
|
36
|
+
|
|
37
|
+
@encoded.ptr = lfanew
|
|
30
38
|
@signature = @encoded.read(4)
|
|
31
39
|
raise InvalidExeFormat, "Invalid PE signature #{@signature.inspect}" if @signature != MAGIC
|
|
32
40
|
@coff_offset = @encoded.ptr
|
|
@@ -38,6 +46,20 @@ class PE < COFF
|
|
|
38
46
|
super()
|
|
39
47
|
end
|
|
40
48
|
|
|
49
|
+
RICH_MAGIC = 0x68636952 # "Rich"
|
|
50
|
+
def decode_productid(ary)
|
|
51
|
+
return unless idx = ary.index(RICH_MAGIC) and xorkey = ary[idx+1]
|
|
52
|
+
ary = ary[0, idx-1].map { |dw| dw ^ xorkey }
|
|
53
|
+
return unless idx = ary.rindex(0x536E6144) # "DanS"
|
|
54
|
+
ary = ary[idx+2..-1]
|
|
55
|
+
out = []
|
|
56
|
+
until ary.empty?
|
|
57
|
+
ar1 = ary.shift
|
|
58
|
+
out << { :id => ar1 >> 16, :ver => ar1 & 0xffff, :count => ary.shift.to_i }
|
|
59
|
+
end
|
|
60
|
+
out
|
|
61
|
+
end
|
|
62
|
+
|
|
41
63
|
# creates a default MZ file to be used in the PE header
|
|
42
64
|
# this one is specially crafted to fit in the 0x3c bytes before the signature
|
|
43
65
|
def encode_default_mz_header
|
|
@@ -226,7 +248,7 @@ EOS
|
|
|
226
248
|
a.each { |aa|
|
|
227
249
|
next if aa == Expression::Unknown
|
|
228
250
|
dasm.auto_label_at(aa, 'seh', 'loc', 'sub')
|
|
229
|
-
dasm.addrs_todo <<
|
|
251
|
+
dasm.addrs_todo << { :addr => aa }
|
|
230
252
|
}
|
|
231
253
|
super(dasm, di)
|
|
232
254
|
else
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
3
|
+
#
|
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
require 'metasm/exe_format/main'
|
|
8
|
+
require 'metasm/encode'
|
|
9
|
+
require 'metasm/decode'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
module Metasm
|
|
13
|
+
# WebAssembly
|
|
14
|
+
# leb integer encoding taken from dex.rb
|
|
15
|
+
class WasmFile < ExeFormat
|
|
16
|
+
MAGIC = "\0asm"
|
|
17
|
+
MAGIC.force_encoding('binary') if MAGIC.respond_to?(:force_encoding)
|
|
18
|
+
|
|
19
|
+
SECTION_NAME = { 1 => 'Type', 2 => 'Import', 3 => 'Function', 4 => 'Table',
|
|
20
|
+
5 => 'Memory', 6 => 'Global', 7 => 'Export', 8 => 'Start',
|
|
21
|
+
9 => 'Element', 10 => 'Code', 11 => 'Data' }
|
|
22
|
+
|
|
23
|
+
TYPE = { -1 => 'i32', -2 => 'i64', -3 => 'f32', -4 => 'f64',
|
|
24
|
+
-0x10 => 'anyfunc', -0x20 => 'func', -0x40 => 'none' }
|
|
25
|
+
|
|
26
|
+
EXTERNAL_KIND = { 0 => 'function', 1 => 'table', 2 => 'memory', 3 => 'global' }
|
|
27
|
+
|
|
28
|
+
# begin WTF
|
|
29
|
+
OPCODE_IMM_COUNT = Hash.new(0)
|
|
30
|
+
[2, 3, 4, 0xc, 0xd, 0x10].each { |op| OPCODE_IMM_COUNT[op] = 1 }
|
|
31
|
+
OPCODE_IMM_COUNT[0x11] = 2
|
|
32
|
+
(0x20..0x24).each { |op| OPCODE_IMM_COUNT[op] = 1 }
|
|
33
|
+
(0x28..0x3e).each { |op| OPCODE_IMM_COUNT[op] = 2 }
|
|
34
|
+
(0x3f..0x42).each { |op| OPCODE_IMM_COUNT[op] = 1 }
|
|
35
|
+
# 0x43 followed by uint32, 0x44 followed by uint64 (float constants)
|
|
36
|
+
# end WTF
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class SerialStruct < Metasm::SerialStruct
|
|
40
|
+
# TODO move uleb/sleb to new_field for sizeof
|
|
41
|
+
new_int_field :u4, :uleb, :sleb
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class Header < SerialStruct
|
|
45
|
+
mem :sig, 4, MAGIC
|
|
46
|
+
decode_hook { |exe, hdr| raise InvalidExeFormat, "E: invalid WasmFile signature #{hdr.sig.inspect}" if hdr.sig != MAGIC }
|
|
47
|
+
u4 :ver, 1
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
class Module < SerialStruct
|
|
51
|
+
uleb :id
|
|
52
|
+
fld_enum :id, SECTION_NAME
|
|
53
|
+
uleb :payload_len
|
|
54
|
+
attr_accessor :edata, :raw_offset, :name
|
|
55
|
+
|
|
56
|
+
def decode(exe)
|
|
57
|
+
super(exe)
|
|
58
|
+
@raw_offset = exe.encoded.ptr
|
|
59
|
+
@edata = exe.encoded[exe.encoded.ptr, @payload_len]
|
|
60
|
+
exe.encoded.ptr += @payload_len
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
attr_accessor :endianness
|
|
65
|
+
|
|
66
|
+
def encode_u4(val) Expression[val].encode(:u32, @endianness) end
|
|
67
|
+
def decode_u4(edata = @encoded) edata.decode_imm(:u32, @endianness) end
|
|
68
|
+
def sizeof_u4 ; 4 ; end
|
|
69
|
+
def encode_uleb(val, signed=false)
|
|
70
|
+
v = val
|
|
71
|
+
out = EncodedData.new
|
|
72
|
+
while v > 0x7f or v < -0x40 or (signed and v > 0x3f)
|
|
73
|
+
out << Expression[0x80 | (v&0x7f)].encode(:u8, @endianness)
|
|
74
|
+
v >>= 7
|
|
75
|
+
end
|
|
76
|
+
out << Expression[v & 0x7f].encode(:u8, @endianness)
|
|
77
|
+
end
|
|
78
|
+
def decode_uleb(ed = @encoded, signed=false)
|
|
79
|
+
v = s = 0
|
|
80
|
+
while s < 10*7
|
|
81
|
+
b = ed.read(1).unpack('C').first.to_i
|
|
82
|
+
v |= (b & 0x7f) << s
|
|
83
|
+
s += 7
|
|
84
|
+
break if (b&0x80) == 0
|
|
85
|
+
end
|
|
86
|
+
v = Expression.make_signed(v, s) if signed
|
|
87
|
+
v
|
|
88
|
+
end
|
|
89
|
+
def encode_sleb(val) encode_uleb(val, true) end
|
|
90
|
+
def decode_sleb(ed = @encoded) decode_uleb(ed, true) end
|
|
91
|
+
attr_accessor :header, :modules, :type, :import, :function_signature,
|
|
92
|
+
:table, :memory, :global, :export, :start_function_index,
|
|
93
|
+
:element, :function_body, :data, :code_info
|
|
94
|
+
|
|
95
|
+
def initialize(endianness=:little)
|
|
96
|
+
@endianness = endianness
|
|
97
|
+
@encoded = EncodedData.new
|
|
98
|
+
super()
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def decode_type(edata=@encoded)
|
|
102
|
+
form = decode_sleb(edata)
|
|
103
|
+
type = TYPE[form] || "unk_type_#{form}"
|
|
104
|
+
if type == 'func'
|
|
105
|
+
type = { :params => [], :ret => [] }
|
|
106
|
+
decode_uleb(edata).times {
|
|
107
|
+
type[:params] << decode_type(edata)
|
|
108
|
+
}
|
|
109
|
+
decode_uleb(edata).times {
|
|
110
|
+
type[:ret] << decode_type(edata)
|
|
111
|
+
}
|
|
112
|
+
end
|
|
113
|
+
type
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# return the nth global
|
|
117
|
+
# use the @global array and the @import array
|
|
118
|
+
def get_global_nr(nr)
|
|
119
|
+
glob_imports = @import.to_a.find_all { |i| i[:kind] == 'global' }
|
|
120
|
+
return glob_imports[nr] if nr < glob_imports.length
|
|
121
|
+
nr -= glob_imports.length
|
|
122
|
+
@global[nr]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# return the nth function body
|
|
126
|
+
# use the @function_body array and the @import array
|
|
127
|
+
def get_function_nr(nr)
|
|
128
|
+
func_imports = @import.to_a.find_all { |i| i[:kind] == 'function' }
|
|
129
|
+
return func_imports[nr] if nr < func_imports.length
|
|
130
|
+
nr -= func_imports.length
|
|
131
|
+
@function_body[nr]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def type_to_s(t)
|
|
135
|
+
return t unless t.kind_of?(::Hash)
|
|
136
|
+
(t[:ret].map { |tt| type_to_s(tt) }.join(', ') << ' f(' << t[:params].map { |tt| type_to_s(tt) }.join(', ') << ')').strip
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def decode_limits(edata=@encoded)
|
|
140
|
+
flags = decode_uleb(edata)
|
|
141
|
+
out = { :initial_size => decode_uleb(edata) }
|
|
142
|
+
out[:maximum] = decode_uleb(edata) if flags & 1
|
|
143
|
+
out
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# wtf
|
|
147
|
+
# read wasm bytecode until reaching the end opcode
|
|
148
|
+
# return the byte offset
|
|
149
|
+
def read_code_until_end(m=nil)
|
|
150
|
+
if m
|
|
151
|
+
raw_offset = m.raw_offset + m.edata.ptr
|
|
152
|
+
edata = m.edata
|
|
153
|
+
else
|
|
154
|
+
edata = @encoded
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
while op = edata.decode_imm(:u8, @endianness)
|
|
158
|
+
case op
|
|
159
|
+
when 0xb
|
|
160
|
+
# end opcode
|
|
161
|
+
return raw_offset
|
|
162
|
+
when 0xe
|
|
163
|
+
# indirect branch wtf
|
|
164
|
+
decode_uleb(edata).times { decode_uleb(edata) }
|
|
165
|
+
decode_uleb(edata)
|
|
166
|
+
when 0x43
|
|
167
|
+
edata.read(4)
|
|
168
|
+
when 0x44
|
|
169
|
+
edata.read(8)
|
|
170
|
+
else
|
|
171
|
+
OPCODE_IMM_COUNT[op].times { decode_uleb(edata) }
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
raw_offset
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def decode_header
|
|
178
|
+
@header = Header.decode(self)
|
|
179
|
+
@modules = []
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def decode
|
|
183
|
+
decode_header
|
|
184
|
+
while @encoded.ptr < @encoded.length
|
|
185
|
+
@modules << Module.decode(self)
|
|
186
|
+
end
|
|
187
|
+
@modules.each { |m|
|
|
188
|
+
@encoded.add_export(new_label("module_#{m.id}"), m.raw_offset)
|
|
189
|
+
f = "decode_module_#{m.id.to_s.downcase}"
|
|
190
|
+
send(f, m) if respond_to?(f)
|
|
191
|
+
}
|
|
192
|
+
func_imports = @import.to_a.find_all { |i| i[:kind] == 'function' }
|
|
193
|
+
export.to_a.each { |e|
|
|
194
|
+
next if e[:kind] != 'function' # TODO resolve init_offset for globals etc?
|
|
195
|
+
idx = e[:index] - func_imports.length
|
|
196
|
+
next if not fb = function_body.to_a[idx]
|
|
197
|
+
@encoded.add_export(new_label(e[:field]), fb[:init_offset], true)
|
|
198
|
+
}
|
|
199
|
+
# bytecode start addr => { :local_var => [], :params => [], :ret => [] }
|
|
200
|
+
# :local_var absent for external code (imported funcs)
|
|
201
|
+
@code_info = {}
|
|
202
|
+
import.to_a.each { |i|
|
|
203
|
+
next unless i[:kind] == 'function'
|
|
204
|
+
@code_info["#{i[:module]}_#{i[:field]}"] = { :params => i[:type][:params], :ret => i[:type][:ret] }
|
|
205
|
+
}
|
|
206
|
+
function_body.to_a.each { |fb|
|
|
207
|
+
@code_info[fb[:init_offset]] = { :local_var => fb[:local_var], :params => fb[:type][:params], :ret => fb[:type][:ret] }
|
|
208
|
+
}
|
|
209
|
+
global_idx = import.to_a.find_all { |i| i[:kind] == 'global' }.length - 1
|
|
210
|
+
global.to_a.each { |g|
|
|
211
|
+
@code_info[g[:init_offset]] = { :local_var => [], :params => [], :ret => [g[:type]] }
|
|
212
|
+
@encoded.add_export new_label("global_#{global_idx += 1}_init"), g[:init_offset]
|
|
213
|
+
}
|
|
214
|
+
element.to_a.each { |e|
|
|
215
|
+
@code_info[e[:init_offset]] = { :local_var => [], :params => [], :ret => ['i32'] }
|
|
216
|
+
}
|
|
217
|
+
data.to_a.each { |d|
|
|
218
|
+
@code_info[d[:init_offset]] = { :local_var => [], :params => [], :ret => ['i32'] }
|
|
219
|
+
}
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def decode_module_type(m)
|
|
223
|
+
@type = []
|
|
224
|
+
decode_uleb(m.edata).times {
|
|
225
|
+
@type << decode_type(m.edata)
|
|
226
|
+
}
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def decode_module_import(m)
|
|
230
|
+
@import = []
|
|
231
|
+
decode_uleb(m.edata).times {
|
|
232
|
+
i = {}
|
|
233
|
+
i[:module] = m.edata.read(decode_uleb(m.edata))
|
|
234
|
+
i[:field] = m.edata.read(decode_uleb(m.edata))
|
|
235
|
+
kind = decode_uleb(m.edata)
|
|
236
|
+
i[:kind] = EXTERNAL_KIND[kind] || kind
|
|
237
|
+
case i[:kind]
|
|
238
|
+
when 'function'
|
|
239
|
+
i[:type] = @type[decode_uleb(m.edata)] # XXX keep index only, in case @type is not yet known ?
|
|
240
|
+
when 'table'
|
|
241
|
+
i[:type] = decode_type(m.edata)
|
|
242
|
+
i[:limits] = decode_limits(m.edata)
|
|
243
|
+
when 'memory'
|
|
244
|
+
i[:limits] = decode_limits(m.edata)
|
|
245
|
+
when 'global'
|
|
246
|
+
i[:type] = decode_type(m.edata)
|
|
247
|
+
i[:mutable] = decode_uleb(m.edata)
|
|
248
|
+
end
|
|
249
|
+
@import << i
|
|
250
|
+
}
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def decode_module_function(m)
|
|
254
|
+
@function_signature = []
|
|
255
|
+
idx = 0
|
|
256
|
+
decode_uleb(m.edata).times {
|
|
257
|
+
@function_signature << @type[decode_uleb(m.edata)]
|
|
258
|
+
@function_body[idx][:type] = @function_signature[idx] if function_body
|
|
259
|
+
idx += 1
|
|
260
|
+
}
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def decode_module_table(m)
|
|
264
|
+
@table = []
|
|
265
|
+
decode_uleb(m.edata).times {
|
|
266
|
+
@table << { :type => decode_type(m.edata), :limits => decode_limits(m.edata) }
|
|
267
|
+
}
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def decode_module_memory(m)
|
|
271
|
+
@memory = []
|
|
272
|
+
decode_uleb(m.edata).times {
|
|
273
|
+
@memory << { :limits => decode_limits(m.edata) }
|
|
274
|
+
}
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def decode_module_global(m)
|
|
278
|
+
@global = []
|
|
279
|
+
decode_uleb(m.edata).times {
|
|
280
|
+
@global << { :type => decode_type(m.edata), :mutable => decode_uleb(m.edata), :init_offset => read_code_until_end(m) }
|
|
281
|
+
}
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def decode_module_export(m)
|
|
285
|
+
@export = []
|
|
286
|
+
decode_uleb(m.edata).times {
|
|
287
|
+
flen = decode_uleb(m.edata)
|
|
288
|
+
fld = m.edata.read(flen)
|
|
289
|
+
kind = decode_uleb(m.edata)
|
|
290
|
+
kind = EXTERNAL_KIND[kind] || kind
|
|
291
|
+
index = decode_uleb(m.edata)
|
|
292
|
+
@export << { :field => fld, :kind => kind, :index => index }
|
|
293
|
+
}
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def decode_module_start(m)
|
|
297
|
+
@start_function_index = decode_uleb(m.edata)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def decode_module_element(m)
|
|
301
|
+
@element = []
|
|
302
|
+
decode_uleb(m.edata).times {
|
|
303
|
+
seg = { :table_index => decode_uleb(m.edata),
|
|
304
|
+
:init_offset => read_code_until_end(m),
|
|
305
|
+
:elems => [] }
|
|
306
|
+
decode_uleb(m.edata).times {
|
|
307
|
+
seg[:elems] << decode_uleb(m.edata)
|
|
308
|
+
}
|
|
309
|
+
@element << seg
|
|
310
|
+
@encoded.add_export new_label("element_#{@element.length-1}_init_addr"), @element.last[:init_offset]
|
|
311
|
+
}
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def decode_module_code(m)
|
|
315
|
+
@function_body = []
|
|
316
|
+
idx = 0
|
|
317
|
+
decode_uleb(m.edata).times {
|
|
318
|
+
local_vars = []
|
|
319
|
+
body_size = decode_uleb(m.edata) # size of local defs + bytecode (in bytes)
|
|
320
|
+
next_ptr = m.edata.ptr + body_size
|
|
321
|
+
decode_uleb(m.edata).times { # nr of local vars types
|
|
322
|
+
n_vars_of_this_type = decode_uleb(m.edata) # nr of local vars of this type
|
|
323
|
+
type = decode_type(m.edata) # actual type
|
|
324
|
+
n_vars_of_this_type.times {
|
|
325
|
+
local_vars << type
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
code_offset = m.raw_offset + m.edata.ptr # bytecode comes next
|
|
329
|
+
m.edata.ptr = next_ptr
|
|
330
|
+
@function_body << { :local_var => local_vars, :init_offset => code_offset }
|
|
331
|
+
@function_body.last[:type] = @function_signature[idx] if function_signature
|
|
332
|
+
@encoded.add_export new_label("function_#{@function_body.length-1}"), @function_body.last[:init_offset]
|
|
333
|
+
idx += 1
|
|
334
|
+
}
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def decode_module_data(m)
|
|
338
|
+
@data = []
|
|
339
|
+
decode_uleb(m.edata).times {
|
|
340
|
+
idx = decode_uleb(m.edata)
|
|
341
|
+
initoff = read_code_until_end(m)
|
|
342
|
+
data_len = decode_uleb(m.edata)
|
|
343
|
+
data_start_ptr = m.raw_offset + m.edata.ptr
|
|
344
|
+
data = m.edata.read(data_len)
|
|
345
|
+
data_end_ptr = m.raw_offset + m.edata.ptr
|
|
346
|
+
|
|
347
|
+
@data << { :index => idx, :init_offset => initoff, :data => data }
|
|
348
|
+
@encoded.add_export new_label("data_#{@data.length-1}_init_addr"), initoff
|
|
349
|
+
@encoded.add_export new_label("data_#{@data.length-1}_start"), data_start_ptr
|
|
350
|
+
@encoded.add_export new_label("data_#{@data.length-1}_end"), data_end_ptr
|
|
351
|
+
}
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def decode_module_0(m)
|
|
355
|
+
# id == 0 for not well-known modules
|
|
356
|
+
# the module name is encoded at start of payload (uleb name length + actual name)
|
|
357
|
+
m.name = m.edata.read(decode_uleb(m.edata))
|
|
358
|
+
f = "decode_module_0_#{m.name.downcase}"
|
|
359
|
+
send(f, m) if respond_to?(f)
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def decode_module_0_name(m)
|
|
363
|
+
# TODO parse stored names of local variables etc
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def cpu_from_headers
|
|
367
|
+
WebAsm.new(self)
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def init_disassembler
|
|
371
|
+
dasm = super()
|
|
372
|
+
function_body.to_a.each { |fb|
|
|
373
|
+
v = []
|
|
374
|
+
fb[:local_var].map { |lv| type_to_s(lv) }.each { |lv|
|
|
375
|
+
v.last && lv == v.last.last ? v.last << lv : v << [lv]
|
|
376
|
+
}
|
|
377
|
+
v.map! { |sublist|
|
|
378
|
+
# i32 ; i32 ; i32 ; i32 ; i32 ; i32 ; i64 -> 5 * i32 ; i64
|
|
379
|
+
sublist.length > 3 ? "#{sublist.length} * #{sublist.first}" : sublist.join(' ; ')
|
|
380
|
+
}
|
|
381
|
+
dasm.add_comment fb[:init_offset], "proto: #{fb[:type] ? type_to_s(fb[:type]) : 'unknown'}"
|
|
382
|
+
dasm.add_comment fb[:init_offset], "vars: #{v.join(' ; ')}"
|
|
383
|
+
}
|
|
384
|
+
global.to_a.each { |g|
|
|
385
|
+
dasm.add_comment g[:init_offset], "type: #{type_to_s(g[:type])}"
|
|
386
|
+
}
|
|
387
|
+
dasm.function[:default] = @cpu.disassembler_default_func
|
|
388
|
+
dasm
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def each_section
|
|
392
|
+
yield @encoded, 0
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
def get_default_entrypoints
|
|
396
|
+
global.to_a.map { |g| g[:init_offset] } +
|
|
397
|
+
element.to_a.map { |e| e[:init_offset] } +
|
|
398
|
+
data.to_a.map { |d| d[:init_offset] } +
|
|
399
|
+
function_body.to_a.map { |f| f[:init_offset] }
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
end
|