Skip to content

Commit c892da4

Browse files
committed
Land rapid7#3181, @dmaloney-r7's fix for metasm
2 parents 74554ed + a2ea880 commit c892da4

30 files changed

+660
-97
lines changed

lib/metasm/metasm.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ module Metasm
3636
'Ia32' => 'cpu/ia32', 'MIPS' => 'cpu/mips', 'PowerPC' => 'cpu/ppc', 'ARM' => 'cpu/arm',
3737
'X86_64' => 'cpu/x86_64', 'Sh4' => 'cpu/sh4', 'Dalvik' => 'cpu/dalvik', 'ARC' => 'cpu/arc',
3838
'Python' => 'cpu/python', 'Z80' => 'cpu/z80', 'CY16' => 'cpu/cy16', 'BPF' => 'cpu/bpf',
39+
'MSP430' => 'cpu/msp430',
3940
'C' => 'compile_c',
4041
'MZ' => 'exe_format/mz', 'PE' => 'exe_format/pe',
4142
'ELF' => 'exe_format/elf', 'COFF' => 'exe_format/coff',

lib/metasm/metasm/cpu/ia32/parse.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def self.parse(lexer, otok, cpu)
104104
i = o
105105
s = 1
106106
when Expression
107-
if o.op == :* and (o.rexpr.kind_of? Reg or o.lexpr.kind_of? Reg)
107+
if o.op == :* and (o.rexpr.kind_of?(Reg) or o.lexpr.kind_of?(Reg))
108108
# scaled index
109109
raise otok, 'mrm: too many indexes' if i
110110
s = o.lexpr
@@ -129,7 +129,9 @@ def self.parse(lexer, otok, cpu)
129129
walker[regify[content.reduce]]
130130

131131
# ensure found immediate is really an immediate
132-
raise otok, 'mrm: reg in imm' if imm.kind_of? Expression and not imm.externals.grep(Reg).empty?
132+
raise otok, 'mrm: reg in imm' if imm.kind_of?(Expression) and not imm.externals.grep(Reg).empty?
133+
134+
raise otok, 'mrm: bad reg size' if b.kind_of?(Reg) and i.kind_of?(Reg) and b.sz != i.sz
133135

134136
# find default address size
135137
adsz = b ? b.sz : i ? i.sz : nil

lib/metasm/metasm/cpu/msp430.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
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/main'
8+
require 'metasm/cpu/msp430/decode'
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
# This file is part of Metasm, the Ruby assembly manipulation suite
2+
# Copyright (C) 2006-2010 Yoann GUILLOT
3+
#
4+
# Licence is LGPL, see LICENCE in the top-level directory
5+
6+
require 'metasm/cpu/msp430/opcodes'
7+
require 'metasm/decode'
8+
9+
module Metasm
10+
class MSP430
11+
def build_opcode_bin_mask(op)
12+
op.bin_mask = 0
13+
op.fields.each_key { |f|
14+
op.bin_mask |= @fields_mask[f] << @fields_shift[f]
15+
}
16+
op.bin_mask ^= 0xffff
17+
end
18+
19+
def build_bin_lookaside
20+
lookaside = Array.new(256) { [] }
21+
opcode_list.each { |op|
22+
build_opcode_bin_mask op
23+
b = (op.bin >> 8) & 255
24+
msk = (op.bin_mask >> 8) & 255
25+
26+
for i in b..(b | (255^msk))
27+
lookaside[i] << op if i & msk == b & msk
28+
end
29+
}
30+
lookaside
31+
end
32+
33+
def decode_findopcode(edata)
34+
di = DecodedInstruction.new(self)
35+
val = edata.decode_imm(:u16, @endianness)
36+
edata.ptr -= 2
37+
di.opcode = @bin_lookaside[(val >> 8) & 0xff].find { |opcode| (val & opcode.bin_mask) == opcode.bin }
38+
di if di.opcode
39+
end
40+
41+
def decode_instr_op(edata, di)
42+
before_ptr = edata.ptr
43+
op = di.opcode
44+
di.instruction.opname = op.name
45+
val = edata.decode_imm(:u16, @endianness)
46+
47+
field_val = lambda{ |f|
48+
(val >> @fields_shift[f]) & @fields_mask[f]
49+
}
50+
51+
# must decode rs first
52+
vals = {}
53+
([:rs, :rd, :r_pc] & op.args).each { |a|
54+
mod = { :rs => :as, :rd => :ad, :r_pc => :ad }[a]
55+
mod = :as if mod == :ad and not op.fields[mod] # addop_macro1 -> rs + ad
56+
57+
if a == :r_pc
58+
r = Reg.new(0)
59+
else
60+
r = Reg.new(field_val[a])
61+
end
62+
63+
w = op.props[:byte] ? 1 : 2
64+
65+
case field_val[mod]
66+
when 0
67+
if r.i == 3 and a == :rs
68+
vals[a] = Expression[0]
69+
else
70+
vals[a] = r
71+
end
72+
when 1
73+
if r.i == 3 and a == :rs
74+
vals[a] = Expression[1]
75+
else
76+
imm = edata.decode_imm(:u16, @endianness)
77+
r = nil if r.i == 2 # [imm]
78+
vals[a] = Memref.new(r, imm, w)
79+
end
80+
when 2
81+
if r.i == 3
82+
vals[a] = Expression[2]
83+
elsif r.i == 2
84+
vals[a] = Expression[4]
85+
else
86+
vals[a] = Memref.new(r, 0, w)
87+
end
88+
when 3
89+
if r.i == 3
90+
vals[a] = Expression[-1]
91+
elsif r.i == 2
92+
vals[a] = Expression[8]
93+
elsif r.i == 0 # pc++
94+
# XXX order wrt other edata.decode_imm ?
95+
vals[a] = Expression[edata.decode_imm(:u16, @endianness)]
96+
else
97+
vals[a] = Memref.new(r, 0, w, true)
98+
end
99+
end
100+
}
101+
102+
op.args.each { |a|
103+
di.instruction.args << case a
104+
when :joff; Expression[2 * Expression.make_signed(field_val[a], 10)]
105+
when :rs, :rd, :r_pc; vals[a]
106+
else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
107+
end
108+
}
109+
110+
di.bin_length += edata.ptr - before_ptr
111+
112+
return if edata.ptr > edata.length
113+
114+
di
115+
end
116+
117+
def decode_instr_interpret(di, addr)
118+
if di.opcode.props[:setip] and di.opcode.name =~ /^j/
119+
delta = di.instruction.args.last.reduce
120+
arg = Expression[[addr, :+, di.bin_length], :+, delta].reduce
121+
di.instruction.args[-1] = Expression[arg]
122+
end
123+
124+
di
125+
end
126+
127+
def backtrace_binding
128+
@backtrace_binding ||= init_backtrace_binding
129+
end
130+
131+
def init_backtrace_binding
132+
@backtrace_binding ||= {}
133+
134+
opcode_list.map { |ol| ol.name }.uniq.each { |op|
135+
@backtrace_binding[op] ||= case op
136+
when 'mov'; lambda { |di, a0, a1| { a0 => Expression[a1] }}
137+
when 'cmp', 'test'; lambda { |di, *a| {} } # TODO
138+
when 'add', 'adc' ; lambda { |di, a0, a1| { a0 => Expression[a0, :+, a1] } }
139+
when 'sub', 'sbc'; lambda { |di, a0, a1| { a0 => Expression[a0, :-, a1] } }
140+
when 'and'; lambda { |di, a0, a1| { a0 => Expression[a0, :&, a1] } }
141+
when 'or'; lambda { |di, a0, a1| { a0 => Expression[a0, :|, a1] } }
142+
when 'xor'; lambda { |di, a0, a1| { a0 => Expression[a0, :^, a1] } }
143+
when 'push'; lambda { |di, a0| { Indirection[:sp, 2] => Expression[a0],
144+
:sp => Expression[:sp, :-, 2] } }
145+
when 'call'; lambda { |di, a0| { Indirection[:sp, 2] => Expression[di.next_addr],
146+
:sp => Expression[:sp, :-, 2] } }
147+
when 'pop'; lambda { |di, a0| { a0 => Expression[Indirection[:sp, 2]],
148+
:sp => Expression[:sp, :+, 2] } }
149+
when 'ret'; lambda { |di| { :sp => Expression[:sp, :+, 2] } }
150+
when 'reti'; lambda { |di| { :sp => Expression[:sp, :+, 4] } }
151+
when /^j/; lambda { |di, a0| {} }
152+
end
153+
}
154+
155+
@backtrace_binding
156+
end
157+
158+
def get_backtrace_binding(di)
159+
a = di.instruction.args.map { |arg|
160+
case arg
161+
when Reg; arg.symbolic
162+
when Memref; arg.symbolic(di.address)
163+
else arg
164+
end
165+
}
166+
167+
if binding = backtrace_binding[di.opcode.basename]
168+
bd = binding[di, *a] || {}
169+
di.instruction.args.grep(Memref).each { |m|
170+
next unless r = m.base and m.postincr
171+
r = m.base.symbolic
172+
bd[r] ||= Expression[r, :+, m.size]
173+
}
174+
bd
175+
else
176+
puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
177+
{ :incomplete_binding => Expression[1] }
178+
end
179+
end
180+
181+
def get_xrefs_x(dasm, di)
182+
return [] if not di.opcode.props[:setip]
183+
184+
case di.instruction.opname
185+
when 'ret'
186+
return [Indirection[:sp, 2, di.address]]
187+
when 'reti'
188+
return [Indirection[[:sp, :+, 2], 2, di.address]]
189+
end
190+
191+
# XXX add pc, 42 ?
192+
val = di.instruction.args[0]
193+
case val
194+
when Reg; val = val.symbolic
195+
when Memref; val = val.symbolic(di.address)
196+
end
197+
198+
[Expression[val]]
199+
end
200+
201+
def backtrace_is_function_return(expr, di=nil)
202+
expr = Expression[expr].reduce_rec
203+
expr.kind_of?(Indirection) and expr.len == 2 and expr.target == Expression[:sp]
204+
end
205+
206+
# updates the function backtrace_binding
207+
# if the function is big and no specific register is given, do nothing (the binding will be lazily updated later, on demand)
208+
def backtrace_update_function_binding(dasm, faddr, f, retaddrlist, *wantregs)
209+
b = f.backtrace_binding
210+
211+
bt_val = lambda { |r|
212+
next if not retaddrlist
213+
b[r] = Expression::Unknown
214+
bt = []
215+
retaddrlist.each { |retaddr|
216+
bt |= dasm.backtrace(Expression[r], retaddr, :include_start => true,
217+
:snapshot_addr => faddr, :origin => retaddr)
218+
}
219+
if bt.length != 1
220+
b[r] = Expression::Unknown
221+
else
222+
b[r] = bt.first
223+
end
224+
}
225+
226+
if not wantregs.empty?
227+
wantregs.each(&bt_val)
228+
else
229+
bt_val[:sp]
230+
end
231+
232+
b
233+
end
234+
235+
def replace_instr_arg_immediate(i, old, new)
236+
i.args.map! { |a|
237+
case a
238+
when Expression; a == old ? new : Expression[a.bind(old => new).reduce]
239+
when Memref
240+
a.base = (a.base == old ? new : Expression[a.base.bind(old => new).reduce]) if a.base.kind_of?(Expression)
241+
a
242+
else a
243+
end
244+
}
245+
end
246+
end
247+
end

lib/metasm/metasm/cpu/msp430/main.rb

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# This file is part of Metasm, the Ruby assembly manipulation suite
2+
# Copyright (C) 2006-2010 Yoann GUILLOT
3+
#
4+
# Licence is LGPL, see LICENCE in the top-level directory
5+
6+
require 'metasm/main'
7+
8+
module Metasm
9+
10+
class MSP430 < CPU
11+
def initialize(e = :little)
12+
super()
13+
@endianness = e
14+
@size = 16
15+
end
16+
17+
class Reg
18+
include Renderable
19+
Sym = (4..15).inject(0 => :pc, 1 => :sp, 2 => :flags, 3 => :rzero) { |h, i| h.update i => "r#{i}".to_sym }
20+
21+
attr_accessor :i
22+
def initialize(i) ; @i = i end
23+
def symbolic ; Sym[@i] end
24+
def render ; [Sym[@i].to_s] end
25+
def ==(o) ; o.class == self.class and o.i == @i end
26+
end
27+
28+
class Memref
29+
attr_accessor :base, :offset, :size, :postincr
30+
31+
def initialize(base, offset = 0, size = nil, postincr = false)
32+
@base = base
33+
@offset = Expression[offset]
34+
@size = size
35+
@postincr = postincr
36+
end
37+
38+
def symbolic(orig=nil)
39+
r = @base.symbolic if @base
40+
e = Expression[r, :+, @offset].reduce
41+
Indirection[e, (@size || 1), orig]
42+
end
43+
44+
include Renderable
45+
46+
def render
47+
b = @base
48+
b = @base.to_s + '++' if @base and @postincr
49+
p = Expression[b, :+, @offset].reduce
50+
Indirection[p, @size].render
51+
end
52+
end
53+
54+
def init_opcode_list
55+
init
56+
end
57+
58+
def dbg_register_list
59+
@dbg_register_list ||= Reg::Sym.sort.transpose.last
60+
end
61+
end
62+
end

0 commit comments

Comments
 (0)