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