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,199 @@
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 CoverageWidget < DrawableWidget
9
+ attr_accessor :dasm, :sections, :pixel_w, :pixel_h
10
+
11
+ # TODO wheel -> zoom, dragdrop -> scroll?(zoomed)
12
+ def initialize_widget(dasm, parent_widget)
13
+ @dasm = dasm
14
+ @parent_widget = parent_widget
15
+
16
+ @curaddr = 0
17
+ @pixel_w = @pixel_h = 2 # use a font ?
18
+ @sections = []
19
+ @section_x = []
20
+ @slave = nil # another dasmwidget whose curaddr is kept sync
21
+
22
+ @default_color_association = { :caret => :yellow, :caret_col => :darkyellow,
23
+ :background => :palegrey, :code => :red, :data => :blue }
24
+ end
25
+
26
+ def click(x, y)
27
+ x, y = x.to_i - 1, y.to_i
28
+ @sections.zip(@section_x).each { |(a, l, seq), (sx, sxe)|
29
+ if x >= sx and x < sxe+@pixel_w
30
+ @curaddr = a + (x-sx)/@pixel_w*@byte_per_col + (y/@pixel_h-@spacing)*@byte_per_col/@col_height
31
+ @slave.focus_addr(@curaddr) if @slave rescue @slave=nil
32
+ redraw
33
+ break
34
+ end
35
+ }
36
+ end
37
+
38
+ def doubleclick(x, y)
39
+ click(x, y)
40
+ cw = @parent_widget.clone_window(@curaddr, :listing)
41
+ @slave = cw.dasm_widget
42
+ @slave.focus_changed_callback = lambda { redraw rescue @slave.focus_changed_callback = nil }
43
+ end
44
+ alias rightclick doubleclick
45
+
46
+ def mouse_wheel(dir, x, y)
47
+ # TODO zoom ?
48
+ case dir
49
+ when :up
50
+ when :down
51
+ end
52
+ end
53
+
54
+ def paint
55
+ @curaddr = @slave.curaddr if @slave and @slave.curaddr rescue @slave=nil
56
+
57
+ @spacing = 4 # pixels left for borders / inter-section
58
+
59
+ @col_height = height/@pixel_h - 2*@spacing # pixels per column
60
+ @col_height = 1 if @col_height < 1
61
+
62
+ cols = width/@pixel_w - 2*@spacing
63
+ cols -= (@sections.length-1) * (@spacing+1) # space+1: last col of each section may be only 1byte long
64
+ cols = 64 if cols < 64
65
+
66
+ # find how much bytes we must stuff per pixel so that it fits in the window
67
+ bytes = @sections.map { |a, l, seq| l }.inject(0) { |a, b| a+b }
68
+ @byte_per_col = (bytes / cols + @col_height) / @col_height * @col_height
69
+ @byte_per_col = @col_height if @byte_per_col < @col_height
70
+
71
+ x = @spacing*@pixel_w
72
+ ybase = @spacing*@pixel_h
73
+
74
+ # draws a rectangle covering h1 to h2 in y, of width w
75
+ # advances x as needed
76
+ draw_rect = lambda { |h1, h2, rw|
77
+ h2 += 1
78
+ draw_rectangle(x, ybase+@pixel_h*h1, @pixel_w*rw, @pixel_h*(h2-h1))
79
+ rw -= 1 if h2 != @col_height
80
+ x += rw*@pixel_w
81
+ }
82
+
83
+ # draws rectangles to cover o1 to o2
84
+ draw = lambda { |o1, o2|
85
+ next if o1 > o2
86
+ o1_ = o1
87
+
88
+ o1 /= @byte_per_col / @col_height
89
+ o2 /= @byte_per_col / @col_height
90
+
91
+ o11 = o1 % @col_height
92
+ o12 = o1 / @col_height
93
+ o21 = o2 % @col_height
94
+ o22 = o2 / @col_height
95
+
96
+ p11 = (o1_ - 1) / (@byte_per_col / @col_height) % @col_height
97
+ x -= @pixel_w if o11 == @col_height-1 and o11 == p11
98
+
99
+ if o11 > 0
100
+ draw_rect[o11, (o12 == o22 ? o21 : @col_height-1), 1]
101
+ next if o12 == o22
102
+ o12 += 1
103
+ end
104
+
105
+ if o12 < o22
106
+ draw_rect[0, @col_height-1, o22-o12]
107
+ end
108
+
109
+ draw_rect[0, o21, 1]
110
+ }
111
+
112
+ @section_x = []
113
+ @sections.each { |a, l, seq|
114
+ curoff = 0
115
+ @section_x << [x]
116
+ seq += [[l, l-1]] if not seq[-1] or seq[-1][1] < l # to draw last data
117
+ seq.each { |o, oe|
118
+ draw_color :data
119
+ draw[curoff, o-1]
120
+ draw_color :code
121
+ draw[o, oe]
122
+ curoff = oe+1
123
+ }
124
+ @section_x.last << x
125
+ x += @spacing*@pixel_w
126
+ }
127
+
128
+ @sections.zip(@section_x).each { |(a, l, seq), (sx, sxe)|
129
+ next if @curaddr.kind_of? Integer and not a.kind_of? Integer
130
+ next if @curaddr.kind_of? Expression and not a.kind_of? Expression
131
+ co = @curaddr-a
132
+ if co >= 0 and co < l
133
+ draw_color :caret_col
134
+ x = sx + (co/@byte_per_col)*@pixel_w
135
+ draw_rect[-@spacing, -1, 1]
136
+ draw_rect[@col_height, @col_height+@spacing, 1]
137
+ draw_color :caret
138
+ y = (co*@col_height/@byte_per_col) % @col_height
139
+ y = (co % @byte_per_col) / (@byte_per_col/@col_height)
140
+ draw_rect[y, y, 1]
141
+ end
142
+ }
143
+ end
144
+
145
+ def get_cursor_pos
146
+ @curaddr
147
+ end
148
+
149
+ def set_cursor_pos(p)
150
+ @curaddr = p
151
+ @slave.focus_addr(@curaddr) if @slave rescue @slave=nil
152
+ redraw
153
+ end
154
+
155
+ # focus on addr
156
+ # returns true on success (address exists)
157
+ def focus_addr(addr)
158
+ return if not addr = @parent_widget.normalize(addr) or not @dasm.get_section_at(addr)
159
+ @curaddr = addr
160
+ @slave.focus_addr(@curaddr) if @slave rescue @slave=nil
161
+ gui_update
162
+ true
163
+ end
164
+
165
+ # returns the address of the data under the cursor
166
+ def current_address
167
+ @curaddr
168
+ end
169
+
170
+ def gui_update
171
+ # ary of section [addr, len, codespan]
172
+ # codespan is an ary of [code_off_start, code_off_end] (sorted by off)
173
+ @sections = @dasm.sections.map { |a, ed|
174
+ a = Expression[a].reduce
175
+ l = ed.length
176
+ if a.kind_of? Integer
177
+ l += a % 32
178
+ a -= a % 32
179
+ end
180
+ acc = []
181
+ # stuff with addr-section_addr is to handle non-numeric section addrs (eg elf ET_REL)
182
+ @dasm.decoded.keys.map { |da| da-a rescue nil }.grep(Integer).grep(0..l).sort.each { |o|
183
+ next if not da = @dasm.di_at(a+o)
184
+ oe = o + da.bin_length
185
+ if acc[-1] and acc[-1][1] >= o
186
+ # handle di overlapping
187
+ acc[-1][1] = oe if acc[-1][1] < oe
188
+ else
189
+ acc << [o, oe]
190
+ end
191
+ }
192
+ [a, l, acc]
193
+ }
194
+ @sections = @sections.sort if @sections.all? { |a, l, s| a.kind_of? Integer }
195
+ redraw
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,369 @@
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 CdecompListingWidget < DrawableWidget
9
+ attr_accessor :dasm, :curaddr, :tabwidth
10
+
11
+ def initialize_widget(dasm, parent_widget)
12
+ @dasm = dasm
13
+ @parent_widget = parent_widget
14
+
15
+ @view_x = @view_y = 0 # coord of corner of view in characters
16
+ @cwidth = @cheight = 1 # widget size in chars
17
+ @line_text = []
18
+ @line_text_col = [] # each line is [[:col, 'text'], [:col, 'text']]
19
+ @curaddr = nil
20
+ @tabwidth = 8
21
+
22
+ @default_color_association = { :text => :black, :keyword => :blue, :caret => :black,
23
+ :background => :white, :hl_word => :palered, :localvar => :darkred,
24
+ :globalvar => :darkgreen, :intrinsic => :darkyellow }
25
+ end
26
+
27
+ def curfunc
28
+ @dasm.c_parser and (@dasm.c_parser.toplevel.symbol[@curaddr] or @dasm.c_parser.toplevel.struct[@curaddr])
29
+ end
30
+
31
+ def click(x, y)
32
+ @caret_x = (x-1).to_i / @font_width + @view_x
33
+ @caret_y = y.to_i / @font_height + @view_y
34
+ update_caret
35
+ end
36
+
37
+ def rightclick(x, y)
38
+ click(x, y)
39
+ if @dasm.c_parser and @dasm.c_parser.toplevel.symbol[@hl_word]
40
+ @parent_widget.clone_window(@hl_word, :decompile)
41
+ elsif @hl_word
42
+ @parent_widget.clone_window(@hl_word)
43
+ end
44
+ end
45
+
46
+ def doubleclick(x, y)
47
+ click(x, y)
48
+ @parent_widget.focus_addr(@hl_word)
49
+ end
50
+
51
+ def mouse_wheel(dir, x, y)
52
+ case dir
53
+ when :up
54
+ if @caret_y > 0
55
+ @view_y -= 4
56
+ @caret_y -= 4
57
+ @caret_y = 0 if @caret_y < 0
58
+ end
59
+ when :down
60
+ if @caret_y < @line_text.length - 1
61
+ @view_y += 4
62
+ @caret_y += 4
63
+ redraw
64
+ end
65
+ end
66
+ redraw
67
+ end
68
+
69
+ def paint
70
+ @cwidth = width/@font_width
71
+ @cheight = height/@font_height
72
+
73
+ # adjust viewport to cursor
74
+ sz_x = @line_text.map { |l| l.length }.max.to_i + 1
75
+ sz_y = @line_text.length.to_i + 1
76
+ @view_x = @caret_x - @cwidth + 1 if @caret_x > @view_x + @cwidth - 1
77
+ @view_x = @caret_x if @caret_x < @view_x
78
+ @view_x = sz_x - @cwidth - 1 if @view_x >= sz_x - @cwidth
79
+ @view_x = 0 if @view_x < 0
80
+
81
+ @view_y = @caret_y - @cheight + 1 if @caret_y > @view_y + @cheight - 1
82
+ @view_y = @caret_y if @caret_y < @view_y
83
+ @view_y = sz_y - @cheight - 1 if @view_y >= sz_y - @cheight
84
+ @view_y = 0 if @view_y < 0
85
+
86
+ # current cursor position
87
+ x = 1
88
+ y = 0
89
+
90
+ # renders a string at current cursor position with a color
91
+ # must not include newline
92
+ render = lambda { |str, color|
93
+ # function ends when we write under the bottom of the listing
94
+ if @hl_word
95
+ stmp = str
96
+ pre_x = 0
97
+ while stmp =~ /^(.*?)(\b#{Regexp.escape @hl_word}\b)/
98
+ s1, s2 = $1, $2
99
+ pre_x += s1.length*@font_width
100
+ hl_w = s2.length*@font_width
101
+ draw_rectangle_color(:hl_word, x+pre_x, y, hl_w, @font_height)
102
+ pre_x += hl_w
103
+ stmp = stmp[s1.length+s2.length..-1]
104
+ end
105
+ end
106
+ draw_string_color(color, x, y, str)
107
+ x += str.length * @font_width
108
+ }
109
+
110
+ @line_text_col[@view_y, @cheight + 1].each { |l|
111
+ cx = 0
112
+ l.each { |c, t|
113
+ cx += t.length
114
+ if cx-t.length > @view_x + @cwidth + 1
115
+ elsif cx < @view_x
116
+ else
117
+ t = t[(@view_x - cx + t.length)..-1] if cx-t.length < @view_x
118
+ render[t, c]
119
+ end
120
+ }
121
+ x = 1
122
+ y += @font_height
123
+ }
124
+
125
+ if focus?
126
+ # draw caret
127
+ cx = (@caret_x-@view_x)*@font_width+1
128
+ cy = (@caret_y-@view_y)*@font_height
129
+ draw_line_color(:caret, cx, cy, cx, cy+@font_height-1)
130
+ end
131
+
132
+ @oldcaret_x, @oldcaret_y = @caret_x, @caret_y
133
+ end
134
+
135
+ def keypress(key)
136
+ case key
137
+ when :left
138
+ if @caret_x >= 1
139
+ @caret_x -= 1
140
+ update_caret
141
+ end
142
+ when :up
143
+ if @caret_y > 0
144
+ @caret_y -= 1
145
+ update_caret
146
+ end
147
+ when :right
148
+ if @caret_x < @line_text[@caret_y].to_s.length
149
+ @caret_x += 1
150
+ update_caret
151
+ end
152
+ when :down
153
+ if @caret_y < @line_text.length
154
+ @caret_y += 1
155
+ update_caret
156
+ end
157
+ when :home
158
+ @caret_x = @line_text[@caret_y].to_s[/^\s*/].length
159
+ update_caret
160
+ when :end
161
+ @caret_x = @line_text[@caret_y].to_s.length
162
+ update_caret
163
+ when :pgup
164
+ @caret_y -= @cheight/2
165
+ @caret_y = 0 if @caret_y < 0
166
+ update_caret
167
+ when :pgdown
168
+ @caret_y += @cheight/2
169
+ @caret_y = @line_text.length if @caret_y > @line_text.length
170
+ update_caret
171
+ when ?n # rename local/global variable
172
+ f = curfunc.initializer if curfunc and curfunc.initializer.kind_of? C::Block
173
+ n = @hl_word
174
+ if (f and f.symbol[n]) or @dasm.c_parser.toplevel.symbol[n]
175
+ @parent_widget.inputbox("new name for #{n}", :text => n) { |v|
176
+ if v !~ /^[a-z_$][a-z_0-9$]*$/i
177
+ @parent_widget.messagebox("invalid name #{v.inspect} !")
178
+ next
179
+ end
180
+ if f and f.symbol[n]
181
+ # TODO add/update comment to the asm instrs
182
+ s = f.symbol[v] = f.symbol.delete(n)
183
+ s.name = v
184
+ f.decompdata[:stackoff_name][s.stackoff] = v if s.stackoff
185
+ elsif @dasm.c_parser.toplevel.symbol[n]
186
+ @dasm.rename_label(n, v)
187
+ @curaddr = v if @curaddr == n
188
+ end
189
+ gui_update
190
+ }
191
+ end
192
+ when ?r # redecompile
193
+ @parent_widget.decompile(@curaddr)
194
+ when ?t # change variable type (you'll want to redecompile after that)
195
+ f = curfunc.initializer if curfunc.kind_of? C::Variable and curfunc.initializer.kind_of? C::Block
196
+ n = @hl_word
197
+ cp = @dasm.c_parser
198
+ if (f and s = f.symbol[n]) or s = cp.toplevel.symbol[n] or s = cp.toplevel.symbol[@curaddr]
199
+ s_ = s.dup
200
+ s_.initializer = nil if s.kind_of? C::Variable # for static var, avoid dumping the initializer in the textbox
201
+ s_.attributes &= C::Attributes::DECLSPECS if s_.attributes
202
+ @parent_widget.inputbox("new type for #{s.name}", :text => s_.dump_def(cp.toplevel)[0].join(' ')) { |t|
203
+ if t == ''
204
+ if s.type.kind_of? C::Function and s.initializer and s.initializer.decompdata
205
+ s.initializer.decompdata[:stackoff_type].clear
206
+ s.initializer.decompdata.delete :return_type
207
+ elsif s.kind_of? C::Variable and s.stackoff
208
+ f.decompdata[:stackoff_type].delete s.stackoff
209
+ end
210
+ next
211
+ end
212
+ begin
213
+ cp.lexer.feed(t)
214
+ raise 'bad type' if not v = C::Variable.parse_type(cp, cp.toplevel, true)
215
+ v.parse_declarator(cp, cp.toplevel)
216
+ if s.type.kind_of? C::Function and s.initializer and s.initializer.decompdata
217
+ # updated type of a decompiled func: update stack
218
+ vt = v.type.untypedef
219
+ vt = vt.type.untypedef if vt.kind_of? C::Pointer
220
+ raise 'function forever !' if not vt.kind_of? C::Function
221
+ # TODO _declspec
222
+ ao = 1
223
+ vt.args.to_a.each { |a|
224
+ next if a.has_attribute_var('register')
225
+ ao = (ao + [cp.sizeof(a), cp.typesize[:ptr]].max - 1) / cp.typesize[:ptr] * cp.typesize[:ptr]
226
+ s.initializer.decompdata[:stackoff_name][ao] = a.name if a.name
227
+ s.initializer.decompdata[:stackoff_type][ao] = a.type
228
+ ao += cp.sizeof(a)
229
+ }
230
+ s.initializer.decompdata[:return_type] = vt.type
231
+ s.type = v.type
232
+ else
233
+ f.decompdata[:stackoff_type][s.stackoff] = v.type if f and s.kind_of? C::Variable and s.stackoff
234
+ s.type = v.type
235
+ end
236
+ gui_update
237
+ rescue Object
238
+ @parent_widget.messagebox([$!.message, $!.backtrace].join("\n"), "error")
239
+ end
240
+ cp.readtok until cp.eos?
241
+ }
242
+ end
243
+ else return false
244
+ end
245
+ true
246
+ end
247
+
248
+ def get_cursor_pos
249
+ [@curaddr, @caret_x, @caret_y, @view_y]
250
+ end
251
+
252
+ def set_cursor_pos(p)
253
+ focus_addr p[0]
254
+ @caret_x, @caret_y, @view_y = p[1, 3]
255
+ update_caret
256
+ end
257
+
258
+ # hint that the caret moved
259
+ # redraws the caret, change the hilighted word, redraw if needed
260
+ def update_caret
261
+ redraw if @caret_x < @view_x or @caret_x >= @view_x + @cwidth or @caret_y < @view_y or @caret_y >= @view_y + @cheight
262
+
263
+ invalidate_caret(@oldcaret_x-@view_x, @oldcaret_y-@view_y)
264
+ invalidate_caret(@caret_x-@view_x, @caret_y-@view_y)
265
+ @oldcaret_x, @oldcaret_y = @caret_x, @caret_y
266
+
267
+ redraw if update_hl_word(@line_text[@caret_y], @caret_x)
268
+ end
269
+
270
+ # focus on addr
271
+ # returns true on success (address exists & decompiled)
272
+ def focus_addr(addr)
273
+ if @dasm.c_parser and (@dasm.c_parser.toplevel.symbol[addr] or @dasm.c_parser.toplevel.struct[addr])
274
+ @curaddr = addr
275
+ @caret_x = @caret_y = 0
276
+ gui_update
277
+ return true
278
+ end
279
+
280
+ return if not addr = @parent_widget.normalize(addr)
281
+
282
+ # scan up to func start/entrypoint
283
+ todo = [addr]
284
+ done = []
285
+ ep = @dasm.entrypoints.to_a.inject({}) { |h, e| h.update @dasm.normalize(e) => true }
286
+ while addr = todo.pop
287
+ next if not di = @dasm.di_at(addr)
288
+ addr = di.block.address
289
+ next if done.include?(addr) or not @dasm.di_at(addr)
290
+ done << addr
291
+ break if @dasm.function[addr] or ep[addr]
292
+ empty = true
293
+ @dasm.decoded[addr].block.each_from_samefunc(@dasm) { |na| empty = false ; todo << na }
294
+ break if empty
295
+ end
296
+ @dasm.auto_label_at(addr, 'loc') if @dasm.get_section_at(addr) and not @dasm.get_label_at(addr)
297
+ return if not l = @dasm.get_label_at(addr)
298
+ @curaddr = l
299
+ @caret_x = @caret_y = 0
300
+ gui_update
301
+ true
302
+ end
303
+
304
+ # returns the address of the data under the cursor
305
+ def current_address
306
+ @curaddr
307
+ end
308
+
309
+ def update_line_text
310
+ @line_text = curfunc.dump_def(@dasm.c_parser.toplevel)[0].map { |l| l.gsub("\t", ' '*@tabwidth) }
311
+ @line_text_col = []
312
+
313
+ if f = curfunc and f.kind_of? C::Variable and f.initializer.kind_of? C::Block
314
+ keyword_re = /\b(#{C::Keyword.keys.join('|')})\b/
315
+ intrinsic_re = /\b(intrinsic_\w+)\b/
316
+ lv = f.initializer.symbol.keys
317
+ lv << '00' if lv.empty?
318
+ localvar_re = /\b(#{lv.join('|')})\b/
319
+ globalvar_re = /\b(#{f.initializer.outer.symbol.keys.join('|')})\b/
320
+ end
321
+
322
+ @line_text.each { |l|
323
+ lc = []
324
+ if f
325
+ while l and l.length > 0
326
+ if (i_k = (l =~ keyword_re)) == 0
327
+ m = $1.length
328
+ col = :keyword
329
+ elsif (i_i = (l =~ intrinsic_re)) == 0
330
+ m = $1.length
331
+ col = :intrinsic
332
+ elsif (i_l = (l =~ localvar_re)) == 0
333
+ m = $1.length
334
+ col = :localvar
335
+ elsif (i_g = (l =~ globalvar_re)) == 0
336
+ m = $1.length
337
+ col = :globalvar
338
+ else
339
+ m = ([i_k, i_i, i_l, i_g, l.length] - [nil, false]).min
340
+ col = :text
341
+ end
342
+ lc << [col, l[0, m]]
343
+ l = l[m..-1]
344
+ end
345
+ else
346
+ lc << [:text, l]
347
+ end
348
+ @line_text_col << lc
349
+ }
350
+ end
351
+
352
+ def gui_update
353
+ if not curfunc and not @decompiling ||= false
354
+ @line_text = ['please wait']
355
+ @line_text_col = [[[:text, 'please wait']]]
356
+ redraw
357
+ @decompiling = true
358
+ @dasm.decompile_func(@curaddr)
359
+ @decompiling = false
360
+ end
361
+ if curfunc
362
+ update_line_text
363
+ update_caret
364
+ end
365
+ redraw
366
+ end
367
+ end
368
+ end
369
+ end