metasm 1.0.0
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.
- data/BUGS +11 -0
- data/CREDITS +17 -0
- data/README +270 -0
- data/TODO +114 -0
- data/doc/code_organisation.txt +146 -0
- data/doc/const_missing.txt +16 -0
- data/doc/core_classes.txt +75 -0
- data/doc/feature_list.txt +53 -0
- data/doc/index.txt +59 -0
- data/doc/install_notes.txt +170 -0
- data/doc/style.css +3 -0
- data/doc/use_cases.txt +18 -0
- data/lib/metasm.rb +80 -0
- data/lib/metasm/arm.rb +12 -0
- data/lib/metasm/arm/debug.rb +39 -0
- data/lib/metasm/arm/decode.rb +167 -0
- data/lib/metasm/arm/encode.rb +77 -0
- data/lib/metasm/arm/main.rb +75 -0
- data/lib/metasm/arm/opcodes.rb +177 -0
- data/lib/metasm/arm/parse.rb +130 -0
- data/lib/metasm/arm/render.rb +55 -0
- data/lib/metasm/compile_c.rb +1457 -0
- data/lib/metasm/dalvik.rb +8 -0
- data/lib/metasm/dalvik/decode.rb +196 -0
- data/lib/metasm/dalvik/main.rb +60 -0
- data/lib/metasm/dalvik/opcodes.rb +366 -0
- data/lib/metasm/decode.rb +213 -0
- data/lib/metasm/decompile.rb +2659 -0
- data/lib/metasm/disassemble.rb +2068 -0
- data/lib/metasm/disassemble_api.rb +1280 -0
- data/lib/metasm/dynldr.rb +1329 -0
- data/lib/metasm/encode.rb +333 -0
- data/lib/metasm/exe_format/a_out.rb +194 -0
- data/lib/metasm/exe_format/autoexe.rb +82 -0
- data/lib/metasm/exe_format/bflt.rb +189 -0
- data/lib/metasm/exe_format/coff.rb +455 -0
- data/lib/metasm/exe_format/coff_decode.rb +901 -0
- data/lib/metasm/exe_format/coff_encode.rb +1078 -0
- data/lib/metasm/exe_format/dex.rb +457 -0
- data/lib/metasm/exe_format/dol.rb +145 -0
- data/lib/metasm/exe_format/elf.rb +923 -0
- data/lib/metasm/exe_format/elf_decode.rb +979 -0
- data/lib/metasm/exe_format/elf_encode.rb +1375 -0
- data/lib/metasm/exe_format/macho.rb +827 -0
- data/lib/metasm/exe_format/main.rb +228 -0
- data/lib/metasm/exe_format/mz.rb +164 -0
- data/lib/metasm/exe_format/nds.rb +172 -0
- data/lib/metasm/exe_format/pe.rb +437 -0
- data/lib/metasm/exe_format/serialstruct.rb +246 -0
- data/lib/metasm/exe_format/shellcode.rb +114 -0
- data/lib/metasm/exe_format/xcoff.rb +167 -0
- data/lib/metasm/gui.rb +23 -0
- data/lib/metasm/gui/cstruct.rb +373 -0
- data/lib/metasm/gui/dasm_coverage.rb +199 -0
- data/lib/metasm/gui/dasm_decomp.rb +369 -0
- data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
- data/lib/metasm/gui/dasm_graph.rb +1354 -0
- data/lib/metasm/gui/dasm_hex.rb +543 -0
- data/lib/metasm/gui/dasm_listing.rb +599 -0
- data/lib/metasm/gui/dasm_main.rb +906 -0
- data/lib/metasm/gui/dasm_opcodes.rb +291 -0
- data/lib/metasm/gui/debug.rb +1228 -0
- data/lib/metasm/gui/gtk.rb +884 -0
- data/lib/metasm/gui/qt.rb +495 -0
- data/lib/metasm/gui/win32.rb +3004 -0
- data/lib/metasm/gui/x11.rb +621 -0
- data/lib/metasm/ia32.rb +14 -0
- data/lib/metasm/ia32/compile_c.rb +1523 -0
- data/lib/metasm/ia32/debug.rb +193 -0
- data/lib/metasm/ia32/decode.rb +1167 -0
- data/lib/metasm/ia32/decompile.rb +564 -0
- data/lib/metasm/ia32/encode.rb +314 -0
- data/lib/metasm/ia32/main.rb +233 -0
- data/lib/metasm/ia32/opcodes.rb +872 -0
- data/lib/metasm/ia32/parse.rb +327 -0
- data/lib/metasm/ia32/render.rb +91 -0
- data/lib/metasm/main.rb +1193 -0
- data/lib/metasm/mips.rb +11 -0
- data/lib/metasm/mips/compile_c.rb +7 -0
- data/lib/metasm/mips/decode.rb +253 -0
- data/lib/metasm/mips/encode.rb +51 -0
- data/lib/metasm/mips/main.rb +72 -0
- data/lib/metasm/mips/opcodes.rb +443 -0
- data/lib/metasm/mips/parse.rb +51 -0
- data/lib/metasm/mips/render.rb +43 -0
- data/lib/metasm/os/gnu_exports.rb +270 -0
- data/lib/metasm/os/linux.rb +1112 -0
- data/lib/metasm/os/main.rb +1686 -0
- data/lib/metasm/os/remote.rb +527 -0
- data/lib/metasm/os/windows.rb +2027 -0
- data/lib/metasm/os/windows_exports.rb +745 -0
- data/lib/metasm/parse.rb +876 -0
- data/lib/metasm/parse_c.rb +3938 -0
- data/lib/metasm/pic16c/decode.rb +42 -0
- data/lib/metasm/pic16c/main.rb +17 -0
- data/lib/metasm/pic16c/opcodes.rb +68 -0
- data/lib/metasm/ppc.rb +11 -0
- data/lib/metasm/ppc/decode.rb +264 -0
- data/lib/metasm/ppc/decompile.rb +251 -0
- data/lib/metasm/ppc/encode.rb +51 -0
- data/lib/metasm/ppc/main.rb +129 -0
- data/lib/metasm/ppc/opcodes.rb +410 -0
- data/lib/metasm/ppc/parse.rb +52 -0
- data/lib/metasm/preprocessor.rb +1277 -0
- data/lib/metasm/render.rb +130 -0
- data/lib/metasm/sh4.rb +8 -0
- data/lib/metasm/sh4/decode.rb +336 -0
- data/lib/metasm/sh4/main.rb +292 -0
- data/lib/metasm/sh4/opcodes.rb +381 -0
- data/lib/metasm/x86_64.rb +12 -0
- data/lib/metasm/x86_64/compile_c.rb +1025 -0
- data/lib/metasm/x86_64/debug.rb +59 -0
- data/lib/metasm/x86_64/decode.rb +268 -0
- data/lib/metasm/x86_64/encode.rb +264 -0
- data/lib/metasm/x86_64/main.rb +135 -0
- data/lib/metasm/x86_64/opcodes.rb +118 -0
- data/lib/metasm/x86_64/parse.rb +68 -0
- data/misc/bottleneck.rb +61 -0
- data/misc/cheader-findpppath.rb +58 -0
- data/misc/hexdiff.rb +74 -0
- data/misc/hexdump.rb +55 -0
- data/misc/metasm-all.rb +13 -0
- data/misc/objdiff.rb +47 -0
- data/misc/objscan.rb +40 -0
- data/misc/pdfparse.rb +661 -0
- data/misc/ppc_pdf2oplist.rb +192 -0
- data/misc/tcp_proxy_hex.rb +84 -0
- data/misc/txt2html.rb +440 -0
- data/samples/a.out.rb +31 -0
- data/samples/asmsyntax.rb +77 -0
- data/samples/bindiff.rb +555 -0
- data/samples/compilation-steps.rb +49 -0
- data/samples/cparser_makestackoffset.rb +55 -0
- data/samples/dasm-backtrack.rb +38 -0
- data/samples/dasmnavig.rb +318 -0
- data/samples/dbg-apihook.rb +228 -0
- data/samples/dbghelp.rb +143 -0
- data/samples/disassemble-gui.rb +102 -0
- data/samples/disassemble.rb +133 -0
- data/samples/dump_upx.rb +95 -0
- data/samples/dynamic_ruby.rb +1929 -0
- data/samples/elf_list_needed.rb +46 -0
- data/samples/elf_listexports.rb +33 -0
- data/samples/elfencode.rb +25 -0
- data/samples/exeencode.rb +128 -0
- data/samples/factorize-headers-elfimports.rb +77 -0
- data/samples/factorize-headers-peimports.rb +109 -0
- data/samples/factorize-headers.rb +43 -0
- data/samples/gdbclient.rb +583 -0
- data/samples/generate_libsigs.rb +102 -0
- data/samples/hotfix_gtk_dbg.rb +59 -0
- data/samples/install_win_env.rb +78 -0
- data/samples/lindebug.rb +924 -0
- data/samples/linux_injectsyscall.rb +95 -0
- data/samples/machoencode.rb +31 -0
- data/samples/metasm-shell.rb +91 -0
- data/samples/pe-hook.rb +69 -0
- data/samples/pe-ia32-cpuid.rb +203 -0
- data/samples/pe-mips.rb +35 -0
- data/samples/pe-shutdown.rb +78 -0
- data/samples/pe-testrelocs.rb +51 -0
- data/samples/pe-testrsrc.rb +24 -0
- data/samples/pe_listexports.rb +31 -0
- data/samples/peencode.rb +19 -0
- data/samples/peldr.rb +494 -0
- data/samples/preprocess-flatten.rb +19 -0
- data/samples/r0trace.rb +308 -0
- data/samples/rubstop.rb +399 -0
- data/samples/scan_pt_gnu_stack.rb +54 -0
- data/samples/scanpeexports.rb +62 -0
- data/samples/shellcode-c.rb +40 -0
- data/samples/shellcode-dynlink.rb +146 -0
- data/samples/source.asm +34 -0
- data/samples/struct_offset.rb +47 -0
- data/samples/testpe.rb +32 -0
- data/samples/testraw.rb +45 -0
- data/samples/win32genloader.rb +132 -0
- data/samples/win32hooker-advanced.rb +169 -0
- data/samples/win32hooker.rb +96 -0
- data/samples/win32livedasm.rb +33 -0
- data/samples/win32remotescan.rb +133 -0
- data/samples/wintrace.rb +92 -0
- data/tests/all.rb +8 -0
- data/tests/dasm.rb +39 -0
- data/tests/dynldr.rb +35 -0
- data/tests/encodeddata.rb +132 -0
- data/tests/ia32.rb +82 -0
- data/tests/mips.rb +116 -0
- data/tests/parse_c.rb +239 -0
- data/tests/preprocessor.rb +269 -0
- data/tests/x86_64.rb +62 -0
- metadata +255 -0
|
@@ -0,0 +1,599 @@
|
|
|
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
|
+
module Metasm
|
|
7
|
+
module Gui
|
|
8
|
+
class AsmListingWidget < DrawableWidget
|
|
9
|
+
attr_accessor :dasm, :arrow_zone_w
|
|
10
|
+
# nr of raw bytes to display next to each decoded instruction
|
|
11
|
+
attr_accessor :raw_data_length
|
|
12
|
+
|
|
13
|
+
def initialize_widget(dasm, parent_widget)
|
|
14
|
+
@dasm = dasm
|
|
15
|
+
@parent_widget = parent_widget
|
|
16
|
+
|
|
17
|
+
@arrows = [] # array of [linefrom, lineto] (may be :up or :down for offscreen)
|
|
18
|
+
@line_address = []
|
|
19
|
+
@line_text = []
|
|
20
|
+
@line_text_color = []
|
|
21
|
+
@want_update_line_text = @want_update_caret = true
|
|
22
|
+
@wantaddr = nil
|
|
23
|
+
@arrow_zone_w = 40
|
|
24
|
+
@raw_data_length = 0
|
|
25
|
+
|
|
26
|
+
addrs = @dasm.sections.keys.grep(Integer)
|
|
27
|
+
@minaddr = addrs.min.to_i
|
|
28
|
+
@maxaddr = (addrs.max + @dasm.sections[addrs.max].length rescue (1 << @dasm.cpu.size))
|
|
29
|
+
@startaddr = @dasm.prog_binding['entrypoint'] || @minaddr
|
|
30
|
+
|
|
31
|
+
@default_color_association = { :comment => :darkblue, :label => :darkgreen, :text => :black,
|
|
32
|
+
:instruction => :black, :address => :blue, :caret => :black, :raw_data => :black,
|
|
33
|
+
:background => :white, :cursorline_bg => :paleyellow, :hl_word => :palered,
|
|
34
|
+
:arrows_bg => :palegrey, :arrow_up => :darkblue, :arrow_dn => :darkyellow, :arrow_hl => :red }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def resized(w, h)
|
|
38
|
+
col = w/@font_width
|
|
39
|
+
lin = h/@font_height
|
|
40
|
+
@caret_x = col-1 if @caret_x >= col
|
|
41
|
+
@caret_y = lin-1 if @caret_y >= lin and lin > 0
|
|
42
|
+
gui_update
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def adjust_startaddr(off=0, update = true)
|
|
46
|
+
@startaddr += off
|
|
47
|
+
@startaddr = @maxaddr - 1 if @startaddr.kind_of? Integer and @startaddr >= @maxaddr
|
|
48
|
+
if off = (0..16).find { |off_| di = @dasm.decoded[@startaddr-off_] and di.respond_to? :bin_length and di.bin_length > off_ } and off != 0
|
|
49
|
+
# align on @decoded boundary
|
|
50
|
+
@startaddr -= off
|
|
51
|
+
end
|
|
52
|
+
@startaddr = @minaddr if @startaddr.kind_of? Integer and @startaddr < @minaddr
|
|
53
|
+
gui_update if update
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def click(x, y)
|
|
57
|
+
set_caret_from_click(x - @arrow_zone_w, y)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def rightclick(x, y)
|
|
61
|
+
click(x, y)
|
|
62
|
+
@parent_widget.clone_window(@hl_word, :listing)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def doubleclick(x, y)
|
|
66
|
+
click(x, y)
|
|
67
|
+
@parent_widget.focus_addr(@hl_word)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def mouse_wheel(dir, x, y)
|
|
71
|
+
case dir
|
|
72
|
+
when :up
|
|
73
|
+
# TODO handle block start (multiline) / data aggregation (db 100h dup(?), strings..)
|
|
74
|
+
@wantaddr = @line_address[@caret_y]
|
|
75
|
+
adjust_startaddr(-1, false)
|
|
76
|
+
adjust_startaddr(-1, false)
|
|
77
|
+
adjust_startaddr(-1, false)
|
|
78
|
+
adjust_startaddr(-1)
|
|
79
|
+
when :down
|
|
80
|
+
# scroll down 4 lines, or more if all the 4 1st lines have the same addr (eg block start)
|
|
81
|
+
@wantaddr = @line_address[@caret_y]
|
|
82
|
+
a = @line_address[4..-1].find { |v| v != @line_address[0] } if @line_address[4]
|
|
83
|
+
@startaddr = a || (@startaddr + 4)
|
|
84
|
+
adjust_startaddr
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# renders the disassembler from @startaddr
|
|
89
|
+
def paint
|
|
90
|
+
w_w = width
|
|
91
|
+
w_h = height
|
|
92
|
+
|
|
93
|
+
# arrow bg
|
|
94
|
+
draw_rectangle_color(:arrows_bg, 0, 0, @arrow_zone_w, w_h)
|
|
95
|
+
|
|
96
|
+
# TODO scroll line-by-line when an addr is displayed on multiple lines (eg labels/comments)
|
|
97
|
+
# TODO selection
|
|
98
|
+
|
|
99
|
+
update_line_text if @want_update_line_text
|
|
100
|
+
update_caret if @want_update_caret
|
|
101
|
+
|
|
102
|
+
if @parent_widget.bg_color_callback
|
|
103
|
+
ly = 0
|
|
104
|
+
@line_address.each { |a|
|
|
105
|
+
if c = @parent_widget.bg_color_callback[a]
|
|
106
|
+
draw_rectangle_color(c, @arrow_zone_w, ly*@font_height, w_w, @font_height)
|
|
107
|
+
end
|
|
108
|
+
ly += 1
|
|
109
|
+
}
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# current window position
|
|
113
|
+
x = @arrow_zone_w + 1
|
|
114
|
+
y = 0
|
|
115
|
+
|
|
116
|
+
# renders a string at current cursor position with a color
|
|
117
|
+
# must not include newline
|
|
118
|
+
render = lambda { |str, color|
|
|
119
|
+
# function ends when we write under the bottom of the listing
|
|
120
|
+
next if not str or y >= w_h or x >= w_w
|
|
121
|
+
if @hl_word and @hl_word != ''
|
|
122
|
+
stmp = str
|
|
123
|
+
pre_x = 0
|
|
124
|
+
while stmp =~ /^(.*?)(\b#{Regexp.escape @hl_word}\b)/
|
|
125
|
+
s1, s2 = $1, $2
|
|
126
|
+
pre_x += s1.length * @font_width
|
|
127
|
+
hl_x = s2.length * @font_width
|
|
128
|
+
draw_rectangle_color(:hl_word, x+pre_x, y, hl_x, @font_height)
|
|
129
|
+
pre_x += hl_x
|
|
130
|
+
stmp = stmp[s1.length+s2.length..-1]
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
draw_string_color(color, x, y, str)
|
|
134
|
+
x += str.length * @font_width
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# draw caret line background
|
|
138
|
+
draw_rectangle_color(:cursorline_bg, 0, @caret_y*@font_height, w_w, @font_height)
|
|
139
|
+
|
|
140
|
+
@line_text_color.each { |a|
|
|
141
|
+
a.each { |s, c| render[s, c] }
|
|
142
|
+
x = @arrow_zone_w + 1
|
|
143
|
+
y += @font_height
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if focus?
|
|
147
|
+
cx = @arrow_zone_w + @caret_x*@font_width+1
|
|
148
|
+
cy = @caret_y*@font_height
|
|
149
|
+
draw_line_color(:caret, cx, cy, cx, cy+@font_height-1)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
paint_arrows
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# draws the @arrows defined in paint_listing
|
|
156
|
+
def paint_arrows
|
|
157
|
+
return if @arrows.empty? or not @line_address[0]
|
|
158
|
+
w_w, w_h = @arrow_zone_w, height
|
|
159
|
+
|
|
160
|
+
slot_alloc = {} # [y1, y2] => x slot -- y1 <= y2
|
|
161
|
+
# find a free x slot for the vertical side of the arrow
|
|
162
|
+
max = (w_w-6)/3
|
|
163
|
+
find_free = lambda { |y1, y2|
|
|
164
|
+
y1, y2 = y2, y1 if y2 < y1
|
|
165
|
+
slot_alloc[[y1, y2]] = (0...max).find { |off|
|
|
166
|
+
not slot_alloc.find { |(oy1, oy2), oo|
|
|
167
|
+
# return true if this slot cannot share with off
|
|
168
|
+
next if oo != off # not same slot => ok
|
|
169
|
+
next if oy1 == y1 and y1 != 0 # same upbound & in window
|
|
170
|
+
next if oy2 == y2 and y2 != w_h-1 # same lowbound & in window
|
|
171
|
+
# check overlapping segment
|
|
172
|
+
(y1 >= oy1 and y1 <= oy2) or
|
|
173
|
+
(y2 >= oy1 and y2 <= oy2) or
|
|
174
|
+
(oy1 >= y1 and oy1 <= y2) or
|
|
175
|
+
(oy2 >= y1 and oy2 <= y2)
|
|
176
|
+
}
|
|
177
|
+
} || (max-1)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
# alloc slots for arrows, starts by the smallest
|
|
181
|
+
arrs = { :arrow_dn => [], :arrow_up => [], :arrow_hl => [] }
|
|
182
|
+
@arrows.sort_by { |from, to|
|
|
183
|
+
if from.kind_of? Numeric and to.kind_of? Numeric
|
|
184
|
+
(from-to).abs
|
|
185
|
+
else
|
|
186
|
+
100000
|
|
187
|
+
end
|
|
188
|
+
}.each { |from, to|
|
|
189
|
+
y1 = case from
|
|
190
|
+
when :up; 0
|
|
191
|
+
when :down; w_h-1
|
|
192
|
+
else from * @font_height + @font_height/2 - 1
|
|
193
|
+
end
|
|
194
|
+
y2 = case to
|
|
195
|
+
when :up; 0
|
|
196
|
+
when :down; w_h-1
|
|
197
|
+
else to * @font_height + @font_height/2 - 1
|
|
198
|
+
end
|
|
199
|
+
if y1 <= y2
|
|
200
|
+
y1 += 2 if y1 != 0
|
|
201
|
+
else
|
|
202
|
+
y1 -= 2 if y1 != w_h-1
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
col = :arrow_dn
|
|
206
|
+
col = :arrow_up if y1 > y2
|
|
207
|
+
col = :arrow_hl if (from.kind_of? Integer and @line_address[from] == @line_address[@caret_y]) or
|
|
208
|
+
(to.kind_of? Integer and @line_address[to] == @line_address[@caret_y])
|
|
209
|
+
arrs[col] << [y1, y2, find_free[y1, y2]]
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
slot_w = (w_w-4)/slot_alloc.values.uniq.length
|
|
213
|
+
# draw arrows (hl last to overwrite)
|
|
214
|
+
[:arrow_dn, :arrow_up, :arrow_hl].each { |col|
|
|
215
|
+
draw_color(col)
|
|
216
|
+
arrs[col].each { |y1, y2, slot|
|
|
217
|
+
x1 = w_w-1
|
|
218
|
+
x2 = w_w-4 - slot*slot_w - slot_w/2
|
|
219
|
+
|
|
220
|
+
draw_line(x1, y1, x2, y1) if y1 != 0 and y1 != w_h-1
|
|
221
|
+
draw_line(x2, y1, x2, y2)
|
|
222
|
+
draw_line(x2, y2, x1, y2) if y2 != 0 and y2 != w_h-1
|
|
223
|
+
draw_line(x1, y2, x1-3, y2-3) if y2 != 0 and y2 != w_h-1
|
|
224
|
+
draw_line(x1, y2, x1-3, y2+3) if y2 != 0 and y2 != w_h-1
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# if curaddr points to an instruction, find the next data, else find the next instruction
|
|
230
|
+
def move_to_next
|
|
231
|
+
a = current_address
|
|
232
|
+
if not @dasm.get_section_at(a)
|
|
233
|
+
a = @dasm.sections.map { |k, e| k }.find_all { |k| k > a }.min
|
|
234
|
+
elsif @dasm.di_at(a)
|
|
235
|
+
while di = @dasm.di_at(a)
|
|
236
|
+
a = di.block.list.last.next_addr
|
|
237
|
+
end
|
|
238
|
+
else
|
|
239
|
+
a = @dasm.decoded.keys.find_all { |k| k > a }.min
|
|
240
|
+
end
|
|
241
|
+
@parent_widget.focus_addr(a) if a
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# see move_to_next
|
|
245
|
+
def move_to_prev
|
|
246
|
+
a = current_address
|
|
247
|
+
if not @dasm.get_section_at(a)
|
|
248
|
+
a = @dasm.sections.map { |k, e| k }.find_all { |k| k < a }.max
|
|
249
|
+
a += @dasm.get_section_at(a)[0].length - 1 if a
|
|
250
|
+
elsif @dasm.di_at(a)
|
|
251
|
+
while di = @dasm.di_at(a)
|
|
252
|
+
a = di.block.list.first.address
|
|
253
|
+
if off = (1..16).find { |off_|
|
|
254
|
+
@dasm.decoded[a-off_].kind_of? DecodedInstruction and
|
|
255
|
+
@dasm.decoded[a-off_].next_addr == a }
|
|
256
|
+
a -= off
|
|
257
|
+
else
|
|
258
|
+
a -= 1
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
else
|
|
262
|
+
a = @dasm.decoded.keys.find_all { |k| k < a }.max
|
|
263
|
+
end
|
|
264
|
+
@parent_widget.focus_addr(a) if a
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def keypress_ctrl(key)
|
|
268
|
+
case key
|
|
269
|
+
when ?n; move_to_next ; true
|
|
270
|
+
when ?p; move_to_prev ; true
|
|
271
|
+
else return false
|
|
272
|
+
end
|
|
273
|
+
true
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def keypress(key)
|
|
277
|
+
case key
|
|
278
|
+
when :left
|
|
279
|
+
if @caret_x >= 1
|
|
280
|
+
@caret_x -= 1
|
|
281
|
+
update_caret
|
|
282
|
+
end
|
|
283
|
+
when :up
|
|
284
|
+
if @caret_y > 1 or (@caret_y == 1 and @startaddr == @minaddr)
|
|
285
|
+
@caret_y -= 1
|
|
286
|
+
else
|
|
287
|
+
adjust_startaddr(-1)
|
|
288
|
+
end
|
|
289
|
+
update_caret
|
|
290
|
+
when :right
|
|
291
|
+
if @caret_x < @line_text[@caret_y].to_s.length
|
|
292
|
+
@caret_x += 1
|
|
293
|
+
update_caret
|
|
294
|
+
end
|
|
295
|
+
when :down
|
|
296
|
+
if @caret_y < @line_address.length-3 or (@caret_y < @line_address.length - 2 and @startaddr == @maxaddr)
|
|
297
|
+
@caret_y += 1
|
|
298
|
+
else
|
|
299
|
+
if a = @line_address[0] and na = @line_address.find { |na_| na_ != a }
|
|
300
|
+
@startaddr = na
|
|
301
|
+
gui_update
|
|
302
|
+
else
|
|
303
|
+
adjust_startaddr(1)
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
update_caret
|
|
307
|
+
when :pgup
|
|
308
|
+
adjust_startaddr(-15)
|
|
309
|
+
when :pgdown
|
|
310
|
+
@startaddr = @line_address[@line_address.length/2] || @startaddr + 15
|
|
311
|
+
gui_update
|
|
312
|
+
when :home
|
|
313
|
+
@caret_x = 0
|
|
314
|
+
update_caret
|
|
315
|
+
when :end
|
|
316
|
+
@caret_x = @line_text[@caret_y].to_s.length
|
|
317
|
+
update_caret
|
|
318
|
+
else return false
|
|
319
|
+
end
|
|
320
|
+
true
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def get_cursor_pos
|
|
324
|
+
[@startaddr, @caret_x, @caret_y]
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def set_cursor_pos(p)
|
|
328
|
+
@startaddr, @caret_x, @caret_y = p
|
|
329
|
+
gui_update
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# hint that the caret moved
|
|
333
|
+
# redraws the caret, change the hilighted word, redraw if needed
|
|
334
|
+
def update_caret
|
|
335
|
+
if @want_update_line_text
|
|
336
|
+
@want_update_caret = true
|
|
337
|
+
return
|
|
338
|
+
end
|
|
339
|
+
return if not @line_text[@caret_y]
|
|
340
|
+
@want_update_caret = false
|
|
341
|
+
if update_hl_word(@line_text[@caret_y], @caret_x) or @oldcaret_y != @caret_y or true
|
|
342
|
+
redraw
|
|
343
|
+
else
|
|
344
|
+
return if @oldcaret_x == @caret_x and @oldcaret_y == @caret_y
|
|
345
|
+
|
|
346
|
+
invalidate_caret(@oldcaret_x, @oldcaret_y, @arrow_zone_w, 0)
|
|
347
|
+
invalidate_caret(@caret_x, @caret_y, @arrow_zone_w, 0)
|
|
348
|
+
|
|
349
|
+
if @arrows.find { |f, t| f == @caret_y or t == @caret_y or f == @oldcaret_y or t == @oldcaret_y }
|
|
350
|
+
invalidate(0, 0, @arrow_zone_w, 1000000)
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
@parent_widget.focus_changed_callback[] if @parent_widget.focus_changed_callback and @oldcaret_y != @caret_y
|
|
354
|
+
|
|
355
|
+
@oldcaret_x = @caret_x
|
|
356
|
+
@oldcaret_y = @caret_y
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
# focus on addr
|
|
360
|
+
# addr may be a dasm label, dasm address, dasm address in string form (eg "0DEADBEEFh")
|
|
361
|
+
# may scroll the window
|
|
362
|
+
# returns true on success (address exists)
|
|
363
|
+
def focus_addr(addr)
|
|
364
|
+
return if not addr = @parent_widget.normalize(addr)
|
|
365
|
+
if l = @line_address.index(addr) and l < @line_address.length - 4
|
|
366
|
+
@caret_y, @caret_x = @line_address.rindex(addr), 0
|
|
367
|
+
elsif addr.kind_of?(Integer) ? (addr >= @minaddr and addr <= @maxaddr) : @dasm.get_section_at(addr)
|
|
368
|
+
@startaddr, @caret_x, @caret_y = addr, 0, 0
|
|
369
|
+
adjust_startaddr
|
|
370
|
+
@wantaddr = @startaddr
|
|
371
|
+
@line_address[@caret_y] = @startaddr # so that right after focus_addr(42) ; self.current_address => 42 (coverage sync)
|
|
372
|
+
else
|
|
373
|
+
return
|
|
374
|
+
end
|
|
375
|
+
update_caret
|
|
376
|
+
true
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# returns the address of the data under the cursor
|
|
380
|
+
def current_address
|
|
381
|
+
@line_address[@caret_y] || -1
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# reads @dasm to update @line_text_color/@line_text/@line_address/@arrows
|
|
385
|
+
def update_line_text
|
|
386
|
+
@want_update_line_text = false
|
|
387
|
+
|
|
388
|
+
w_h = (height + @font_height - 1) / @font_height
|
|
389
|
+
|
|
390
|
+
curaddr = @startaddr
|
|
391
|
+
|
|
392
|
+
@line_address.clear
|
|
393
|
+
@line_text.clear
|
|
394
|
+
@line_text_color.clear # list of [str, color]
|
|
395
|
+
|
|
396
|
+
line = 0
|
|
397
|
+
|
|
398
|
+
# list of arrows to draw ([addr_from, addr_to])
|
|
399
|
+
arrows_addr = []
|
|
400
|
+
|
|
401
|
+
str_c = []
|
|
402
|
+
|
|
403
|
+
nl = lambda {
|
|
404
|
+
@line_address[line] = curaddr
|
|
405
|
+
@line_text[line] = str_c.map { |s, c| s }.join
|
|
406
|
+
@line_text_color[line] = str_c
|
|
407
|
+
str_c = []
|
|
408
|
+
line += 1
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
while line < w_h
|
|
412
|
+
if di = @dasm.di_at(curaddr)
|
|
413
|
+
if di.block_head?
|
|
414
|
+
# render dump_block_header, add a few colors
|
|
415
|
+
b_header = '' ; @dasm.dump_block_header(di.block) { |l| b_header << l ; b_header << ?\n if b_header[-1] != ?\n }
|
|
416
|
+
b_header.each_line { |l|
|
|
417
|
+
l.chomp!
|
|
418
|
+
cmt = (l[0, 2] == '//' or l[-1] != ?:)
|
|
419
|
+
str_c << [l, (cmt ? :comment : :label)]
|
|
420
|
+
nl[]
|
|
421
|
+
}
|
|
422
|
+
# ary
|
|
423
|
+
di.block.each_from_samefunc(@dasm) { |addr|
|
|
424
|
+
addr = @dasm.normalize addr
|
|
425
|
+
next if not addr.kind_of? ::Integer or (ndi = @dasm.di_at(addr) and ndi.next_addr == curaddr)
|
|
426
|
+
arrows_addr << [addr, curaddr]
|
|
427
|
+
}
|
|
428
|
+
end
|
|
429
|
+
if di.block.list.last == di
|
|
430
|
+
di.block.each_to_samefunc(@dasm) { |addr|
|
|
431
|
+
addr = @dasm.normalize addr
|
|
432
|
+
next if not addr.kind_of? ::Integer or (di.next_addr == addr and
|
|
433
|
+
(not di.opcode.props[:saveip] or di.block.to_subfuncret))
|
|
434
|
+
arrows_addr << [curaddr, addr]
|
|
435
|
+
}
|
|
436
|
+
end
|
|
437
|
+
str_c << ["#{Expression[di.address]} ", :address]
|
|
438
|
+
if @raw_data_length.to_i > 0
|
|
439
|
+
if s = @dasm.get_edata_at(curaddr)
|
|
440
|
+
raw = s.read(di.bin_length)
|
|
441
|
+
raw = raw.unpack('H*').first
|
|
442
|
+
else
|
|
443
|
+
raw = ''
|
|
444
|
+
end
|
|
445
|
+
raw = raw.ljust(@raw_data_length*2)[0, @raw_data_length*2]
|
|
446
|
+
raw += (di.bin_length > @raw_data_length ? '- ' : ' ')
|
|
447
|
+
str_c << [raw, :raw_data]
|
|
448
|
+
end
|
|
449
|
+
str_c << ["#{di.instruction} ".ljust(di.comment ? 24 : 0), :instruction]
|
|
450
|
+
str_c << [" ; #{di.comment.join(' ')}", :comment] if di.comment
|
|
451
|
+
nl[]
|
|
452
|
+
|
|
453
|
+
# instr overlapping
|
|
454
|
+
if off = (1...di.bin_length).find { |off_| @dasm.decoded[curaddr + off_] }
|
|
455
|
+
nl[]
|
|
456
|
+
curaddr += off
|
|
457
|
+
str_c << ["// ------ overlap (#{di.bin_length - off}) ------", :comment]
|
|
458
|
+
nl[]
|
|
459
|
+
else
|
|
460
|
+
curaddr += [di.bin_length, 1].max
|
|
461
|
+
end
|
|
462
|
+
elsif s = @dasm.get_edata_at(curaddr) and s.ptr < s.length
|
|
463
|
+
@dasm.comment[curaddr].each { |c| str_c << ["// #{c}", :comment] ; nl[] } if @dasm.comment[curaddr]
|
|
464
|
+
if label = s.inv_export[s.ptr]
|
|
465
|
+
l_list = @dasm.label_alias[curaddr].to_a.sort
|
|
466
|
+
label = l_list.pop
|
|
467
|
+
nl[] if not l_list.empty?
|
|
468
|
+
l_list.each { |name|
|
|
469
|
+
str_c << ["#{name}:", :label]
|
|
470
|
+
nl[]
|
|
471
|
+
}
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
len = 256
|
|
475
|
+
comment = nil
|
|
476
|
+
if s.data.length > s.ptr
|
|
477
|
+
str = s.read(len).unpack('C*')
|
|
478
|
+
s.ptr -= len # we may not display the whole bunch, ptr is advanced later
|
|
479
|
+
len = str.length
|
|
480
|
+
if @dasm.xrefs[curaddr] or rel = s.reloc[s.ptr]
|
|
481
|
+
xlen = nil
|
|
482
|
+
xlen = rel.length if rel
|
|
483
|
+
comment = []
|
|
484
|
+
@dasm.each_xref(curaddr) { |xref|
|
|
485
|
+
xlen ||= xref.len || 1 if xref.len
|
|
486
|
+
comment << " #{xref.type}#{xref.len}:#{Expression[xref.origin]}" if xref.origin
|
|
487
|
+
} if @dasm.xrefs[curaddr]
|
|
488
|
+
len = xlen if xlen and xlen > 2 # db xref may point a string
|
|
489
|
+
comment = nil if comment.empty?
|
|
490
|
+
len = (1..len).find { |l| @dasm.xrefs[curaddr+l] or s.inv_export[s.ptr+l] or s.reloc[s.ptr+l] } || len
|
|
491
|
+
str = str[0, len] if len < str.length
|
|
492
|
+
str = str.pack('C*').unpack(@dasm.cpu.endianness == :big ? 'n*' : 'v*') if len == 2
|
|
493
|
+
if (xlen == 1 or xlen == 2) and asc = str.inject('') { |asc_, c|
|
|
494
|
+
case c
|
|
495
|
+
when 0x20..0x7e, 9, 10, 13; asc_ << c
|
|
496
|
+
else break asc_
|
|
497
|
+
end
|
|
498
|
+
} and asc.length >= 1
|
|
499
|
+
dat = "#{xlen == 1 ? 'db' : 'dw'} #{asc.inspect} "
|
|
500
|
+
aoff = asc.length * xlen
|
|
501
|
+
else
|
|
502
|
+
len = 1 if (len != 2 and len != 4 and len != 8) or len < 1
|
|
503
|
+
dat = "#{%w[x db dw x dd x x x dq][len]} #{Expression[s.decode_imm("u#{len*8}".to_sym, @dasm.cpu.endianness)]} "
|
|
504
|
+
aoff = len
|
|
505
|
+
end
|
|
506
|
+
elsif asc = str.inject('') { |asc_, c|
|
|
507
|
+
case c
|
|
508
|
+
when 10; break asc_ << c
|
|
509
|
+
when 0x20..0x7e, 9, 13; asc_ << c
|
|
510
|
+
else break asc_
|
|
511
|
+
end
|
|
512
|
+
} and asc.length > 3
|
|
513
|
+
len = asc.length
|
|
514
|
+
len = (1..len).find { |l| @dasm.xrefs[curaddr+l] or s.inv_export[s.ptr+l] or s.reloc[s.ptr+l] } || len
|
|
515
|
+
asc = asc[0, len]
|
|
516
|
+
dat = "db #{asc.inspect} "
|
|
517
|
+
aoff = asc.length
|
|
518
|
+
elsif rep = str.inject(0) { |rep_, c|
|
|
519
|
+
case c
|
|
520
|
+
when str[0]; rep_+1
|
|
521
|
+
else break rep_
|
|
522
|
+
end
|
|
523
|
+
} and rep > 4
|
|
524
|
+
rep = (1..rep).find { |l| @dasm.xrefs[curaddr+l] or s.inv_export[s.ptr+l] or s.reloc[s.ptr+l] } || rep
|
|
525
|
+
rep -= curaddr % 256 if rep == 256 and curaddr.kind_of? Integer
|
|
526
|
+
dat = "db #{Expression[rep]} dup(#{Expression[str[0]]}) "
|
|
527
|
+
aoff = rep
|
|
528
|
+
else
|
|
529
|
+
dat = "db #{Expression[str[0]]} "
|
|
530
|
+
aoff = 1
|
|
531
|
+
end
|
|
532
|
+
else
|
|
533
|
+
if @dasm.xrefs[curaddr]
|
|
534
|
+
comment = []
|
|
535
|
+
@dasm.each_xref(curaddr) { |xref|
|
|
536
|
+
len = xref.len if xref.len
|
|
537
|
+
comment << " #{xref.type}#{xref.len}:#{Expression[xref.origin]} "
|
|
538
|
+
}
|
|
539
|
+
len = 1 if (len != 2 and len != 4 and len != 8) or len < 1
|
|
540
|
+
dat = "#{%w[x db dw x dd x x x dq][len]} ? "
|
|
541
|
+
aoff = len
|
|
542
|
+
else
|
|
543
|
+
len = [len, s.length-s.ptr].min
|
|
544
|
+
len -= curaddr % 256 if len == 256 and curaddr.kind_of? Integer
|
|
545
|
+
len = (1..len).find { |l| @dasm.xrefs[curaddr+l] or s.inv_export[s.ptr+l] or s.reloc[s.ptr+l] } || len
|
|
546
|
+
dat = "db #{Expression[len]} dup(?) "
|
|
547
|
+
aoff = len
|
|
548
|
+
end
|
|
549
|
+
end
|
|
550
|
+
str_c << ["#{Expression[curaddr]} ", :address]
|
|
551
|
+
if @raw_data_length.to_i > 0
|
|
552
|
+
if s = @dasm.get_section_at(curaddr)
|
|
553
|
+
raw = s[0].read([aoff, @raw_data_length].min)
|
|
554
|
+
raw = raw.unpack('H*').first
|
|
555
|
+
else
|
|
556
|
+
raw = ''
|
|
557
|
+
end
|
|
558
|
+
raw = raw.ljust(@raw_data_length*2)
|
|
559
|
+
raw += (aoff > @raw_data_length ? '- ' : ' ')
|
|
560
|
+
str_c << [raw, :raw_data]
|
|
561
|
+
end
|
|
562
|
+
str_c << ["#{label} ", :label] if label
|
|
563
|
+
str_c << [dat.ljust(comment ? 24 : 0), :instruction]
|
|
564
|
+
str_c << [" ; #{comment.join(' ')}", :comment] if comment
|
|
565
|
+
nl[]
|
|
566
|
+
curaddr += aoff
|
|
567
|
+
else
|
|
568
|
+
nl[]
|
|
569
|
+
curaddr += 1
|
|
570
|
+
end
|
|
571
|
+
end
|
|
572
|
+
@line_address[w_h..-1] = [] if @line_address.length >= w_h
|
|
573
|
+
@caret_y = @line_address.rindex(@wantaddr) || @caret_y if @wantaddr
|
|
574
|
+
@wantaddr = nil
|
|
575
|
+
|
|
576
|
+
# convert arrows_addr to @arrows (with line numbers)
|
|
577
|
+
# updates @arrows_widget if @arrows changed
|
|
578
|
+
prev_arrows = @arrows
|
|
579
|
+
addr_line = {} # addr => last line (di)
|
|
580
|
+
@line_address.each_with_index { |a, l| addr_line[a] = l }
|
|
581
|
+
@arrows = arrows_addr.uniq.sort.map { |from, to|
|
|
582
|
+
[(addr_line[from] || (from < curaddr ? :up : :down) rescue :up),
|
|
583
|
+
(addr_line[ to ] || ( to < curaddr ? :up : :down) rescue :up)]
|
|
584
|
+
}
|
|
585
|
+
invalidate(0, 0, @arrow_zone_w, 100000) if prev_arrows != @arrows
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
def gui_update
|
|
589
|
+
# allows a focus_addr after an operation that changed section addresses (eg rebase)
|
|
590
|
+
addrs = @dasm.sections.keys.grep(Integer)
|
|
591
|
+
@minaddr = addrs.min.to_i
|
|
592
|
+
@maxaddr = (addrs.max + @dasm.sections[addrs.max].length rescue (1 << @dasm.cpu.size))
|
|
593
|
+
|
|
594
|
+
@want_update_line_text = true
|
|
595
|
+
redraw
|
|
596
|
+
end
|
|
597
|
+
end
|
|
598
|
+
end
|
|
599
|
+
end
|