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,543 @@
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 HexWidget < DrawableWidget
9
+ # data_size = size of data in bytes (1 => chars, 4 => dwords..)
10
+ # line_size = nr of bytes shown per line
11
+ # view_addr = addr of 1st byte to display
12
+ attr_accessor :dasm, :show_address, :show_data, :show_ascii,
13
+ :data_size, :line_size, :endianness,
14
+ #:data_sign, :data_hex,
15
+ :caret_x_data, :focus_zone,
16
+ :keep_aligned, :relative_addr, :hl_curbyte,
17
+ :view_addr, :write_pending
18
+
19
+ def initialize_widget(dasm, parent_widget)
20
+ @dasm = dasm
21
+ @parent_widget = parent_widget
22
+
23
+ # @caret_x = caret position in octets
24
+ # in hex, round to nearest @data_size and add @caret_x_data (nibbles)
25
+ @x_data = 7
26
+ @caret_x_data = 0
27
+ @oldcaret_x_data = 42
28
+ @focus_zone = @oldfocus_zone = :hex
29
+ @addr_min = @dasm.sections.keys.grep(Integer).min rescue nil
30
+ @addr_max = @dasm.sections.map { |s, e| s + e.length }.max rescue nil
31
+ @view_addr = @dasm.prog_binding['entrypoint'] || @addr_min || 0
32
+ @show_address = @show_data = @show_ascii = true
33
+ @data_size = 1
34
+ @line_size = 16
35
+ @num_lines = 2 # height of widget in lines
36
+ @write_pending = {} # addr -> newvalue (characters)
37
+ @endianness = @dasm.cpu.endianness
38
+ @raw_data_cache = {} # addr -> raw @line_size data at addr
39
+ #@data_sign = false
40
+ #@data_hex = true
41
+ @keep_aligned = false # true to keep the topleft octet a multiple of linewidth
42
+ @relative_addr = nil # show '+42h' in the addr column if not nil
43
+ @hl_curbyte = true # draw grey bg for current byte
44
+
45
+ @default_color_association = { :ascii => :black, :data => :black,
46
+ :address => :blue, :caret => :black, :background => :white,
47
+ :write_pending => :darkred, :caret_mirror => :palegrey }
48
+ end
49
+
50
+ def resized(w, h)
51
+ wc = w/@font_width
52
+ hc = h/@font_height
53
+ ca = current_address
54
+ @num_lines = hc
55
+ @caret_y = hc-1 if @caret_y >= hc
56
+ ols = @line_size
57
+ @line_size = 8
58
+ @line_size *= 2 while x_ascii+(@show_ascii ? @line_size : 0) < wc # booh..
59
+ @line_size /= 2
60
+ if @line_size != ols
61
+ @view_addr &= -@line_size if @keep_aligned
62
+ focus_addr ca
63
+ gui_update
64
+ end
65
+ end
66
+
67
+ # converts a screen x coord (in characters) to a [@caret_x, @caret_x_data, @focus_zone]
68
+ def chroff_to_caretx(x)
69
+ if x < x_data
70
+ [0, 0, (@show_data ? :hex : :ascii)]
71
+ elsif x < x_ascii
72
+ x -= x_data
73
+ x -= x/(4*(2*@data_size+1)+1) # remove space after each 4*@data_size
74
+ x -= x/(2*@data_size+1) # remove space after each @data_size
75
+ x = 2*@line_size-1 if x >= 2*@line_size # between hex & ascii
76
+ cx = x/(2*@data_size)*@data_size
77
+ cxd = x-2*cx
78
+ [cx, cxd, :hex]
79
+ elsif x < x_ascii+@line_size
80
+ x -= x_ascii
81
+ [x, 0, :ascii]
82
+ else
83
+ [@line_size-1, 0, (@show_ascii ? :ascii : :hex)]
84
+ end
85
+ end
86
+
87
+ def click(x, y)
88
+ @caret_x, @caret_x_data, @focus_zone = chroff_to_caretx((x-1).to_i / @font_width)
89
+ @caret_y = y.to_i / @font_height
90
+ update_caret
91
+ end
92
+
93
+ def rightclick(x, y)
94
+ doubleclick(x, y)
95
+ end
96
+
97
+ def doubleclick(x, y)
98
+ if x < @x_data * @font_width
99
+ if @relative_addr
100
+ @relative_addr = nil
101
+ else
102
+ @relative_addr = @view_addr
103
+ end
104
+ else
105
+ @data_size = {1 => 2, 2 => 4, 4 => 8, 8 => 1}[@data_size]
106
+ end
107
+ redraw
108
+ end
109
+
110
+ def mouse_wheel(dir, x, y)
111
+ off = height.to_i/@font_height/4*@line_size
112
+ case dir
113
+ when :up; @view_addr -= off
114
+ when :down; @view_addr += off
115
+ end
116
+ gui_update
117
+ end
118
+
119
+ # returns 1 line of data
120
+ def data_at(addr, len=@line_size)
121
+ if len == @line_size and l = @raw_data_cache[addr]
122
+ l
123
+ elsif s = @dasm.get_section_at(addr)
124
+ l = s[0].read(len)
125
+ @raw_data_cache[addr] = l if len == @line_size
126
+ l
127
+ end
128
+ end
129
+
130
+ def paint
131
+ w_h = height
132
+ curaddr = @view_addr
133
+ # current window position
134
+ x = 1
135
+ y = 0
136
+ @num_lines = 0
137
+
138
+ # renders a string at current cursor position with a color
139
+ # must not include newline
140
+ render = lambda { |str, color|
141
+ draw_string_color(color, x, y, str)
142
+ x += str.length * @font_width
143
+ }
144
+
145
+ if @show_address
146
+ @x_data = [6, Expression[curaddr].to_s.length].max + 1
147
+ end
148
+
149
+ xd = x_data*@font_width + 1
150
+ xa = x_ascii*@font_width + 1
151
+ hexfmt = "%0#{@data_size*2}x "
152
+ wp_win = {} # @write_pending clipped to current window
153
+ if not @write_pending.empty?
154
+ if curaddr.kind_of? Integer
155
+ @write_pending.keys.grep(curaddr...curaddr+(w_h/@font_height+1)*@line_size).each { |k| wp_win[k] = @write_pending[k] }
156
+ else wp_win = @write_pending.dup
157
+ end
158
+ end
159
+
160
+ # draw text until screen is full
161
+ while y < w_h
162
+ if @show_address
163
+ if @relative_addr
164
+ diff = Expression[curaddr] - @relative_addr
165
+ if diff.kind_of? Integer
166
+ addr = "#{'+' if diff >= 0}#{Expression[diff]}".ljust(@x_data-1)
167
+ else
168
+ addr = "#{Expression[curaddr]}"
169
+ end
170
+ else
171
+ addr = "#{Expression[curaddr]}"
172
+ end
173
+ render[addr.rjust(@x_data-1, '0'), :address]
174
+ end
175
+
176
+ d = data_at(curaddr)
177
+ if not d and data_at(curaddr+@line_size-1, 1)
178
+ # data in the current line but not from the beginning
179
+ d_o = (1...@line_size).find { |o| d = data_at(curaddr+o, @line_size-o) }.to_i
180
+ else
181
+ d_o = 0
182
+ end
183
+ wp = {}
184
+ d.length.times { |o|
185
+ if c = wp_win[curaddr+d_o+o]
186
+ wp[d_o+o] = true
187
+ d = d.dup
188
+ d[o, 1] = c.chr
189
+ end
190
+ } if d
191
+ if @show_data and d
192
+ x = xd
193
+ if d_o > 0
194
+ d_do = [0].pack('C')*(d_o % @data_size) + d
195
+ i = d_o/@data_size
196
+ x += (i*(@data_size*2+1) + i/4) * @font_width
197
+ else
198
+ d_do = d
199
+ i = 0
200
+ end
201
+ # XXX non-hex display ? (signed int, float..)
202
+ case @data_size
203
+ when 1; pak = 'C*'
204
+ when 2; pak = (@endianness == :little ? 'v*' : 'n*')
205
+ when 4; pak = (@endianness == :little ? 'V*' : 'N*')
206
+ when 8; pak = 'Q*' # XXX endianness..
207
+ end
208
+ awp = {} ; wp.each_key { |k| awp[k/@data_size] = true }
209
+
210
+ if @hl_curbyte and @caret_y == y/@font_height
211
+ cx = (x_data + x_data_cur(@caret_x, 0))*@font_width + 1
212
+ draw_rectangle_color(:caret_mirror, cx, y, @data_size*2*@font_width, @font_height)
213
+ end
214
+
215
+ if awp.empty?
216
+ s = ''
217
+ d_do.unpack(pak).each { |b|
218
+ s << (hexfmt % b)
219
+ s << ' ' if i & 3 == 3
220
+ i += 1
221
+ }
222
+ render[s, :data]
223
+ else
224
+ d_do.unpack(pak).each { |b|
225
+ col = awp[i] ? :write_pending : :data
226
+ render[hexfmt % b, col]
227
+ render[' ', :data] if i & 3 == 3
228
+ i+=1
229
+ }
230
+ end
231
+ end
232
+ if @show_ascii and d
233
+ x = xa + d_o*@font_width
234
+ d = d.gsub(/[^\x20-\x7e]/, '.')
235
+ if wp.empty?
236
+ render[d, :ascii]
237
+ else
238
+ d.length.times { |o|
239
+ col = wp[o] ? :write_pending : :ascii
240
+ render[d[o, 1], col]
241
+ }
242
+ end
243
+ end
244
+
245
+ curaddr += @line_size
246
+ @num_lines += 1
247
+ x = 1
248
+ y += @font_height
249
+ end
250
+
251
+ # draw caret
252
+ if @show_data
253
+ cx = (x_data + x_data_cur)*@font_width+1
254
+ cy = @caret_y*@font_height
255
+ col = (focus? && @focus_zone == :hex) ? :caret : :caret_mirror
256
+ draw_line_color(col, cx, cy, cx, cy+@font_height-1)
257
+ end
258
+
259
+ if @show_ascii
260
+ cx = (x_ascii + @caret_x)*@font_width+1
261
+ cy = @caret_y*@font_height
262
+ col = (focus? && @focus_zone == :ascii) ? :caret : :caret_mirror
263
+ draw_line_color(col, cx, cy, cx, cy+@font_height-1)
264
+ end
265
+
266
+ @oldcaret_x, @oldcaret_y, @oldcaret_x_data, @oldfocus_zone = @caret_x, @caret_y, @caret_x_data, @focus_zone
267
+ end
268
+
269
+ # char x of start of data zone
270
+ def x_data
271
+ @show_address ? @x_data : 0
272
+ end
273
+
274
+ # char x of start of ascii zone
275
+ def x_ascii
276
+ x_data + (@show_data ? @line_size*2 + @line_size/@data_size + @line_size/@data_size/4 : 0)
277
+ end
278
+
279
+ # current offset in data zone of caret
280
+ def x_data_cur(cx = @caret_x, cxd = @caret_x_data)
281
+ x = (cx/@data_size)*@data_size
282
+ 2*x + x/@data_size + x/@data_size/4 + cxd
283
+ end
284
+
285
+ def keypress(key)
286
+ case key
287
+ when :left
288
+ key_left
289
+ update_caret
290
+ when :right
291
+ key_right
292
+ update_caret
293
+ when :up
294
+ key_up
295
+ update_caret
296
+ when :down
297
+ key_down
298
+ update_caret
299
+ when :pgup
300
+ if not @addr_min or @view_addr > @addr_min
301
+ @view_addr -= (@num_lines/2)*@line_size
302
+ gui_update
303
+ end
304
+ when :pgdown
305
+ if not @addr_max or @view_addr < @addr_max
306
+ @view_addr += (@num_lines/2)*@line_size
307
+ gui_update
308
+ end
309
+ when :home
310
+ @caret_x = 0
311
+ update_caret
312
+ when :end
313
+ @caret_x = @line_size-1
314
+ update_caret
315
+
316
+ when :backspace
317
+ key_left
318
+ if @focus_zone == :hex
319
+ key_left if @caret_x_data & 1 == 1
320
+ oo = @caret_x_data/2
321
+ oo = @data_size - oo - 1 if @endianness == :little
322
+ @write_pending.delete current_address + oo
323
+ else
324
+ @write_pending.delete current_address
325
+ end
326
+ redraw
327
+ when :tab
328
+ switch_focus_zone
329
+ update_caret
330
+ when :enter
331
+ commit_writes
332
+ gui_update
333
+ when :esc
334
+ if not @write_pending.empty?
335
+ @write_pending.clear
336
+ redraw
337
+ else return false
338
+ end
339
+
340
+ when ?\x20..?\x7e
341
+ if @focus_zone == :hex
342
+ if ?a.kind_of?(String) # ruby1.9
343
+ v = key.ord
344
+ case key
345
+ when ?0..?9; v -= ?0.ord
346
+ when ?a..?f; v -= ?a.ord-10
347
+ when ?A..?F; v -= ?A.ord-10
348
+ else return false
349
+ end
350
+ else
351
+ case v = key
352
+ when ?0..?9; v -= ?0
353
+ when ?a..?f; v -= ?a-10
354
+ when ?A..?F; v -= ?A-10
355
+ else return false
356
+ end
357
+ end
358
+
359
+ oo = @caret_x_data/2
360
+ oo = @data_size - oo - 1 if @endianness == :little
361
+ baddr = current_address + oo
362
+ return false if not d = data_at(baddr, 1)
363
+ o = 4*((@caret_x_data+1) % 2)
364
+ @write_pending[baddr] ||= d[0]
365
+ if ?a.kind_of?(String)
366
+ @write_pending[baddr] = ((@write_pending[baddr].ord & ~(0xf << o)) | (v << o)).chr
367
+ else
368
+ @write_pending[baddr] = (@write_pending[baddr] & ~(0xf << o)) | (v << o)
369
+ end
370
+ else
371
+ @write_pending[current_address] = key
372
+ end
373
+ key_right
374
+ redraw
375
+ else return false
376
+ end
377
+ true
378
+ end
379
+
380
+ def keypress_ctrl(key)
381
+ case key
382
+ when ?f
383
+ if @focus_zone == :hex
384
+ prompt_search_hex
385
+ else
386
+ prompt_search_ascii
387
+ end
388
+ else return false
389
+ end
390
+ true
391
+ end
392
+
393
+ # pop a dialog, scans the sections for a hex pattern
394
+ def prompt_search_hex
395
+ inputbox('hex pattern to search (hex regexp, use .. for wildcard)') { |pat|
396
+ pat = pat.gsub(' ', '').gsub('..', '.').gsub(/[0-9a-f][0-9a-f]/i) { |o| "\\x#{o}" }
397
+ pat = Regexp.new(pat, Regexp::MULTILINE, 'n') # 'n' = force ascii-8bit
398
+ list = [['addr']] + @dasm.pattern_scan(pat).map { |a| [Expression[a]] }
399
+ listwindow("hex search #{pat}", list) { |i| focus_addr i[0] }
400
+ }
401
+ end
402
+
403
+ # pop a dialog, scans the sections for a regex
404
+ def prompt_search_ascii
405
+ inputbox('data pattern to search (regexp)') { |pat|
406
+ list = [['addr']] + @dasm.pattern_scan(/#{pat}/).map { |a| [Expression[a]] }
407
+ listwindow("data search #{pat}", list) { |i| focus_addr i[0] }
408
+ }
409
+ end
410
+
411
+ def key_left
412
+ if @focus_zone == :hex
413
+ if @caret_x_data > 0
414
+ @caret_x_data -= 1
415
+ else
416
+ @caret_x_data = @data_size*2-1
417
+ @caret_x -= @data_size
418
+ end
419
+ else
420
+ @caret_x -= 1
421
+ end
422
+ if @caret_x < 0
423
+ @caret_x += @line_size
424
+ key_up
425
+ end
426
+ end
427
+
428
+ def key_right
429
+ if @focus_zone == :hex
430
+ if @caret_x_data < @data_size*2-1
431
+ @caret_x_data += 1
432
+ else
433
+ @caret_x_data = 0
434
+ @caret_x += @data_size
435
+ end
436
+ else
437
+ @caret_x += 1
438
+ end
439
+ if @caret_x >= @line_size
440
+ @caret_x = 0
441
+ key_down
442
+ end
443
+ end
444
+
445
+ def key_up
446
+ if @caret_y > 0
447
+ @caret_y -= 1
448
+ elsif not @addr_min or @view_addr > @addr_min
449
+ @view_addr -= @line_size
450
+ redraw
451
+ else
452
+ @caret_x = @caret_x_data = 0
453
+ end
454
+ end
455
+
456
+ def key_down
457
+ if @caret_y < @num_lines-2
458
+ @caret_y += 1
459
+ elsif not @addr_max or @view_addr < @addr_max
460
+ @view_addr += @line_size
461
+ redraw
462
+ else
463
+ @caret_x = @line_size-1 # XXX partial final line... (01 23 45 bla )
464
+ @caret_x_data = @data_size*2-1
465
+ end
466
+ end
467
+
468
+ def switch_focus_zone(n=nil)
469
+ n ||= { :hex => :ascii, :ascii => :hex }[@focus_zone]
470
+ @caret_x = @caret_x / @data_size * @data_size if n == :hex
471
+ @caret_x_data = 0
472
+ @focus_zone = n
473
+ end
474
+
475
+ def commit_writes
476
+ a = s = nil
477
+ @write_pending.each { |k, v|
478
+ if not s or k < a or k >= a + s.length
479
+ s, a = @dasm.get_section_at(k)
480
+ end
481
+ next if not s
482
+ s[k-a] = v
483
+ }
484
+ @write_pending.clear
485
+ rescue
486
+ @parent_widget.messagebox($!, $!.class.to_s)
487
+ end
488
+
489
+ def get_cursor_pos
490
+ [@view_addr, @caret_x, @caret_y, @caret_x_data, @focus_zone]
491
+ end
492
+
493
+ def set_cursor_pos(p)
494
+ @view_addr, @caret_x, @caret_y, @caret_x_data, @focus_zone = p
495
+ redraw
496
+ update_caret
497
+ end
498
+
499
+ # hint that the caret moved
500
+ def update_caret
501
+ return redraw if @hl_curbyte
502
+ a = []
503
+ a << [x_data + x_data_cur, @caret_y] << [x_data + x_data_cur(@oldcaret_x, @oldcaret_x_data), @oldcaret_y] if @show_data
504
+ a << [x_ascii + @caret_x, @caret_y] << [x_ascii + @oldcaret_x, @oldcaret_y] if @show_ascii
505
+ a.each { |x, y| invalidate_caret(x, y) }
506
+ @oldcaret_x, @oldcaret_y, @oldcaret_x_data, @oldfocus_zone = @caret_x, @caret_y, @caret_x_data, @focus_zone
507
+ end
508
+
509
+ # focus on addr
510
+ # returns true on success (address exists)
511
+ def focus_addr(addr)
512
+ return if not addr = @parent_widget.normalize(addr)
513
+ if addr.kind_of? Integer
514
+ return if @addr_min and (addr < @addr_min or addr > @addr_max)
515
+ addr &= -@line_size if @keep_aligned
516
+ @view_addr = addr if addr < @view_addr or addr >= @view_addr+(@num_lines-2)*@line_size
517
+ elsif s = @dasm.get_section_at(addr)
518
+ @view_addr = Expression[s[1]]
519
+ else return
520
+ end
521
+ @caret_x = (addr-@view_addr) % @line_size
522
+ @caret_x_data = 0
523
+ @caret_y = (addr-@view_addr) / @line_size
524
+ @focus_zone = :ascii
525
+ redraw
526
+ update_caret
527
+ true
528
+ end
529
+
530
+ # returns the address of the data under the cursor
531
+ def current_address
532
+ @view_addr + @caret_y.to_i*@line_size + @caret_x.to_i
533
+ end
534
+
535
+ def gui_update
536
+ @addr_min = @dasm.sections.keys.grep(Integer).min rescue nil
537
+ @addr_max = @dasm.sections.map { |s, e| s + e.length }.max rescue nil
538
+ @raw_data_cache.clear
539
+ redraw
540
+ end
541
+ end
542
+ end
543
+ end