metasm 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (192) hide show
  1. data/BUGS +11 -0
  2. data/CREDITS +17 -0
  3. data/README +270 -0
  4. data/TODO +114 -0
  5. data/doc/code_organisation.txt +146 -0
  6. data/doc/const_missing.txt +16 -0
  7. data/doc/core_classes.txt +75 -0
  8. data/doc/feature_list.txt +53 -0
  9. data/doc/index.txt +59 -0
  10. data/doc/install_notes.txt +170 -0
  11. data/doc/style.css +3 -0
  12. data/doc/use_cases.txt +18 -0
  13. data/lib/metasm.rb +80 -0
  14. data/lib/metasm/arm.rb +12 -0
  15. data/lib/metasm/arm/debug.rb +39 -0
  16. data/lib/metasm/arm/decode.rb +167 -0
  17. data/lib/metasm/arm/encode.rb +77 -0
  18. data/lib/metasm/arm/main.rb +75 -0
  19. data/lib/metasm/arm/opcodes.rb +177 -0
  20. data/lib/metasm/arm/parse.rb +130 -0
  21. data/lib/metasm/arm/render.rb +55 -0
  22. data/lib/metasm/compile_c.rb +1457 -0
  23. data/lib/metasm/dalvik.rb +8 -0
  24. data/lib/metasm/dalvik/decode.rb +196 -0
  25. data/lib/metasm/dalvik/main.rb +60 -0
  26. data/lib/metasm/dalvik/opcodes.rb +366 -0
  27. data/lib/metasm/decode.rb +213 -0
  28. data/lib/metasm/decompile.rb +2659 -0
  29. data/lib/metasm/disassemble.rb +2068 -0
  30. data/lib/metasm/disassemble_api.rb +1280 -0
  31. data/lib/metasm/dynldr.rb +1329 -0
  32. data/lib/metasm/encode.rb +333 -0
  33. data/lib/metasm/exe_format/a_out.rb +194 -0
  34. data/lib/metasm/exe_format/autoexe.rb +82 -0
  35. data/lib/metasm/exe_format/bflt.rb +189 -0
  36. data/lib/metasm/exe_format/coff.rb +455 -0
  37. data/lib/metasm/exe_format/coff_decode.rb +901 -0
  38. data/lib/metasm/exe_format/coff_encode.rb +1078 -0
  39. data/lib/metasm/exe_format/dex.rb +457 -0
  40. data/lib/metasm/exe_format/dol.rb +145 -0
  41. data/lib/metasm/exe_format/elf.rb +923 -0
  42. data/lib/metasm/exe_format/elf_decode.rb +979 -0
  43. data/lib/metasm/exe_format/elf_encode.rb +1375 -0
  44. data/lib/metasm/exe_format/macho.rb +827 -0
  45. data/lib/metasm/exe_format/main.rb +228 -0
  46. data/lib/metasm/exe_format/mz.rb +164 -0
  47. data/lib/metasm/exe_format/nds.rb +172 -0
  48. data/lib/metasm/exe_format/pe.rb +437 -0
  49. data/lib/metasm/exe_format/serialstruct.rb +246 -0
  50. data/lib/metasm/exe_format/shellcode.rb +114 -0
  51. data/lib/metasm/exe_format/xcoff.rb +167 -0
  52. data/lib/metasm/gui.rb +23 -0
  53. data/lib/metasm/gui/cstruct.rb +373 -0
  54. data/lib/metasm/gui/dasm_coverage.rb +199 -0
  55. data/lib/metasm/gui/dasm_decomp.rb +369 -0
  56. data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
  57. data/lib/metasm/gui/dasm_graph.rb +1354 -0
  58. data/lib/metasm/gui/dasm_hex.rb +543 -0
  59. data/lib/metasm/gui/dasm_listing.rb +599 -0
  60. data/lib/metasm/gui/dasm_main.rb +906 -0
  61. data/lib/metasm/gui/dasm_opcodes.rb +291 -0
  62. data/lib/metasm/gui/debug.rb +1228 -0
  63. data/lib/metasm/gui/gtk.rb +884 -0
  64. data/lib/metasm/gui/qt.rb +495 -0
  65. data/lib/metasm/gui/win32.rb +3004 -0
  66. data/lib/metasm/gui/x11.rb +621 -0
  67. data/lib/metasm/ia32.rb +14 -0
  68. data/lib/metasm/ia32/compile_c.rb +1523 -0
  69. data/lib/metasm/ia32/debug.rb +193 -0
  70. data/lib/metasm/ia32/decode.rb +1167 -0
  71. data/lib/metasm/ia32/decompile.rb +564 -0
  72. data/lib/metasm/ia32/encode.rb +314 -0
  73. data/lib/metasm/ia32/main.rb +233 -0
  74. data/lib/metasm/ia32/opcodes.rb +872 -0
  75. data/lib/metasm/ia32/parse.rb +327 -0
  76. data/lib/metasm/ia32/render.rb +91 -0
  77. data/lib/metasm/main.rb +1193 -0
  78. data/lib/metasm/mips.rb +11 -0
  79. data/lib/metasm/mips/compile_c.rb +7 -0
  80. data/lib/metasm/mips/decode.rb +253 -0
  81. data/lib/metasm/mips/encode.rb +51 -0
  82. data/lib/metasm/mips/main.rb +72 -0
  83. data/lib/metasm/mips/opcodes.rb +443 -0
  84. data/lib/metasm/mips/parse.rb +51 -0
  85. data/lib/metasm/mips/render.rb +43 -0
  86. data/lib/metasm/os/gnu_exports.rb +270 -0
  87. data/lib/metasm/os/linux.rb +1112 -0
  88. data/lib/metasm/os/main.rb +1686 -0
  89. data/lib/metasm/os/remote.rb +527 -0
  90. data/lib/metasm/os/windows.rb +2027 -0
  91. data/lib/metasm/os/windows_exports.rb +745 -0
  92. data/lib/metasm/parse.rb +876 -0
  93. data/lib/metasm/parse_c.rb +3938 -0
  94. data/lib/metasm/pic16c/decode.rb +42 -0
  95. data/lib/metasm/pic16c/main.rb +17 -0
  96. data/lib/metasm/pic16c/opcodes.rb +68 -0
  97. data/lib/metasm/ppc.rb +11 -0
  98. data/lib/metasm/ppc/decode.rb +264 -0
  99. data/lib/metasm/ppc/decompile.rb +251 -0
  100. data/lib/metasm/ppc/encode.rb +51 -0
  101. data/lib/metasm/ppc/main.rb +129 -0
  102. data/lib/metasm/ppc/opcodes.rb +410 -0
  103. data/lib/metasm/ppc/parse.rb +52 -0
  104. data/lib/metasm/preprocessor.rb +1277 -0
  105. data/lib/metasm/render.rb +130 -0
  106. data/lib/metasm/sh4.rb +8 -0
  107. data/lib/metasm/sh4/decode.rb +336 -0
  108. data/lib/metasm/sh4/main.rb +292 -0
  109. data/lib/metasm/sh4/opcodes.rb +381 -0
  110. data/lib/metasm/x86_64.rb +12 -0
  111. data/lib/metasm/x86_64/compile_c.rb +1025 -0
  112. data/lib/metasm/x86_64/debug.rb +59 -0
  113. data/lib/metasm/x86_64/decode.rb +268 -0
  114. data/lib/metasm/x86_64/encode.rb +264 -0
  115. data/lib/metasm/x86_64/main.rb +135 -0
  116. data/lib/metasm/x86_64/opcodes.rb +118 -0
  117. data/lib/metasm/x86_64/parse.rb +68 -0
  118. data/misc/bottleneck.rb +61 -0
  119. data/misc/cheader-findpppath.rb +58 -0
  120. data/misc/hexdiff.rb +74 -0
  121. data/misc/hexdump.rb +55 -0
  122. data/misc/metasm-all.rb +13 -0
  123. data/misc/objdiff.rb +47 -0
  124. data/misc/objscan.rb +40 -0
  125. data/misc/pdfparse.rb +661 -0
  126. data/misc/ppc_pdf2oplist.rb +192 -0
  127. data/misc/tcp_proxy_hex.rb +84 -0
  128. data/misc/txt2html.rb +440 -0
  129. data/samples/a.out.rb +31 -0
  130. data/samples/asmsyntax.rb +77 -0
  131. data/samples/bindiff.rb +555 -0
  132. data/samples/compilation-steps.rb +49 -0
  133. data/samples/cparser_makestackoffset.rb +55 -0
  134. data/samples/dasm-backtrack.rb +38 -0
  135. data/samples/dasmnavig.rb +318 -0
  136. data/samples/dbg-apihook.rb +228 -0
  137. data/samples/dbghelp.rb +143 -0
  138. data/samples/disassemble-gui.rb +102 -0
  139. data/samples/disassemble.rb +133 -0
  140. data/samples/dump_upx.rb +95 -0
  141. data/samples/dynamic_ruby.rb +1929 -0
  142. data/samples/elf_list_needed.rb +46 -0
  143. data/samples/elf_listexports.rb +33 -0
  144. data/samples/elfencode.rb +25 -0
  145. data/samples/exeencode.rb +128 -0
  146. data/samples/factorize-headers-elfimports.rb +77 -0
  147. data/samples/factorize-headers-peimports.rb +109 -0
  148. data/samples/factorize-headers.rb +43 -0
  149. data/samples/gdbclient.rb +583 -0
  150. data/samples/generate_libsigs.rb +102 -0
  151. data/samples/hotfix_gtk_dbg.rb +59 -0
  152. data/samples/install_win_env.rb +78 -0
  153. data/samples/lindebug.rb +924 -0
  154. data/samples/linux_injectsyscall.rb +95 -0
  155. data/samples/machoencode.rb +31 -0
  156. data/samples/metasm-shell.rb +91 -0
  157. data/samples/pe-hook.rb +69 -0
  158. data/samples/pe-ia32-cpuid.rb +203 -0
  159. data/samples/pe-mips.rb +35 -0
  160. data/samples/pe-shutdown.rb +78 -0
  161. data/samples/pe-testrelocs.rb +51 -0
  162. data/samples/pe-testrsrc.rb +24 -0
  163. data/samples/pe_listexports.rb +31 -0
  164. data/samples/peencode.rb +19 -0
  165. data/samples/peldr.rb +494 -0
  166. data/samples/preprocess-flatten.rb +19 -0
  167. data/samples/r0trace.rb +308 -0
  168. data/samples/rubstop.rb +399 -0
  169. data/samples/scan_pt_gnu_stack.rb +54 -0
  170. data/samples/scanpeexports.rb +62 -0
  171. data/samples/shellcode-c.rb +40 -0
  172. data/samples/shellcode-dynlink.rb +146 -0
  173. data/samples/source.asm +34 -0
  174. data/samples/struct_offset.rb +47 -0
  175. data/samples/testpe.rb +32 -0
  176. data/samples/testraw.rb +45 -0
  177. data/samples/win32genloader.rb +132 -0
  178. data/samples/win32hooker-advanced.rb +169 -0
  179. data/samples/win32hooker.rb +96 -0
  180. data/samples/win32livedasm.rb +33 -0
  181. data/samples/win32remotescan.rb +133 -0
  182. data/samples/wintrace.rb +92 -0
  183. data/tests/all.rb +8 -0
  184. data/tests/dasm.rb +39 -0
  185. data/tests/dynldr.rb +35 -0
  186. data/tests/encodeddata.rb +132 -0
  187. data/tests/ia32.rb +82 -0
  188. data/tests/mips.rb +116 -0
  189. data/tests/parse_c.rb +239 -0
  190. data/tests/preprocessor.rb +269 -0
  191. data/tests/x86_64.rb +62 -0
  192. 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