metasm 1.0.1 → 1.0.2
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 +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
|