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.
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