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