metasm 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|