metasm 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.hgtags +3 -0
- data/Gemfile +1 -0
- data/INSTALL +61 -0
- data/LICENCE +458 -0
- data/README +29 -21
- data/Rakefile +10 -0
- data/TODO +10 -12
- data/doc/code_organisation.txt +2 -0
- data/doc/core/DynLdr.txt +247 -0
- data/doc/core/ExeFormat.txt +43 -0
- data/doc/core/Expression.txt +220 -0
- data/doc/core/GNUExports.txt +27 -0
- data/doc/core/Ia32.txt +236 -0
- data/doc/core/SerialStruct.txt +108 -0
- data/doc/core/VirtualString.txt +145 -0
- data/doc/core/WindowsExports.txt +61 -0
- data/doc/core/index.txt +1 -0
- data/doc/style.css +6 -3
- data/doc/usage/debugger.txt +327 -0
- data/doc/usage/index.txt +1 -0
- data/doc/use_cases.txt +2 -2
- data/metasm.gemspec +22 -0
- data/{lib/metasm.rb → metasm.rb} +11 -3
- data/{lib/metasm → metasm}/compile_c.rb +13 -7
- data/metasm/cpu/arc.rb +8 -0
- data/metasm/cpu/arc/decode.rb +425 -0
- data/metasm/cpu/arc/main.rb +191 -0
- data/metasm/cpu/arc/opcodes.rb +588 -0
- data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
- data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
- data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
- data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
- data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
- data/metasm/cpu/arm/opcodes.rb +324 -0
- data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
- data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
- data/metasm/cpu/arm64.rb +15 -0
- data/metasm/cpu/arm64/debug.rb +38 -0
- data/metasm/cpu/arm64/decode.rb +289 -0
- data/metasm/cpu/arm64/encode.rb +41 -0
- data/metasm/cpu/arm64/main.rb +105 -0
- data/metasm/cpu/arm64/opcodes.rb +232 -0
- data/metasm/cpu/arm64/parse.rb +20 -0
- data/metasm/cpu/arm64/render.rb +95 -0
- data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
- data/metasm/cpu/bpf/decode.rb +142 -0
- data/metasm/cpu/bpf/main.rb +60 -0
- data/metasm/cpu/bpf/opcodes.rb +81 -0
- data/metasm/cpu/bpf/render.rb +41 -0
- data/metasm/cpu/cy16.rb +9 -0
- data/metasm/cpu/cy16/decode.rb +253 -0
- data/metasm/cpu/cy16/main.rb +63 -0
- data/metasm/cpu/cy16/opcodes.rb +78 -0
- data/metasm/cpu/cy16/render.rb +41 -0
- data/metasm/cpu/dalvik.rb +11 -0
- data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
- data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
- data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
- data/metasm/cpu/ia32.rb +17 -0
- data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
- data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
- data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
- data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
- data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
- data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
- data/metasm/cpu/ia32/opcodes.rb +1424 -0
- data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
- data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
- data/metasm/cpu/mips.rb +14 -0
- data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
- data/metasm/cpu/mips/debug.rb +42 -0
- data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
- data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
- data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
- data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
- data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
- data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
- data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
- data/metasm/cpu/msp430/decode.rb +247 -0
- data/metasm/cpu/msp430/main.rb +62 -0
- data/metasm/cpu/msp430/opcodes.rb +101 -0
- data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
- data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
- data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
- data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
- data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
- data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
- data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
- data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
- data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
- data/metasm/cpu/ppc/parse.rb +55 -0
- data/metasm/cpu/python.rb +8 -0
- data/metasm/cpu/python/decode.rb +136 -0
- data/metasm/cpu/python/main.rb +36 -0
- data/metasm/cpu/python/opcodes.rb +180 -0
- data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
- data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
- data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
- data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
- data/metasm/cpu/x86_64.rb +15 -0
- data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
- data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
- data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
- data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
- data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
- data/metasm/cpu/x86_64/opcodes.rb +136 -0
- data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
- data/metasm/cpu/x86_64/render.rb +35 -0
- data/metasm/cpu/z80.rb +9 -0
- data/metasm/cpu/z80/decode.rb +313 -0
- data/metasm/cpu/z80/main.rb +67 -0
- data/metasm/cpu/z80/opcodes.rb +224 -0
- data/metasm/cpu/z80/render.rb +59 -0
- data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
- data/{lib/metasm → metasm}/decode.rb +35 -4
- data/{lib/metasm → metasm}/decompile.rb +15 -16
- data/{lib/metasm → metasm}/disassemble.rb +201 -45
- data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
- data/{lib/metasm → metasm}/dynldr.rb +220 -133
- data/{lib/metasm → metasm}/encode.rb +10 -1
- data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
- data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
- data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
- data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
- data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
- data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
- data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
- data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
- data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
- data/metasm/exe_format/gb.rb +65 -0
- data/metasm/exe_format/javaclass.rb +424 -0
- data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
- data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
- data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
- data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
- data/metasm/exe_format/pyc.rb +167 -0
- data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
- data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
- data/metasm/exe_format/shellcode_rwx.rb +114 -0
- data/metasm/exe_format/swf.rb +205 -0
- data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
- data/metasm/exe_format/zip.rb +335 -0
- data/metasm/gui.rb +13 -0
- data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
- data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
- data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
- data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
- data/metasm/gui/dasm_graph.rb +1695 -0
- data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
- data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
- data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
- data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
- data/{lib/metasm → metasm}/gui/debug.rb +93 -27
- data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
- data/{lib/metasm → metasm}/gui/qt.rb +12 -2
- data/{lib/metasm → metasm}/gui/win32.rb +179 -42
- data/{lib/metasm → metasm}/gui/x11.rb +59 -59
- data/{lib/metasm → metasm}/main.rb +389 -264
- data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
- data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
- data/{lib/metasm → metasm}/os/linux.rb +628 -151
- data/metasm/os/main.rb +330 -0
- data/{lib/metasm → metasm}/os/windows.rb +132 -42
- data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
- data/{lib/metasm → metasm}/parse.rb +26 -24
- data/{lib/metasm → metasm}/parse_c.rb +221 -116
- data/{lib/metasm → metasm}/preprocessor.rb +55 -40
- data/{lib/metasm → metasm}/render.rb +14 -38
- data/misc/hexdump.rb +2 -1
- data/misc/lint.rb +58 -0
- data/misc/txt2html.rb +9 -7
- data/samples/bindiff.rb +3 -4
- data/samples/dasm-plugins/bindiff.rb +15 -0
- data/samples/dasm-plugins/bookmark.rb +133 -0
- data/samples/dasm-plugins/c_constants.rb +57 -0
- data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
- data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
- data/samples/dasm-plugins/dasm_all.rb +70 -0
- data/samples/dasm-plugins/demangle_cpp.rb +31 -0
- data/samples/dasm-plugins/deobfuscate.rb +251 -0
- data/samples/dasm-plugins/dump_text.rb +35 -0
- data/samples/dasm-plugins/export_graph_svg.rb +86 -0
- data/samples/dasm-plugins/findgadget.rb +75 -0
- data/samples/dasm-plugins/hl_opcode.rb +32 -0
- data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
- data/samples/dasm-plugins/imm2off.rb +34 -0
- data/samples/dasm-plugins/match_libsigs.rb +93 -0
- data/samples/dasm-plugins/patch_file.rb +95 -0
- data/samples/dasm-plugins/scanfuncstart.rb +36 -0
- data/samples/dasm-plugins/scanxrefs.rb +26 -0
- data/samples/dasm-plugins/selfmodify.rb +197 -0
- data/samples/dasm-plugins/stringsxrefs.rb +28 -0
- data/samples/dasmnavig.rb +1 -1
- data/samples/dbg-apihook.rb +24 -9
- data/samples/dbg-plugins/heapscan.rb +283 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
- data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
- data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
- data/samples/dbg-plugins/heapscan/winheap.h +174 -0
- data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
- data/samples/dbg-plugins/trace_func.rb +214 -0
- data/samples/disassemble-gui.rb +35 -5
- data/samples/disassemble.rb +31 -6
- data/samples/dump_upx.rb +24 -12
- data/samples/dynamic_ruby.rb +12 -3
- data/samples/exeencode.rb +6 -5
- data/samples/factorize-headers-peimports.rb +1 -1
- data/samples/lindebug.rb +175 -381
- data/samples/metasm-shell.rb +1 -2
- data/samples/peldr.rb +2 -2
- data/tests/all.rb +1 -1
- data/tests/arc.rb +26 -0
- data/tests/dynldr.rb +22 -4
- data/tests/expression.rb +55 -0
- data/tests/graph_layout.rb +285 -0
- data/tests/ia32.rb +79 -26
- data/tests/mips.rb +9 -2
- data/tests/x86_64.rb +66 -18
- metadata +330 -218
- data/lib/metasm/arm/opcodes.rb +0 -177
- data/lib/metasm/gui.rb +0 -23
- data/lib/metasm/gui/dasm_graph.rb +0 -1354
- data/lib/metasm/ia32.rb +0 -14
- data/lib/metasm/ia32/opcodes.rb +0 -873
- data/lib/metasm/ppc/parse.rb +0 -52
- data/lib/metasm/x86_64.rb +0 -12
- data/lib/metasm/x86_64/opcodes.rb +0 -118
- data/samples/gdbclient.rb +0 -583
- data/samples/rubstop.rb +0 -399
@@ -0,0 +1,67 @@
|
|
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
|
+
|
9
|
+
module Metasm
|
10
|
+
class Z80 < CPU
|
11
|
+
class Reg
|
12
|
+
class << self
|
13
|
+
attr_accessor :s_to_i, :i_to_s
|
14
|
+
end
|
15
|
+
@i_to_s = { 8 => { 0 => 'B', 1 => 'C', 2 => 'D', 3 => 'E',
|
16
|
+
4 => 'H', 5 => 'L', 7 => 'A' },
|
17
|
+
16 => { 0 => 'BC', 1 => 'DE', 2 => 'HL', 3 => 'SP',
|
18
|
+
4 => 'AF' } } # AF is 3 too
|
19
|
+
@s_to_i = @i_to_s.inject({}) { |h, (sz, rh)|
|
20
|
+
h.update rh.inject({}) { |hh, (i, n)|
|
21
|
+
hh.update n => [sz, i] } }
|
22
|
+
|
23
|
+
attr_accessor :sz, :i
|
24
|
+
def initialize(sz, i)
|
25
|
+
@sz = sz
|
26
|
+
@i = i
|
27
|
+
end
|
28
|
+
|
29
|
+
def symbolic(orig=nil) ; to_s.to_sym ; end
|
30
|
+
|
31
|
+
def self.from_str(s)
|
32
|
+
raise "Bad name #{s.inspect}" if not x = @s_to_i[s]
|
33
|
+
new(*x)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Memref
|
38
|
+
attr_accessor :base, :offset, :sz
|
39
|
+
def initialize(base, offset, sz=nil)
|
40
|
+
@base = base
|
41
|
+
offset = Expression[offset] if offset
|
42
|
+
@offset = offset
|
43
|
+
@sz = sz
|
44
|
+
end
|
45
|
+
|
46
|
+
def symbolic(orig)
|
47
|
+
p = nil
|
48
|
+
p = Expression[p, :+, @base.symbolic] if base
|
49
|
+
p = Expression[p, :+, @offset] if offset
|
50
|
+
Indirection[p.reduce, @sz, orig]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def initialize(family = :latest)
|
55
|
+
super()
|
56
|
+
@endianness = :little
|
57
|
+
@size = 16
|
58
|
+
@family = family
|
59
|
+
end
|
60
|
+
|
61
|
+
def init_opcode_list
|
62
|
+
send("init_#@family")
|
63
|
+
@opcode_list
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
@@ -0,0 +1,224 @@
|
|
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
|
+
require 'metasm/cpu/z80/main'
|
7
|
+
|
8
|
+
module Metasm
|
9
|
+
|
10
|
+
class Z80
|
11
|
+
def addop(name, bin, *args)
|
12
|
+
o = Opcode.new name, bin
|
13
|
+
args.each { |a|
|
14
|
+
o.args << a if @fields_mask[a] or @valid_args[a]
|
15
|
+
o.props[a] = true if @valid_props[a]
|
16
|
+
o.fields[a] = [bin.length-1, @fields_shift[a]] if @fields_mask[a]
|
17
|
+
raise "wtf #{a.inspect}" unless @valid_args[a] or @valid_props[a] or @fields_mask[a]
|
18
|
+
}
|
19
|
+
@opcode_list << o
|
20
|
+
end
|
21
|
+
|
22
|
+
def addop_macrocc(name, bin, *args)
|
23
|
+
%w[nz z nc c po pe p m].each_with_index { |cc, i|
|
24
|
+
dbin = bin.dup
|
25
|
+
dbin[0] |= i << 3
|
26
|
+
addop name + cc, dbin, *args
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
# data from http://www.z80.info/decoding.htm
|
31
|
+
def init_z80_common
|
32
|
+
@opcode_list = []
|
33
|
+
@valid_args.update [:i8, :u8, :i16, :u16, :m16,
|
34
|
+
:r_a, :r_af, :r_hl, :r_de, :r_sp, :r_i,
|
35
|
+
:m_bc, :m_de, :m_sp, :m_hl, :mf8, :mfc
|
36
|
+
].inject({}) { |h, v| h.update v => true }
|
37
|
+
@fields_mask.update :rz => 7, :ry => 7, :rp => 3, :rp2 => 3, :iy => 7, :iy8 => 7
|
38
|
+
@fields_shift.update :rz => 0, :ry => 3, :rp => 4, :rp2 => 4, :iy => 3, :iy8 => 3
|
39
|
+
|
40
|
+
# some opcodes are in init_z80 when they are not part of the GB ABI
|
41
|
+
addop 'nop', [0b00_000_000]
|
42
|
+
addop 'jr', [0b00_011_000], :setip, :stopexec, :i8
|
43
|
+
%w[nz z nc c].each_with_index { |cc, i|
|
44
|
+
addop 'jr' + cc, [0b00_100_000 | (i << 3)], :setip, :i8
|
45
|
+
}
|
46
|
+
addop 'ld', [0b00_000_001], :rp, :i16
|
47
|
+
addop 'add', [0b00_001_001], :r_hl, :rp
|
48
|
+
|
49
|
+
addop 'ld', [0b00_000_010], :m_bc, :r_a
|
50
|
+
addop 'ld', [0b00_001_010], :r_a, :m_bc
|
51
|
+
addop 'ld', [0b00_010_010], :m_de, :r_a
|
52
|
+
addop 'ld', [0b00_011_010], :r_a, :m_de
|
53
|
+
|
54
|
+
addop 'inc', [0b00_000_011], :rp
|
55
|
+
addop 'dec', [0b00_001_011], :rp
|
56
|
+
addop 'inc', [0b00_000_100], :ry
|
57
|
+
addop 'dec', [0b00_000_101], :ry
|
58
|
+
addop 'ld', [0b00_000_110], :ry, :i8
|
59
|
+
|
60
|
+
addop 'rlca', [0b00_000_111] # rotate
|
61
|
+
addop 'rrca', [0b00_001_111]
|
62
|
+
addop 'rla', [0b00_010_111]
|
63
|
+
addop 'rra', [0b00_011_111]
|
64
|
+
|
65
|
+
addop 'daa', [0b00_100_111]
|
66
|
+
addop 'cpl', [0b00_101_111]
|
67
|
+
addop 'scf', [0b00_110_111]
|
68
|
+
addop 'ccf', [0b00_111_111]
|
69
|
+
|
70
|
+
addop 'halt', [0b01_110_110] # ld (HL), (HL)
|
71
|
+
addop 'ld', [0b01_000_000], :ry, :rz
|
72
|
+
|
73
|
+
addop 'add', [0b10_000_000], :r_a, :rz
|
74
|
+
addop 'adc', [0b10_001_000], :r_a, :rz
|
75
|
+
addop 'sub', [0b10_010_000], :r_a, :rz
|
76
|
+
addop 'sbc', [0b10_011_000], :r_a, :rz
|
77
|
+
addop 'and', [0b10_100_000], :r_a, :rz
|
78
|
+
addop 'xor', [0b10_101_000], :r_a, :rz
|
79
|
+
addop 'or', [0b10_110_000], :r_a, :rz
|
80
|
+
addop 'cmp', [0b10_111_000], :r_a, :rz # alias cp
|
81
|
+
addop 'cp', [0b10_111_000], :r_a, :rz # compare
|
82
|
+
|
83
|
+
addop_macrocc 'ret', [0b11_000_000], :setip
|
84
|
+
addop 'pop', [0b11_000_001], :rp2
|
85
|
+
addop 'ret', [0b11_001_001], :stopexec, :setip
|
86
|
+
addop 'jmp', [0b11_101_001], :r_hl, :setip, :stopexec # alias jp
|
87
|
+
addop 'jp', [0b11_101_001], :r_hl, :setip, :stopexec
|
88
|
+
addop 'ld', [0b11_111_001], :r_sp, :r_hl
|
89
|
+
addop_macrocc 'j', [0b11_000_010], :setip, :u16 # alias jp
|
90
|
+
addop_macrocc 'jp', [0b11_000_010], :setip, :u16
|
91
|
+
addop 'jmp', [0b11_000_011], :setip, :stopexec, :u16 # alias jp
|
92
|
+
addop 'jp', [0b11_000_011], :setip, :stopexec, :u16
|
93
|
+
|
94
|
+
addop 'di', [0b11_110_011] # disable interrupts
|
95
|
+
addop 'ei', [0b11_111_011]
|
96
|
+
addop_macrocc 'call', [0b11_000_100], :u16, :setip, :saveip
|
97
|
+
addop 'push', [0b11_000_101], :rp2
|
98
|
+
addop 'call', [0b11_001_101], :u16, :setip, :saveip, :stopexec
|
99
|
+
|
100
|
+
addop 'add', [0b11_000_110], :r_a, :i8
|
101
|
+
addop 'adc', [0b11_001_110], :r_a, :i8
|
102
|
+
addop 'sub', [0b11_010_110], :r_a, :i8
|
103
|
+
addop 'sbc', [0b11_011_110], :r_a, :i8
|
104
|
+
addop 'and', [0b11_100_110], :r_a, :i8
|
105
|
+
addop 'xor', [0b11_101_110], :r_a, :i8
|
106
|
+
addop 'or', [0b11_110_110], :r_a, :i8
|
107
|
+
addop 'cp', [0b11_111_110], :r_a, :i8
|
108
|
+
|
109
|
+
addop 'rst', [0b11_000_111], :iy8 # call off in page 0
|
110
|
+
|
111
|
+
addop 'rlc', [0xCB, 0b00_000_000], :rz # rotate
|
112
|
+
addop 'rrc', [0xCB, 0b00_001_000], :rz
|
113
|
+
addop 'rl', [0xCB, 0b00_010_000], :rz
|
114
|
+
addop 'rr', [0xCB, 0b00_011_000], :rz
|
115
|
+
addop 'sla', [0xCB, 0b00_100_000], :rz # shift
|
116
|
+
addop 'sra', [0xCB, 0b00_101_000], :rz
|
117
|
+
addop 'srl', [0xCB, 0b00_111_000], :rz
|
118
|
+
addop 'bit', [0xCB, 0b01_000_000], :iy, :rz # bit test
|
119
|
+
addop 'res', [0xCB, 0b10_000_000], :iy, :rz # bit reset
|
120
|
+
addop 'set', [0xCB, 0b11_000_000], :iy, :rz # bit set
|
121
|
+
end
|
122
|
+
|
123
|
+
# standard z80
|
124
|
+
def init_z80
|
125
|
+
init_z80_common
|
126
|
+
|
127
|
+
addop 'ex', [0b00_001_000], :r_af # XXX really ex AF, AF' ...
|
128
|
+
addop 'djnz', [0b00_010_000], :setip, :i8
|
129
|
+
|
130
|
+
addop 'ld', [0b00_100_010], :m16, :r_hl
|
131
|
+
addop 'ld', [0b00_101_010], :r_hl, :m16
|
132
|
+
addop 'ld', [0b00_110_010], :m16, :r_a
|
133
|
+
addop 'ld', [0b00_111_010], :r_a, :m16
|
134
|
+
|
135
|
+
addop 'exx', [0b11_011_001]
|
136
|
+
addop 'out', [0b11_010_011], :i8, :r_a
|
137
|
+
addop 'in', [0b11_011_011], :r_a, :i8
|
138
|
+
|
139
|
+
addop 'ex', [0b11_100_011], :m_sp, :r_hl
|
140
|
+
addop 'ex', [0b11_101_011], :r_de, :r_hl
|
141
|
+
|
142
|
+
addop 'sll', [0xCB, 0b00_110_000], :rz
|
143
|
+
|
144
|
+
addop 'in', [0xED, 0b01_110_000], :u16
|
145
|
+
addop 'in', [0xED, 0b01_000_000], :ry, :u16
|
146
|
+
addop 'out', [0xED, 0b01_110_001], :u16
|
147
|
+
addop 'out', [0xED, 0b01_000_001], :u16, :ry
|
148
|
+
addop 'sbc', [0xED, 0b01_000_010], :r_hl, :rp
|
149
|
+
addop 'adc', [0xED, 0b01_001_010], :r_hl, :rp
|
150
|
+
addop 'ld', [0xED, 0b01_000_011], :m16, :rp
|
151
|
+
addop 'ld', [0xED, 0b01_001_011], :rp, :m16
|
152
|
+
addop 'neg', [0xED, 0b01_000_100], :r_a, :iy # dummy int field
|
153
|
+
addop 'retn', [0xED, 0b01_000_101], :stopexec # dummy int != 1 ? (1 = reti)
|
154
|
+
addop 'reti', [0xED, 0b01_001_101], :stopexec, :setip
|
155
|
+
addop 'im', [0xED, 0b01_000_110], :iy
|
156
|
+
addop 'ld', [0xED, 0b01_000_111], :r_i, :r_a
|
157
|
+
addop 'ld', [0xED, 0b01_001_111], :r_r, :r_a
|
158
|
+
addop 'ld', [0xED, 0b01_010_111], :r_a, :r_i
|
159
|
+
addop 'ld', [0xED, 0b01_011_111], :r_a, :r_r
|
160
|
+
addop 'rrd', [0xED, 0b01_100_111]
|
161
|
+
addop 'rld', [0xED, 0b01_101_111]
|
162
|
+
|
163
|
+
addop 'ldi', [0xED, 0b10_100_000]
|
164
|
+
addop 'ldd', [0xED, 0b10_101_000]
|
165
|
+
addop 'ldir', [0xED, 0b10_110_000]
|
166
|
+
addop 'lddr', [0xED, 0b10_111_000]
|
167
|
+
addop 'cpi', [0xED, 0b10_100_001]
|
168
|
+
addop 'cpd', [0xED, 0b10_101_001]
|
169
|
+
addop 'cpir', [0xED, 0b10_110_001]
|
170
|
+
addop 'cpdr', [0xED, 0b10_111_001]
|
171
|
+
addop 'ini', [0xED, 0b10_100_010]
|
172
|
+
addop 'ind', [0xED, 0b10_101_010]
|
173
|
+
addop 'inir', [0xED, 0b10_110_010]
|
174
|
+
addop 'indr', [0xED, 0b10_111_010]
|
175
|
+
addop 'outi', [0xED, 0b10_100_011]
|
176
|
+
addop 'outd', [0xED, 0b10_101_011]
|
177
|
+
addop 'otir', [0xED, 0b10_110_011]
|
178
|
+
addop 'otdr', [0xED, 0b10_111_011]
|
179
|
+
|
180
|
+
addop 'unk_ed', [0xED], :i8
|
181
|
+
|
182
|
+
addop 'unk_nop', [], :i8 # undefined opcode = nop
|
183
|
+
@unknown_opcode = @opcode_list.last
|
184
|
+
end
|
185
|
+
|
186
|
+
# gameboy processor
|
187
|
+
# from http://nocash.emubase.de/pandocs.htm#cpucomparisionwithz80
|
188
|
+
def init_gb
|
189
|
+
init_z80_common
|
190
|
+
|
191
|
+
addop 'ld', [0x08], :m16, :r_sp
|
192
|
+
addop 'stop', [0x10]
|
193
|
+
|
194
|
+
addop 'ldi', [0x22], :m_hl, :r_a # (hl++) <- a
|
195
|
+
addop 'ldi', [0x2A], :r_a, :m_hl
|
196
|
+
addop 'ldd', [0x32], :m_hl, :r_a # (hl--) <- a
|
197
|
+
addop 'ldd', [0x3A], :r_a, :m_hl
|
198
|
+
|
199
|
+
addop 'reti', [0xD9], :setip, :stopexec
|
200
|
+
|
201
|
+
# override retpo/jpo
|
202
|
+
@opcode_list.delete_if { |op| op.bin[0] & 0xE5 == 0xE0 } # rm E0 E2 E8 EA F0 F2 F8 FA
|
203
|
+
addop 'ld', [0xE0], :mf8, :r_a # (0xff00 + :i8)
|
204
|
+
addop 'ld', [0xE2], :mfc, :r_a # (0xff00 + :r_c)
|
205
|
+
addop 'add', [0xE8], :r_sp, :i8
|
206
|
+
addop 'ld', [0xEA], :m16, :r_a
|
207
|
+
addop 'ld', [0xF0], :r_a, :mf8
|
208
|
+
addop 'ld', [0xF2], :r_a, :mfc
|
209
|
+
addop 'ld', [0xF8], :r_hl, :r_sp, :i8 # hl <- sp+:i8
|
210
|
+
addop 'ld', [0xFA], :r_a, :m16
|
211
|
+
|
212
|
+
addop 'swap', [0xCB, 0x30], :rz
|
213
|
+
|
214
|
+
addop 'inv_dd', [0xDD], :stopexec # invalid prefixes
|
215
|
+
addop 'inv_ed', [0xED], :stopexec
|
216
|
+
addop 'inv_fd', [0xFD], :stopexec
|
217
|
+
|
218
|
+
addop 'unk_nop', [], :i8 # undefined opcode = nop
|
219
|
+
@unknown_opcode = @opcode_list.last
|
220
|
+
end
|
221
|
+
|
222
|
+
alias init_latest init_z80
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,59 @@
|
|
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/cpu/z80/opcodes'
|
8
|
+
require 'metasm/render'
|
9
|
+
|
10
|
+
module Metasm
|
11
|
+
class Z80
|
12
|
+
class Reg
|
13
|
+
include Renderable
|
14
|
+
def render ; [self.class.i_to_s[@sz][@i]] end
|
15
|
+
end
|
16
|
+
class Memref
|
17
|
+
include Renderable
|
18
|
+
def render
|
19
|
+
r = ['(']
|
20
|
+
r << @base if @base
|
21
|
+
r << '+' if @base and @offset
|
22
|
+
r << @offset if @offset
|
23
|
+
r << ')'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def render_instruction(i)
|
28
|
+
r = []
|
29
|
+
r << i.opname
|
30
|
+
if not i.args.empty?
|
31
|
+
r << ' '
|
32
|
+
i.args.each { |a_| r << a_ << ', ' }
|
33
|
+
r.pop
|
34
|
+
end
|
35
|
+
r
|
36
|
+
end
|
37
|
+
|
38
|
+
def gui_hilight_word_regexp_init
|
39
|
+
ret = {}
|
40
|
+
|
41
|
+
# { 'B' => 'B|BC', 'BC' => 'B|C|BC' }
|
42
|
+
|
43
|
+
%w[BC DE HL].each { |w|
|
44
|
+
l0, l1 = w.split(//)
|
45
|
+
ret[l0] = "#{l0}#{l1}?"
|
46
|
+
ret[l1] = "#{l0}?#{l1}"
|
47
|
+
ret[w] = "#{l0}|#{l0}?#{l1}"
|
48
|
+
}
|
49
|
+
|
50
|
+
ret
|
51
|
+
end
|
52
|
+
|
53
|
+
def gui_hilight_word_regexp(word)
|
54
|
+
@gui_hilight_word_hash ||= gui_hilight_word_regexp_init
|
55
|
+
@gui_hilight_word_hash[word] or super(word)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -3,312 +3,7 @@
|
|
3
3
|
#
|
4
4
|
# Licence is LGPL, see LICENCE in the top-level directory
|
5
5
|
|
6
|
-
require 'metasm/main'
|
7
|
-
|
8
6
|
module Metasm
|
9
|
-
# this module regroups OS-related functions
|
10
|
-
# (eg. find_process, inject_shellcode)
|
11
|
-
# a 'class' just to be able to inherit from it...
|
12
|
-
class OS
|
13
|
-
# represents a running process with a few information, and defines methods to get more interaction (#memory, #debugger)
|
14
|
-
class Process
|
15
|
-
attr_accessor :pid, :path, :modules
|
16
|
-
class Module
|
17
|
-
attr_accessor :path, :addr, :size
|
18
|
-
end
|
19
|
-
|
20
|
-
def initialize(pid=nil)
|
21
|
-
@pid = pid
|
22
|
-
end
|
23
|
-
|
24
|
-
def to_s
|
25
|
-
mod = File.basename(path) rescue nil
|
26
|
-
"#{pid}: ".ljust(6) << (mod || '<unknown>')
|
27
|
-
end
|
28
|
-
def inspect
|
29
|
-
'<Process:' + ["pid: #@pid", modules.to_a.map { |m| " #{'%X' % m.addr} #{m.path}" }].join("\n") + '>'
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
# returns the Process whose pid is name (if name is an Integer) or first module path includes name (string)
|
34
|
-
def self.find_process(name)
|
35
|
-
case name
|
36
|
-
when nil
|
37
|
-
when Integer
|
38
|
-
list_processes.find { |pr| pr.pid == name }
|
39
|
-
else
|
40
|
-
list_processes.find { |pr| pr.path.to_s.include? name.to_s } or
|
41
|
-
(find_process(Integer(name)) if name =~ /^(0x[0-9a-f]+|[0-9]+)$/i)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
# create a new debuggee process stopped at start
|
46
|
-
def self.create_process(path)
|
47
|
-
dbg = create_debugger(path)
|
48
|
-
pr = open_process(dbg.pid)
|
49
|
-
pr.debugger = dbg
|
50
|
-
pr.memory = dbg.memory
|
51
|
-
pr
|
52
|
-
end
|
53
|
-
|
54
|
-
# return the platform-specific version
|
55
|
-
def self.current
|
56
|
-
case RUBY_PLATFORM
|
57
|
-
when /mswin|mingw|cygwin/i; WinOS
|
58
|
-
when /linux/i; LinOS
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# This class implements an objects that behaves like a regular string, but
|
64
|
-
# whose real data is dynamically fetched or generated on demand
|
65
|
-
# its size is immutable
|
66
|
-
# implements a page cache
|
67
|
-
# substrings are Strings (small substring) or another VirtualString
|
68
|
-
# (a kind of 'window' on the original VString, when the substring length is > 4096)
|
69
|
-
class VirtualString
|
70
|
-
# formats parameters for reading
|
71
|
-
def [](from, len=nil)
|
72
|
-
if not len and from.kind_of? Range
|
73
|
-
b = from.begin
|
74
|
-
e = from.end
|
75
|
-
b = b + length if b < 0
|
76
|
-
e = e + length if e < 0
|
77
|
-
len = e - b
|
78
|
-
len += 1 if not from.exclude_end?
|
79
|
-
from = b
|
80
|
-
end
|
81
|
-
from = from + length if from < 0
|
82
|
-
|
83
|
-
return nil if from > length or (from == length and not len)
|
84
|
-
len = length - from if len and from + len > length
|
85
|
-
return '' if len == 0
|
86
|
-
|
87
|
-
read_range(from, len)
|
88
|
-
end
|
89
|
-
|
90
|
-
# formats parameters for overwriting portion of the string
|
91
|
-
def []=(from, len, val=nil)
|
92
|
-
raise TypeError, 'cannot modify frozen virtualstring' if frozen?
|
93
|
-
|
94
|
-
if not val
|
95
|
-
val = len
|
96
|
-
len = nil
|
97
|
-
end
|
98
|
-
if not len and from.kind_of? Range
|
99
|
-
b = from.begin
|
100
|
-
e = from.end
|
101
|
-
b = b + length if b < 0
|
102
|
-
e = e + length if e < 0
|
103
|
-
len = e - b
|
104
|
-
len += 1 if not from.exclude_end?
|
105
|
-
from = b
|
106
|
-
elsif not len
|
107
|
-
len = 1
|
108
|
-
val = val.chr
|
109
|
-
end
|
110
|
-
from = from + length if from < 0
|
111
|
-
|
112
|
-
raise IndexError, 'Index out of string' if from > length
|
113
|
-
raise IndexError, 'Cannot modify virtualstring length' if val.length != len or from + len > length
|
114
|
-
|
115
|
-
write_range(from, val)
|
116
|
-
end
|
117
|
-
|
118
|
-
# returns the full raw data
|
119
|
-
def realstring
|
120
|
-
ret = ''
|
121
|
-
addr = 0
|
122
|
-
len = length
|
123
|
-
while len > @pagelength
|
124
|
-
ret << self[addr, @pagelength]
|
125
|
-
addr += @pagelength
|
126
|
-
len -= @pagelength
|
127
|
-
end
|
128
|
-
ret << self[addr, len]
|
129
|
-
end
|
130
|
-
|
131
|
-
# alias to realstring
|
132
|
-
# for bad people checking respond_to? :to_str (like String#<<)
|
133
|
-
# XXX alias does not work (not virtual (a la C++))
|
134
|
-
def to_str
|
135
|
-
realstring
|
136
|
-
end
|
137
|
-
|
138
|
-
# forwards unhandled messages to a frozen realstring
|
139
|
-
def method_missing(m, *args, &b)
|
140
|
-
if ''.respond_to? m
|
141
|
-
puts "Using VirtualString.realstring for #{m} from:", caller if $DEBUG
|
142
|
-
realstring.freeze.send(m, *args, &b)
|
143
|
-
else
|
144
|
-
super(m, *args, &b)
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
# avoid triggering realstring from method_missing if possible
|
149
|
-
def empty?
|
150
|
-
length == 0
|
151
|
-
end
|
152
|
-
|
153
|
-
# avoid triggering realstring from method_missing if possible
|
154
|
-
# heavily used in to find 0-terminated strings in ExeFormats
|
155
|
-
def index(chr, base=0)
|
156
|
-
return if base >= length or base <= -length
|
157
|
-
if i = self[base, 64].index(chr) or i = self[base, @pagelength].index(chr)
|
158
|
-
base + i
|
159
|
-
else
|
160
|
-
realstring.index(chr, base)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
# '=~' does not go through method_missing
|
165
|
-
def =~(o)
|
166
|
-
realstring =~ o
|
167
|
-
end
|
168
|
-
|
169
|
-
# implements a read page cache
|
170
|
-
|
171
|
-
# the real address of our first byte
|
172
|
-
attr_accessor :addr_start
|
173
|
-
# our length
|
174
|
-
attr_accessor :length
|
175
|
-
# array of [addr, raw data], sorted by first == last accessed
|
176
|
-
attr_accessor :pagecache
|
177
|
-
# maximum length of self.pagecache (number of cached pages)
|
178
|
-
attr_accessor :pagecache_len
|
179
|
-
def initialize(addr_start, length)
|
180
|
-
@addr_start = addr_start
|
181
|
-
@length = length
|
182
|
-
@pagecache = []
|
183
|
-
@pagecache_len = 4
|
184
|
-
@pagelength ||= 4096 # must be (1 << x)
|
185
|
-
end
|
186
|
-
|
187
|
-
# returns wether a page is valid or not
|
188
|
-
def page_invalid?(addr)
|
189
|
-
cache_get_page(@addr_start+addr)[2]
|
190
|
-
end
|
191
|
-
|
192
|
-
# invalidates the page cache
|
193
|
-
def invalidate
|
194
|
-
@pagecache.clear
|
195
|
-
end
|
196
|
-
|
197
|
-
# returns the @pagelength-bytes page starting at addr
|
198
|
-
# return nil if the page is invalid/inaccessible
|
199
|
-
# addr is page-aligned by the caller
|
200
|
-
# addr is absolute
|
201
|
-
#def get_page(addr, len=@pagelength)
|
202
|
-
#end
|
203
|
-
|
204
|
-
# searches the cache for a page containing addr, updates if not found
|
205
|
-
def cache_get_page(addr)
|
206
|
-
addr &= ~(@pagelength-1)
|
207
|
-
i = 0
|
208
|
-
@pagecache.each { |c|
|
209
|
-
if addr == c[0]
|
210
|
-
# most recently used first
|
211
|
-
@pagecache.unshift @pagecache.delete_at(i) if i != 0
|
212
|
-
return c
|
213
|
-
end
|
214
|
-
i += 1
|
215
|
-
}
|
216
|
-
@pagecache.pop if @pagecache.length >= @pagecache_len
|
217
|
-
c = [addr]
|
218
|
-
p = get_page(addr)
|
219
|
-
c << p.to_s.ljust(@pagelength, "\0")
|
220
|
-
c << true if not p
|
221
|
-
@pagecache.unshift c
|
222
|
-
c
|
223
|
-
end
|
224
|
-
|
225
|
-
# reads a range from the page cache
|
226
|
-
# returns a new VirtualString (using dup) if the request is bigger than @pagelength bytes
|
227
|
-
def read_range(from, len)
|
228
|
-
from += @addr_start
|
229
|
-
if not len
|
230
|
-
base, page = cache_get_page(from)
|
231
|
-
page[from - base]
|
232
|
-
elsif len <= @pagelength
|
233
|
-
base, page = cache_get_page(from)
|
234
|
-
s = page[from - base, len]
|
235
|
-
if from+len-base > @pagelength # request crosses a page boundary
|
236
|
-
base, page = cache_get_page(from+len)
|
237
|
-
s << page[0, from+len-base]
|
238
|
-
end
|
239
|
-
s
|
240
|
-
else
|
241
|
-
# big request: return a new virtual page
|
242
|
-
dup(from, len)
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
# rewrites a segment of data
|
247
|
-
# the length written is the length of the content (a VirtualString cannot grow/shrink)
|
248
|
-
def write_range(from, content)
|
249
|
-
invalidate
|
250
|
-
rewrite_at(from + @addr_start, content)
|
251
|
-
end
|
252
|
-
|
253
|
-
# overwrites a section of the original data
|
254
|
-
#def rewrite_at(addr, content)
|
255
|
-
#end
|
256
|
-
end
|
257
|
-
|
258
|
-
# on-demand reading of a file
|
259
|
-
class VirtualFile < VirtualString
|
260
|
-
# returns a new VirtualFile of the whole file content (defaults readonly)
|
261
|
-
# returns a String if the file is small (<4096o) and readonly access
|
262
|
-
def self.read(path, mode='rb')
|
263
|
-
raise 'no filename specified' if not path
|
264
|
-
if sz = File.size(path) <= 4096 and (mode == 'rb' or mode == 'r')
|
265
|
-
File.open(path, mode) { |fd| fd.read }
|
266
|
-
else
|
267
|
-
File.open(path, mode) { |fd| new fd, 0, sz }
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
# the underlying file descriptor
|
272
|
-
attr_accessor :fd
|
273
|
-
|
274
|
-
# creates a new virtual mapping of a section of the file
|
275
|
-
# the file descriptor must be seekable
|
276
|
-
def initialize(fd, addr_start = 0, length = nil)
|
277
|
-
@fd = fd.dup
|
278
|
-
if not length
|
279
|
-
@fd.seek(0, File::SEEK_END)
|
280
|
-
length = @fd.tell - addr_start
|
281
|
-
end
|
282
|
-
super(addr_start, length)
|
283
|
-
end
|
284
|
-
|
285
|
-
def dup(addr = @addr_start, len = @length)
|
286
|
-
self.class.new(@fd, addr, len)
|
287
|
-
end
|
288
|
-
|
289
|
-
# reads an aligned page from the file, at file offset addr
|
290
|
-
def get_page(addr, len=@pagelength)
|
291
|
-
@fd.pos = addr
|
292
|
-
@fd.read len
|
293
|
-
end
|
294
|
-
|
295
|
-
def page_invalid?(addr)
|
296
|
-
false
|
297
|
-
end
|
298
|
-
|
299
|
-
# overwrite a section of the file
|
300
|
-
def rewrite_at(addr, data)
|
301
|
-
@fd.pos = addr
|
302
|
-
@fd.write data
|
303
|
-
end
|
304
|
-
|
305
|
-
# returns the full content of the file
|
306
|
-
def realstring
|
307
|
-
@fd.pos = @addr_start
|
308
|
-
@fd.read(@length)
|
309
|
-
end
|
310
|
-
end
|
311
|
-
|
312
7
|
# this class implements a high-level debugging API (abstract superclass)
|
313
8
|
class Debugger
|
314
9
|
class Breakpoint
|
@@ -319,7 +14,7 @@ class Debugger
|
|
319
14
|
:oneshot,
|
320
15
|
# current bp state: :active, :inactive (internal use), :disabled (user-specified)
|
321
16
|
:state,
|
322
|
-
# type: type of breakpoint (:bpx = soft, :
|
17
|
+
# type: type of breakpoint (:bpx = soft, :hwbp = hard, :bpm = memory)
|
323
18
|
:type,
|
324
19
|
# Expression if this is a conditionnal bp
|
325
20
|
# may be a Proc, String or Expression, evaluated every time the breakpoint hits
|
@@ -329,6 +24,7 @@ class Debugger
|
|
329
24
|
:action,
|
330
25
|
# Proc to run to emulate the overwritten instr behavior
|
331
26
|
# used to avoid unset/singlestep/re-set, more multithread friendly
|
27
|
+
# may be a DecodedInstruction for lazy initialization, see Debugger#init_bpx/has_emul_instr(bpx)
|
332
28
|
:emul_instr,
|
333
29
|
# internal data, cpu-specific (overwritten byte for a softbp, memory type/size for hwbp..)
|
334
30
|
:internal,
|
@@ -383,7 +79,7 @@ class Debugger
|
|
383
79
|
return del_bpm if @type == :bpm
|
384
80
|
@hash_shared.delete self
|
385
81
|
if @hash_shared.empty?
|
386
|
-
|
82
|
+
@hash_owner.delete @hash_key
|
387
83
|
elsif @hash_owner[@hash_key] == self
|
388
84
|
@hash_owner[@hash_key] = @hash_shared.first
|
389
85
|
end
|
@@ -432,7 +128,7 @@ class Debugger
|
|
432
128
|
|
433
129
|
# global debugger callbacks, called whenever such event occurs
|
434
130
|
attr_accessor :callback_singlestep, :callback_bpx, :callback_hwbp, :callback_bpm,
|
435
|
-
|
131
|
+
:callback_exception, :callback_newthread, :callback_endthread,
|
436
132
|
:callback_newprocess, :callback_endprocess, :callback_loadlibrary
|
437
133
|
|
438
134
|
# global switches, specify wether to break on exception/thread event
|
@@ -456,15 +152,17 @@ class Debugger
|
|
456
152
|
@pid_stuff_list = [:memory, :cpu, :disassembler, :symbols, :symbols_len,
|
457
153
|
:modulemap, :breakpoint, :breakpoint_memory, :tid, :tid_stuff,
|
458
154
|
:dead_process]
|
459
|
-
@tid_stuff_list = [:state, :info, :breakpoint_thread, :singlestep_cb,
|
155
|
+
@tid_stuff_list = [:state, :info, :breakpoint_thread, :singlestep_cb,
|
460
156
|
:run_method, :run_args, :breakpoint_cause, :dead_thread]
|
461
157
|
@callback_loadlibrary = lambda { |h| loadsyms(h[:address]) ; continue }
|
462
|
-
@callback_newprocess = lambda { |h| log "process #{@pid}
|
158
|
+
@callback_newprocess = lambda { |h| log "process #{@pid} attached" }
|
463
159
|
@callback_endprocess = lambda { |h| log "process #{@pid} died" }
|
464
160
|
initialize_newpid
|
465
161
|
initialize_newtid
|
466
162
|
end
|
467
163
|
|
164
|
+
def dasm; disassembler; end
|
165
|
+
|
468
166
|
def shortname; self.class.name.split('::').last.downcase; end
|
469
167
|
|
470
168
|
attr_reader :pid
|
@@ -582,21 +280,27 @@ class Debugger
|
|
582
280
|
end
|
583
281
|
|
584
282
|
# delete references to the current thread
|
585
|
-
# calls del_pid if no tid left
|
586
283
|
def del_tid
|
587
284
|
@tid_stuff.delete @tid
|
588
285
|
if @tid = @tid_stuff.keys.first
|
589
286
|
swapin_tid
|
590
287
|
else
|
591
|
-
|
288
|
+
del_tid_notid
|
592
289
|
end
|
593
290
|
end
|
594
291
|
|
292
|
+
# wipe the whole process when no TID is left
|
293
|
+
# XXX we may have a pending evt_newthread...
|
294
|
+
def del_tid_notid
|
295
|
+
del_pid
|
296
|
+
end
|
297
|
+
|
298
|
+
|
595
299
|
# change the debugger to a specific pid/tid
|
596
300
|
# if given a block, run the block and then restore the original pid/tid
|
597
301
|
# pid may be an object that respond to #pid/#tid
|
598
|
-
def switch_context(npid, ntid=nil)
|
599
|
-
if npid.respond_to?
|
302
|
+
def switch_context(npid, ntid=nil, &b)
|
303
|
+
if npid.respond_to?(:pid)
|
600
304
|
ntid ||= npid.tid
|
601
305
|
npid = npid.pid
|
602
306
|
end
|
@@ -604,12 +308,12 @@ class Debugger
|
|
604
308
|
oldtid = tid
|
605
309
|
set_pid npid
|
606
310
|
set_tid ntid if ntid
|
607
|
-
if
|
311
|
+
if b
|
608
312
|
# shortcut begin..ensure overhead
|
609
|
-
return
|
313
|
+
return b.call if oldpid == pid and oldtid == tid
|
610
314
|
|
611
315
|
begin
|
612
|
-
|
316
|
+
b.call
|
613
317
|
ensure
|
614
318
|
set_pid oldpid
|
615
319
|
set_tid oldtid
|
@@ -619,31 +323,31 @@ class Debugger
|
|
619
323
|
alias set_context switch_context
|
620
324
|
|
621
325
|
# iterate over all pids, yield in the context of this pid
|
622
|
-
def each_pid
|
326
|
+
def each_pid(&b)
|
623
327
|
# ensure @pid is last, so that we finish in the current context
|
624
328
|
lst = @pid_stuff.keys - [@pid]
|
625
329
|
lst << @pid
|
626
|
-
return lst if not
|
330
|
+
return lst if not b
|
627
331
|
lst.each { |p|
|
628
332
|
set_pid p
|
629
|
-
|
333
|
+
b.call
|
630
334
|
}
|
631
335
|
end
|
632
336
|
|
633
337
|
# iterate over all tids of the current process, yield in its context
|
634
|
-
def each_tid
|
338
|
+
def each_tid(&b)
|
635
339
|
lst = @tid_stuff.keys - [@tid]
|
636
340
|
lst << @tid
|
637
|
-
return lst if not
|
341
|
+
return lst if not b
|
638
342
|
lst.each { |t|
|
639
|
-
set_tid t
|
640
|
-
|
343
|
+
set_tid t rescue next
|
344
|
+
b.call
|
641
345
|
}
|
642
346
|
end
|
643
347
|
|
644
348
|
# iterate over all tids of all pids, yield in their context
|
645
|
-
def each_pid_tid
|
646
|
-
each_pid { each_tid {
|
349
|
+
def each_pid_tid(&b)
|
350
|
+
each_pid { each_tid { b.call } }
|
647
351
|
end
|
648
352
|
|
649
353
|
|
@@ -655,7 +359,8 @@ class Debugger
|
|
655
359
|
# returns the Breakpoint object
|
656
360
|
def add_bp(addr, info={})
|
657
361
|
info[:pid] ||= @pid
|
658
|
-
|
362
|
+
# dont define :tid for bpx, otherwise on del_bp we may switch_context to this thread that may not be stopped -> cant ptrace_write
|
363
|
+
info[:tid] ||= @tid if info[:pid] == @pid and info[:type] == :hwbp
|
659
364
|
|
660
365
|
b = Breakpoint.new
|
661
366
|
info.each { |k, v|
|
@@ -740,45 +445,69 @@ class Debugger
|
|
740
445
|
end
|
741
446
|
|
742
447
|
# called in the context of the target when a bpx is to be initialized
|
743
|
-
#
|
448
|
+
# may (lazily) initialize b.emul_instr for virtual singlestep
|
744
449
|
def init_bpx(b)
|
745
|
-
|
746
|
-
if
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
450
|
+
# dont bother setting stuff up if it is never to be used
|
451
|
+
return if b.oneshot and not b.condition
|
452
|
+
|
453
|
+
# lazy setup of b.emul_instr: delay building emulating lambda to if/when actually needed
|
454
|
+
# we still need to disassemble now and update @disassembler, before we patch the memory for the bpx
|
455
|
+
di = init_bpx_disassemble(b.address)
|
456
|
+
b.hash_shared.each { |bb| bb.emul_instr = di }
|
457
|
+
end
|
458
|
+
|
459
|
+
# retrieve the di at a given address, disassemble if needed
|
460
|
+
# TODO make it so this doesn't interfere with other 'real' disassembler later commands, eg disassemble() or disassemble_fast_deep()
|
461
|
+
# (right now, when they see the block already present they stop all processing)
|
462
|
+
def init_bpx_disassemble(addr)
|
463
|
+
@disassembler.disassemble_fast_block(addr)
|
464
|
+
@disassembler.di_at(addr)
|
465
|
+
end
|
466
|
+
|
467
|
+
# checks if bp has an emul_instr
|
468
|
+
# do the lazy initialization if needed
|
469
|
+
def has_emul_instr(bp)
|
470
|
+
if bp.emul_instr.kind_of?(DecodedInstruction)
|
471
|
+
if di = bp.emul_instr and fdbd = @disassembler.get_fwdemu_binding(di, register_pc) and
|
472
|
+
fdbd.all? { |k, v| (k.kind_of?(Symbol) or k.kind_of?(Indirection)) and
|
473
|
+
k != :incomplete_binding and v != Expression::Unknown }
|
474
|
+
# setup a lambda that will mimic, using the debugger primitives, the actual execution of the instruction
|
475
|
+
bp.emul_instr = lambda {
|
476
|
+
fdbd.map { |k, v|
|
477
|
+
k = Indirection[emulinstr_resv(k.pointer), k.len] if k.kind_of?(Indirection)
|
478
|
+
[k, emulinstr_resv(v)]
|
479
|
+
}.each { |k, v|
|
480
|
+
if k.to_s =~ /flags?_(.+)/i
|
481
|
+
f = $1.downcase.to_sym
|
482
|
+
set_flag_value(f, v)
|
483
|
+
elsif k.kind_of?(Symbol)
|
484
|
+
set_reg_value(k, v)
|
485
|
+
elsif k.kind_of?(Indirection)
|
486
|
+
memory_write_int(k.pointer, v, k.len)
|
487
|
+
end
|
488
|
+
}
|
765
489
|
}
|
490
|
+
bp.hash_shared.each { |bb| bb.emul_instr = bp.emul_instr }
|
491
|
+
else
|
492
|
+
bp.hash_shared.each { |bb| bb.emul_instr = nil }
|
493
|
+
end
|
494
|
+
end
|
766
495
|
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
}
|
496
|
+
bp.emul_instr
|
497
|
+
end
|
498
|
+
|
499
|
+
def emulinstr_resv(e)
|
500
|
+
r = e
|
501
|
+
flags = Expression[r].externals.uniq.find_all { |f| f.to_s =~ /flags?_(.+)/i }
|
502
|
+
if flags.first
|
503
|
+
bd = {}
|
504
|
+
flags.each { |f|
|
505
|
+
f.to_s =~ /flags?_(.+)/i
|
506
|
+
bd[f] = get_flag_value($1.downcase.to_sym)
|
779
507
|
}
|
780
|
-
|
508
|
+
r = r.bind(bd)
|
781
509
|
end
|
510
|
+
resolve(r)
|
782
511
|
end
|
783
512
|
|
784
513
|
# sets a breakpoint on execution
|
@@ -798,6 +527,7 @@ puts di.instruction, fdbd.inspect
|
|
798
527
|
h = { :type => :hwbp }
|
799
528
|
h[:hash_owner] = @breakpoint_thread
|
800
529
|
addr = resolve_expr(addr) if not addr.kind_of? ::Integer
|
530
|
+
mtype = mtype.to_sym
|
801
531
|
h[:hash_key] = [addr, mtype, mlen]
|
802
532
|
h[:internal] = { :type => mtype, :len => mlen }
|
803
533
|
h[:oneshot] = true if oneshot
|
@@ -813,7 +543,7 @@ puts di.instruction, fdbd.inspect
|
|
813
543
|
h = { :type => :bpm }
|
814
544
|
addr = resolve_expr(addr) if not addr.kind_of? ::Integer
|
815
545
|
h[:hash_key] = addr & -4096 # XXX actually referenced at addr, addr+4096, ... addr+len
|
816
|
-
h[:internal] = { :type =>
|
546
|
+
h[:internal] = { :type => mtype, :len => mlen }
|
817
547
|
h[:oneshot] = true if oneshot
|
818
548
|
h[:condition] = cond if cond
|
819
549
|
h[:action] = action if action
|
@@ -821,7 +551,7 @@ puts di.instruction, fdbd.inspect
|
|
821
551
|
end
|
822
552
|
|
823
553
|
|
824
|
-
# define the lambda to use to log stuff
|
554
|
+
# define the lambda to use to log stuff
|
825
555
|
def set_log_proc(l=nil, &b)
|
826
556
|
@log_proc = l || b
|
827
557
|
end
|
@@ -831,7 +561,7 @@ puts di.instruction, fdbd.inspect
|
|
831
561
|
if @log_proc
|
832
562
|
a.each { |aa| @log_proc[aa] }
|
833
563
|
else
|
834
|
-
puts(*a)
|
564
|
+
puts(*a) if $VERBOSE
|
835
565
|
end
|
836
566
|
end
|
837
567
|
|
@@ -843,7 +573,7 @@ puts di.instruction, fdbd.inspect
|
|
843
573
|
|
844
574
|
# invalidates the EncodedData backend for the dasm sections
|
845
575
|
def dasm_invalidate
|
846
|
-
disassembler.sections.each_value { |s| s.data.invalidate if s.data.respond_to?
|
576
|
+
disassembler.sections.each_value { |s| s.data.invalidate if s.data.respond_to?(:invalidate) } if disassembler
|
847
577
|
end
|
848
578
|
|
849
579
|
# return all breakpoints set on a specific address (or all bp)
|
@@ -862,7 +592,7 @@ puts di.instruction, fdbd.inspect
|
|
862
592
|
ret |= bb.hash_shared
|
863
593
|
}
|
864
594
|
|
865
|
-
@breakpoint_memory.each_value { |
|
595
|
+
@breakpoint_memory.each_value { |bb|
|
866
596
|
next if addr and (bb.address+bb.internal[:len] <= addr or bb.address > addr)
|
867
597
|
ret |= bb.hash_shared
|
868
598
|
}
|
@@ -870,9 +600,10 @@ puts di.instruction, fdbd.inspect
|
|
870
600
|
ret
|
871
601
|
end
|
872
602
|
|
873
|
-
|
874
|
-
|
875
|
-
|
603
|
+
# return on of the breakpoints at address addr
|
604
|
+
def find_breakpoint(addr=nil, &b)
|
605
|
+
return @breakpoint[addr] if @breakpoint[addr] and (not b or b.call(@breakpoint[addr]))
|
606
|
+
all_breakpoints(addr).find { |bp| b.call bp }
|
876
607
|
end
|
877
608
|
|
878
609
|
|
@@ -894,7 +625,6 @@ puts di.instruction, fdbd.inspect
|
|
894
625
|
@breakpoint_cause = nil
|
895
626
|
@run_method = run_m
|
896
627
|
@run_args = run_a
|
897
|
-
@state = :running
|
898
628
|
@info = nil
|
899
629
|
true
|
900
630
|
end
|
@@ -922,10 +652,12 @@ puts di.instruction, fdbd.inspect
|
|
922
652
|
return @cpu.dbg_find_singlestep(self) if @cpu.respond_to?(:dbg_find_singlestep)
|
923
653
|
@run_method == :singlestep
|
924
654
|
end
|
925
|
-
|
655
|
+
|
926
656
|
# called when the target stops due to a soft breakpoint exception
|
927
657
|
def evt_bpx(b=nil)
|
928
658
|
b ||= find_bp_bpx
|
659
|
+
# TODO handle race:
|
660
|
+
# bpx foo ; thread hits foo ; we bc foo ; os notify us of bp hit but we already cleared everything related to 'bpx foo' -> unhandled bp exception
|
929
661
|
return evt_exception(:type => 'breakpoint') if not b
|
930
662
|
|
931
663
|
@state = :stopped
|
@@ -959,7 +691,7 @@ puts di.instruction, fdbd.inspect
|
|
959
691
|
|
960
692
|
# return the breakpoint that is responsible for the evt_hwbp
|
961
693
|
def find_bp_hwbp
|
962
|
-
return @cpu.dbg_find_hwbp(self) if @cpu.respond_to?(:
|
694
|
+
return @cpu.dbg_find_hwbp(self) if @cpu.respond_to?(:dbg_find_hwbp)
|
963
695
|
@breakpoint_thread.find { |b| b.address == pc }
|
964
696
|
end
|
965
697
|
|
@@ -995,9 +727,9 @@ puts di.instruction, fdbd.inspect
|
|
995
727
|
return if b.address+b.internal[:len] <= info[:fault_addr]
|
996
728
|
return if b.address >= info[:fault_addr] + info[:fault_len]
|
997
729
|
case b.internal[:type]
|
998
|
-
when :
|
999
|
-
when :r; info[:fault_access] == :r
|
730
|
+
when :r; info[:fault_access] == :r # or info[:fault_access] == :x
|
1000
731
|
when :w; info[:fault_access] == :w
|
732
|
+
when :x; info[:fault_access] == :x # XXX non-NX cpu => check pc is in bpm range ?
|
1001
733
|
when :rw; true
|
1002
734
|
end
|
1003
735
|
end
|
@@ -1008,8 +740,10 @@ puts di.instruction, fdbd.inspect
|
|
1008
740
|
|
1009
741
|
found_valid_active = false
|
1010
742
|
|
743
|
+
pre_callback_pc = pc
|
744
|
+
|
1011
745
|
# XXX may have many active bps with callback that continue/singlestep/singlestep{}...
|
1012
|
-
b.hash_shared.dup.
|
746
|
+
b.hash_shared.dup.find_all { |bb|
|
1013
747
|
# ignore inactive bps
|
1014
748
|
next if bb.state != :active
|
1015
749
|
|
@@ -1030,9 +764,11 @@ puts di.instruction, fdbd.inspect
|
|
1030
764
|
# oneshot
|
1031
765
|
del_bp(bb) if bb.oneshot
|
1032
766
|
|
1033
|
-
# callback
|
1034
767
|
bb.action
|
1035
|
-
}.
|
768
|
+
}.each { |bb| bb.action.call }
|
769
|
+
|
770
|
+
# discard @breakpoint_cause if a bp callback did modify register_pc
|
771
|
+
@breakpoint_cause = nil if pc != pre_callback_pc
|
1036
772
|
|
1037
773
|
# we did break due to a bp whose condition is not true: resume
|
1038
774
|
# (unless a callback already resumed)
|
@@ -1118,13 +854,13 @@ puts di.instruction, fdbd.inspect
|
|
1118
854
|
# resume execution as if we never stopped
|
1119
855
|
# disable offending bp + singlestep if needed
|
1120
856
|
def resume_badbreak(b=nil)
|
1121
|
-
# ensure we didn't delete b
|
857
|
+
# ensure we didn't delete b
|
1122
858
|
if b and b.hash_shared.find { |bb| bb.state == :active }
|
1123
859
|
rm = @run_method
|
1124
860
|
if rm == :singlestep
|
1125
861
|
singlestep_bp(b)
|
1126
862
|
else
|
1127
|
-
|
863
|
+
ra = @run_args
|
1128
864
|
singlestep_bp(b) { send rm, *ra }
|
1129
865
|
end
|
1130
866
|
else
|
@@ -1136,10 +872,10 @@ puts di.instruction, fdbd.inspect
|
|
1136
872
|
# if the breakpoint provides an emulation stub, run that, otherwise
|
1137
873
|
# disable the breakpoint, singlestep, and re-enable
|
1138
874
|
def singlestep_bp(bp, &b)
|
1139
|
-
if
|
875
|
+
if has_emul_instr(bp)
|
1140
876
|
@state = :stopped
|
1141
|
-
|
1142
|
-
|
877
|
+
bp.emul_instr.call
|
878
|
+
b.call if b
|
1143
879
|
else
|
1144
880
|
bp.hash_shared.each { |bb|
|
1145
881
|
disable_bp(bb, :temp_inactive) if bb.state == :active
|
@@ -1151,13 +887,23 @@ puts di.instruction, fdbd.inspect
|
|
1151
887
|
enable_bp(bb) if bb.state == :temp_inactive
|
1152
888
|
}
|
1153
889
|
prev_sscb[] if prev_sscb
|
1154
|
-
|
890
|
+
b.call if b
|
1155
891
|
}
|
1156
892
|
end
|
1157
893
|
end
|
1158
894
|
|
895
|
+
# checks if @breakpoint_cause is valid, or was obsoleted by the user changing pc
|
896
|
+
def check_breakpoint_cause
|
897
|
+
if bp = @breakpoint_cause and
|
898
|
+
(bp.type == :bpx or (bp.type == :hwbp and bp.internal[:type] == :x)) and
|
899
|
+
pc != bp.address
|
900
|
+
bp = @breakpoint_cause = nil
|
901
|
+
end
|
902
|
+
bp
|
903
|
+
end
|
1159
904
|
|
1160
905
|
# checks if the running target has stopped (nonblocking)
|
906
|
+
# returns false if no debug event happened
|
1161
907
|
def check_target
|
1162
908
|
do_check_target
|
1163
909
|
end
|
@@ -1171,7 +917,7 @@ puts di.instruction, fdbd.inspect
|
|
1171
917
|
# bypasses a software breakpoint on pc if needed
|
1172
918
|
# thread breakpoints must be manually disabled before calling continue
|
1173
919
|
def continue
|
1174
|
-
if b =
|
920
|
+
if b = check_breakpoint_cause and b.hash_shared.find { |bb| bb.state == :active }
|
1175
921
|
singlestep_bp(b) {
|
1176
922
|
next if not check_pre_run(:continue)
|
1177
923
|
do_continue
|
@@ -1192,11 +938,11 @@ puts di.instruction, fdbd.inspect
|
|
1192
938
|
# resume execution of the target one instruction at a time
|
1193
939
|
def singlestep(&b)
|
1194
940
|
@singlestep_cb = b
|
1195
|
-
bp =
|
941
|
+
bp = check_breakpoint_cause
|
1196
942
|
return if not check_pre_run(:singlestep)
|
1197
|
-
if bp and bp.hash_shared.find { |bb| bb.state == :active } and
|
943
|
+
if bp and bp.hash_shared.find { |bb| bb.state == :active } and has_emul_instr(bp)
|
1198
944
|
@state = :stopped
|
1199
|
-
|
945
|
+
bp.emul_instr.call
|
1200
946
|
invalidate
|
1201
947
|
evt_singlestep(true)
|
1202
948
|
else
|
@@ -1248,6 +994,11 @@ puts di.instruction, fdbd.inspect
|
|
1248
994
|
do_singlestep
|
1249
995
|
end
|
1250
996
|
|
997
|
+
def stepout_wait
|
998
|
+
stepout
|
999
|
+
wait_target
|
1000
|
+
end
|
1001
|
+
|
1251
1002
|
# set a singleshot breakpoint, run the process, and wait
|
1252
1003
|
def go(target, cond=nil)
|
1253
1004
|
bpx(target, true, cond)
|
@@ -1432,9 +1183,9 @@ puts di.instruction, fdbd.inspect
|
|
1432
1183
|
end
|
1433
1184
|
|
1434
1185
|
# load symbols from all libraries found by the OS module
|
1435
|
-
def loadallsyms
|
1186
|
+
def loadallsyms(&b)
|
1436
1187
|
modules.each { |m|
|
1437
|
-
|
1188
|
+
b.call(m.addr) if b
|
1438
1189
|
loadsyms(m.addr, m.path)
|
1439
1190
|
}
|
1440
1191
|
end
|
@@ -1466,7 +1217,7 @@ puts di.instruction, fdbd.inspect
|
|
1466
1217
|
end
|
1467
1218
|
|
1468
1219
|
# parses the expression contained in arg, updates arg to point after the expr
|
1469
|
-
def parse_expr!(arg)
|
1220
|
+
def parse_expr!(arg, &b)
|
1470
1221
|
return if not e = IndExpression.parse_string!(arg) { |s|
|
1471
1222
|
# handle 400000 -> 0x400000
|
1472
1223
|
# XXX no way to override and force decimal interpretation..
|
@@ -1481,7 +1232,7 @@ puts di.instruction, fdbd.inspect
|
|
1481
1232
|
bd = {}
|
1482
1233
|
e.externals.grep(::String).each { |ex|
|
1483
1234
|
if not v = register_list.find { |r| ex.downcase == r.to_s.downcase } ||
|
1484
|
-
(
|
1235
|
+
(b && b.call(ex)) || symbols.index(ex)
|
1485
1236
|
lst = symbols.values.find_all { |s| s.downcase.include? ex.downcase }
|
1486
1237
|
case lst.length
|
1487
1238
|
when 0
|
@@ -1516,7 +1267,7 @@ puts di.instruction, fdbd.inspect
|
|
1516
1267
|
next if bd[ex]
|
1517
1268
|
case ex
|
1518
1269
|
when ::Symbol; bd[ex] = get_reg_value(ex)
|
1519
|
-
when ::String; bd[ex] = @symbols.index(ex) || 0
|
1270
|
+
when ::String; bd[ex] = @symbols.index(ex) || @disassembler.prog_binding[ex] || 0
|
1520
1271
|
end
|
1521
1272
|
}
|
1522
1273
|
Expression[e].bind(bd).reduce { |i|
|
@@ -1619,7 +1370,7 @@ puts di.instruction, fdbd.inspect
|
|
1619
1370
|
plugin_filename = pf + '.rb'
|
1620
1371
|
end
|
1621
1372
|
end
|
1622
|
-
if not File.exist?(plugin_filename) and File.exist?(plugin_filename + '.rb')
|
1373
|
+
if (not File.exist?(plugin_filename) or File.directory?(plugin_filename)) and File.exist?(plugin_filename + '.rb')
|
1623
1374
|
plugin_filename += '.rb'
|
1624
1375
|
end
|
1625
1376
|
|
@@ -1669,18 +1420,26 @@ puts di.instruction, fdbd.inspect
|
|
1669
1420
|
|
1670
1421
|
# see EData#pattern_scan
|
1671
1422
|
# scans only mapped areas of @memory, using os_process.mappings
|
1672
|
-
def pattern_scan(pat, start=0, len=@memory.length-start)
|
1423
|
+
def pattern_scan(pat, start=0, len=@memory.length-start, &b)
|
1673
1424
|
ret = []
|
1674
|
-
mappings.each { |
|
1675
|
-
|
1676
|
-
|
1677
|
-
|
1678
|
-
|
1679
|
-
|
1680
|
-
|
1425
|
+
mappings.each { |maddr, mlen, perm, *o_|
|
1426
|
+
next if perm !~ /r/i
|
1427
|
+
mlen -= start-maddr if maddr < start
|
1428
|
+
maddr = start if maddr < start
|
1429
|
+
mlen = start+len-maddr if maddr+mlen > start+len
|
1430
|
+
next if mlen <= 0
|
1431
|
+
EncodedData.new(read_mapped_range(maddr, mlen)).pattern_scan(pat) { |off|
|
1432
|
+
off += maddr
|
1433
|
+
ret << off if not b or b.call(off)
|
1681
1434
|
}
|
1682
1435
|
}
|
1683
1436
|
ret
|
1684
1437
|
end
|
1438
|
+
|
1439
|
+
def read_mapped_range(addr, len)
|
1440
|
+
# try to use a single get_page call
|
1441
|
+
s = @memory.get_page(addr, len) || ''
|
1442
|
+
s.length == len ? s : (s = @memory[addr, len] ? s.to_str : nil)
|
1443
|
+
end
|
1685
1444
|
end
|
1686
1445
|
end
|