metasm 1.0.1 → 1.0.2

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 (235) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.hgtags +3 -0
  4. data/Gemfile +1 -0
  5. data/INSTALL +61 -0
  6. data/LICENCE +458 -0
  7. data/README +29 -21
  8. data/Rakefile +10 -0
  9. data/TODO +10 -12
  10. data/doc/code_organisation.txt +2 -0
  11. data/doc/core/DynLdr.txt +247 -0
  12. data/doc/core/ExeFormat.txt +43 -0
  13. data/doc/core/Expression.txt +220 -0
  14. data/doc/core/GNUExports.txt +27 -0
  15. data/doc/core/Ia32.txt +236 -0
  16. data/doc/core/SerialStruct.txt +108 -0
  17. data/doc/core/VirtualString.txt +145 -0
  18. data/doc/core/WindowsExports.txt +61 -0
  19. data/doc/core/index.txt +1 -0
  20. data/doc/style.css +6 -3
  21. data/doc/usage/debugger.txt +327 -0
  22. data/doc/usage/index.txt +1 -0
  23. data/doc/use_cases.txt +2 -2
  24. data/metasm.gemspec +22 -0
  25. data/{lib/metasm.rb → metasm.rb} +11 -3
  26. data/{lib/metasm → metasm}/compile_c.rb +13 -7
  27. data/metasm/cpu/arc.rb +8 -0
  28. data/metasm/cpu/arc/decode.rb +425 -0
  29. data/metasm/cpu/arc/main.rb +191 -0
  30. data/metasm/cpu/arc/opcodes.rb +588 -0
  31. data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
  32. data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
  33. data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
  34. data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
  35. data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
  36. data/metasm/cpu/arm/opcodes.rb +324 -0
  37. data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
  38. data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
  39. data/metasm/cpu/arm64.rb +15 -0
  40. data/metasm/cpu/arm64/debug.rb +38 -0
  41. data/metasm/cpu/arm64/decode.rb +289 -0
  42. data/metasm/cpu/arm64/encode.rb +41 -0
  43. data/metasm/cpu/arm64/main.rb +105 -0
  44. data/metasm/cpu/arm64/opcodes.rb +232 -0
  45. data/metasm/cpu/arm64/parse.rb +20 -0
  46. data/metasm/cpu/arm64/render.rb +95 -0
  47. data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
  48. data/metasm/cpu/bpf/decode.rb +142 -0
  49. data/metasm/cpu/bpf/main.rb +60 -0
  50. data/metasm/cpu/bpf/opcodes.rb +81 -0
  51. data/metasm/cpu/bpf/render.rb +41 -0
  52. data/metasm/cpu/cy16.rb +9 -0
  53. data/metasm/cpu/cy16/decode.rb +253 -0
  54. data/metasm/cpu/cy16/main.rb +63 -0
  55. data/metasm/cpu/cy16/opcodes.rb +78 -0
  56. data/metasm/cpu/cy16/render.rb +41 -0
  57. data/metasm/cpu/dalvik.rb +11 -0
  58. data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
  59. data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
  60. data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
  61. data/metasm/cpu/ia32.rb +17 -0
  62. data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
  63. data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
  64. data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
  65. data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
  66. data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
  67. data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
  68. data/metasm/cpu/ia32/opcodes.rb +1424 -0
  69. data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
  70. data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
  71. data/metasm/cpu/mips.rb +14 -0
  72. data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
  73. data/metasm/cpu/mips/debug.rb +42 -0
  74. data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
  75. data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
  76. data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
  77. data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
  78. data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
  79. data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
  80. data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
  81. data/metasm/cpu/msp430/decode.rb +247 -0
  82. data/metasm/cpu/msp430/main.rb +62 -0
  83. data/metasm/cpu/msp430/opcodes.rb +101 -0
  84. data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
  85. data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
  86. data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
  87. data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
  88. data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
  89. data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
  90. data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
  91. data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
  92. data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
  93. data/metasm/cpu/ppc/parse.rb +55 -0
  94. data/metasm/cpu/python.rb +8 -0
  95. data/metasm/cpu/python/decode.rb +136 -0
  96. data/metasm/cpu/python/main.rb +36 -0
  97. data/metasm/cpu/python/opcodes.rb +180 -0
  98. data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
  99. data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
  100. data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
  101. data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
  102. data/metasm/cpu/x86_64.rb +15 -0
  103. data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
  104. data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
  105. data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
  106. data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
  107. data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
  108. data/metasm/cpu/x86_64/opcodes.rb +136 -0
  109. data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
  110. data/metasm/cpu/x86_64/render.rb +35 -0
  111. data/metasm/cpu/z80.rb +9 -0
  112. data/metasm/cpu/z80/decode.rb +313 -0
  113. data/metasm/cpu/z80/main.rb +67 -0
  114. data/metasm/cpu/z80/opcodes.rb +224 -0
  115. data/metasm/cpu/z80/render.rb +59 -0
  116. data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
  117. data/{lib/metasm → metasm}/decode.rb +35 -4
  118. data/{lib/metasm → metasm}/decompile.rb +15 -16
  119. data/{lib/metasm → metasm}/disassemble.rb +201 -45
  120. data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
  121. data/{lib/metasm → metasm}/dynldr.rb +220 -133
  122. data/{lib/metasm → metasm}/encode.rb +10 -1
  123. data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
  124. data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
  125. data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
  126. data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
  127. data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
  128. data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
  129. data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
  130. data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
  131. data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
  132. data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
  133. data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
  134. data/metasm/exe_format/gb.rb +65 -0
  135. data/metasm/exe_format/javaclass.rb +424 -0
  136. data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
  137. data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
  138. data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
  139. data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
  140. data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
  141. data/metasm/exe_format/pyc.rb +167 -0
  142. data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
  143. data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
  144. data/metasm/exe_format/shellcode_rwx.rb +114 -0
  145. data/metasm/exe_format/swf.rb +205 -0
  146. data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
  147. data/metasm/exe_format/zip.rb +335 -0
  148. data/metasm/gui.rb +13 -0
  149. data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
  150. data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
  151. data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
  152. data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
  153. data/metasm/gui/dasm_graph.rb +1695 -0
  154. data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
  155. data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
  156. data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
  157. data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
  158. data/{lib/metasm → metasm}/gui/debug.rb +93 -27
  159. data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
  160. data/{lib/metasm → metasm}/gui/qt.rb +12 -2
  161. data/{lib/metasm → metasm}/gui/win32.rb +179 -42
  162. data/{lib/metasm → metasm}/gui/x11.rb +59 -59
  163. data/{lib/metasm → metasm}/main.rb +389 -264
  164. data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
  165. data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
  166. data/{lib/metasm → metasm}/os/linux.rb +628 -151
  167. data/metasm/os/main.rb +330 -0
  168. data/{lib/metasm → metasm}/os/windows.rb +132 -42
  169. data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
  170. data/{lib/metasm → metasm}/parse.rb +26 -24
  171. data/{lib/metasm → metasm}/parse_c.rb +221 -116
  172. data/{lib/metasm → metasm}/preprocessor.rb +55 -40
  173. data/{lib/metasm → metasm}/render.rb +14 -38
  174. data/misc/hexdump.rb +2 -1
  175. data/misc/lint.rb +58 -0
  176. data/misc/txt2html.rb +9 -7
  177. data/samples/bindiff.rb +3 -4
  178. data/samples/dasm-plugins/bindiff.rb +15 -0
  179. data/samples/dasm-plugins/bookmark.rb +133 -0
  180. data/samples/dasm-plugins/c_constants.rb +57 -0
  181. data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
  182. data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
  183. data/samples/dasm-plugins/dasm_all.rb +70 -0
  184. data/samples/dasm-plugins/demangle_cpp.rb +31 -0
  185. data/samples/dasm-plugins/deobfuscate.rb +251 -0
  186. data/samples/dasm-plugins/dump_text.rb +35 -0
  187. data/samples/dasm-plugins/export_graph_svg.rb +86 -0
  188. data/samples/dasm-plugins/findgadget.rb +75 -0
  189. data/samples/dasm-plugins/hl_opcode.rb +32 -0
  190. data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
  191. data/samples/dasm-plugins/imm2off.rb +34 -0
  192. data/samples/dasm-plugins/match_libsigs.rb +93 -0
  193. data/samples/dasm-plugins/patch_file.rb +95 -0
  194. data/samples/dasm-plugins/scanfuncstart.rb +36 -0
  195. data/samples/dasm-plugins/scanxrefs.rb +26 -0
  196. data/samples/dasm-plugins/selfmodify.rb +197 -0
  197. data/samples/dasm-plugins/stringsxrefs.rb +28 -0
  198. data/samples/dasmnavig.rb +1 -1
  199. data/samples/dbg-apihook.rb +24 -9
  200. data/samples/dbg-plugins/heapscan.rb +283 -0
  201. data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
  202. data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
  203. data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
  204. data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
  205. data/samples/dbg-plugins/heapscan/winheap.h +174 -0
  206. data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
  207. data/samples/dbg-plugins/trace_func.rb +214 -0
  208. data/samples/disassemble-gui.rb +35 -5
  209. data/samples/disassemble.rb +31 -6
  210. data/samples/dump_upx.rb +24 -12
  211. data/samples/dynamic_ruby.rb +12 -3
  212. data/samples/exeencode.rb +6 -5
  213. data/samples/factorize-headers-peimports.rb +1 -1
  214. data/samples/lindebug.rb +175 -381
  215. data/samples/metasm-shell.rb +1 -2
  216. data/samples/peldr.rb +2 -2
  217. data/tests/all.rb +1 -1
  218. data/tests/arc.rb +26 -0
  219. data/tests/dynldr.rb +22 -4
  220. data/tests/expression.rb +55 -0
  221. data/tests/graph_layout.rb +285 -0
  222. data/tests/ia32.rb +79 -26
  223. data/tests/mips.rb +9 -2
  224. data/tests/x86_64.rb +66 -18
  225. metadata +330 -218
  226. data/lib/metasm/arm/opcodes.rb +0 -177
  227. data/lib/metasm/gui.rb +0 -23
  228. data/lib/metasm/gui/dasm_graph.rb +0 -1354
  229. data/lib/metasm/ia32.rb +0 -14
  230. data/lib/metasm/ia32/opcodes.rb +0 -873
  231. data/lib/metasm/ppc/parse.rb +0 -52
  232. data/lib/metasm/x86_64.rb +0 -12
  233. data/lib/metasm/x86_64/opcodes.rb +0 -118
  234. data/samples/gdbclient.rb +0 -583
  235. data/samples/rubstop.rb +0 -399
@@ -0,0 +1,214 @@
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
+
7
+ # metasm debugger plugin
8
+ # adds a 'trace_func' method to the debugger
9
+ # the methods sets a breakpoint at the beginning of a function, and logs the execution of the instruction blocks
10
+ # does not descend in subfunctions
11
+
12
+ # setup the initial breakpoint at func start
13
+ def trace_func(addr, oneshot = false)
14
+ @trace_terminate = false
15
+ # distinguish different hits on the same function entry
16
+ counter = 0
17
+ bp = bpx(addr, oneshot) {
18
+ counter += 1
19
+ id = [disassembler.normalize(addr), counter, func_retaddr]
20
+ trace_func_newtrace(id)
21
+ trace_func_block(id)
22
+ continue
23
+ }
24
+ if addr == pc
25
+ del_bp bp if oneshot
26
+ bp.action.call
27
+ end
28
+ end
29
+
30
+ # start tracing now, and stop only when @trace_terminate is set
31
+ def trace
32
+ @trace_subfuncs = true
33
+ @trace_terminate = false
34
+ id = [pc, 0, 0]
35
+ trace_func_newtrace(id)
36
+ trace_func_block(id)
37
+ continue
38
+ end
39
+
40
+ # we hit the beginning of a block we want to trace
41
+ def trace_func_block(id)
42
+ blockaddr = pc
43
+ if b = trace_get_block(blockaddr)
44
+ trace_func_add_block(id, blockaddr)
45
+ if b.list.length == 1
46
+ trace_func_blockend(id, blockaddr)
47
+ else
48
+ bpx(b.list.last.address, true) {
49
+ finished = trace_func_blockend(id, blockaddr)
50
+ continue if not finished
51
+ }
52
+ end
53
+ else
54
+ # invalid opcode ?
55
+ trace_func_blockend(id, blockaddr)
56
+ end
57
+ end
58
+
59
+ # we are at the end of a traced block, find whats next
60
+ def trace_func_blockend(id, blockaddr)
61
+ if di = disassembler.di_at(pc)
62
+ if end_stepout(di) and trace_func_istraceend(id, di)
63
+ # trace ends there
64
+ trace_func_finish(id)
65
+ return true
66
+ elsif di.opcode.props[:saveip] and not trace_func_entersubfunc(id, di)
67
+ # call to a subfunction
68
+ bpx(di.next_addr, true) {
69
+ trace_func_block(id)
70
+ continue
71
+ }
72
+ continue
73
+ else
74
+ singlestep {
75
+ newaddr = pc
76
+ trace_func_block(id)
77
+
78
+ trace_func_linkdasm(di.address, newaddr)
79
+ continue
80
+ }
81
+ end
82
+ else
83
+ # XXX should link in the dasm somehow
84
+ singlestep {
85
+ trace_func_block(id)
86
+ continue
87
+ }
88
+ end
89
+ false
90
+ end
91
+
92
+ # retrieve an instructionblock, disassemble if needed
93
+ def trace_get_block(addr)
94
+ # TODO trace all blocks from addr for which we know the target, stop on call / jmp [foo]
95
+ disassembler.disassemble_fast_block(addr)
96
+ if di = disassembler.di_at(addr)
97
+ di.block
98
+ end
99
+ end
100
+
101
+ # update the blocks links in the disassembler
102
+ def trace_func_linkdasm(from_addr, new_addr)
103
+ di = disassembler.di_at(from_addr)
104
+ ndi = disassembler.di_at(new_addr)
105
+
106
+ return if not di
107
+
108
+ # is it a subfunction return ?
109
+ if end_stepout(di) and cdi = (1..8).map { |i|
110
+ disassembler.di_at(new_addr - i)
111
+ }.compact.find { |cdi_|
112
+ cdi_.opcode.props[:saveip] and cdi_.next_addr == new_addr
113
+ }
114
+ cdi.block.add_to_subfuncret new_addr
115
+ ndi.block.add_from_subfuncret cdi.address if ndi
116
+ cdi.block.each_to_normal { |f|
117
+ disassembler.function[f] ||= DecodedFunction.new if disassembler.di_at(f)
118
+ }
119
+ else
120
+ di.block.add_to_normal new_addr
121
+ ndi.block.add_from_normal from_addr if ndi
122
+ end
123
+ end
124
+
125
+ ################################################################################################
126
+ # you can redefine the following functions in another plugin to handle trace events differently
127
+
128
+ # a new trace is about to begin
129
+ def trace_func_newtrace(id)
130
+ @trace_func_counter ||= {}
131
+ @trace_func_counter[id] = 0
132
+
133
+ puts "start tracing #{Expression[id[0]]}"
134
+
135
+ # setup a bg_color_callback on the disassembler
136
+ if not defined? @trace_func_dasmcolor
137
+ @trace_func_dasmcolor = true
138
+ return if not disassembler.gui
139
+ oldcb = disassembler.gui.bg_color_callback
140
+ disassembler.gui.bg_color_callback = lambda { |addr|
141
+ if oldcb and c = oldcb[addr]
142
+ c
143
+ elsif di = disassembler.di_at(addr) and di.block.list.first.comment.to_s =~ /functrace/
144
+ 'ff0'
145
+ end
146
+ }
147
+ end
148
+ end
149
+
150
+ # a new block is added to a trace
151
+ def trace_func_add_block(id, blockaddr)
152
+ @trace_func_counter[id] += 1
153
+ if di = disassembler.di_at(blockaddr)
154
+ di.add_comment "functrace #{@trace_func_counter[id]}"
155
+ end
156
+ end
157
+
158
+ # the trace is finished
159
+ def trace_func_finish(id)
160
+ puts "finished tracing #{Expression[id[0]]}"
161
+ end
162
+
163
+ def trace_subfuncs=(v) @trace_subfuncs = v end
164
+ def trace_subfuncs; @trace_subfuncs ||= false end
165
+
166
+ # the tracer is on a end-of-func instruction, should the trace end ?
167
+ def trace_func_istraceend(id, di)
168
+ if trace_subfuncs
169
+ if @trace_terminate
170
+ true
171
+ elsif id[2] == 0 # trace VS func_trace
172
+ elsif target = disassembler.get_xrefs_x(di)[0]
173
+ # check the current return address against the one saved at trace start
174
+ resolve(disassembler.normalize(target)) == id[2]
175
+ end
176
+ else
177
+ true
178
+ end
179
+ end
180
+
181
+ # the tracer is on a subfunction call instruction, should it trace into or stepover ?
182
+ def trace_func_entersubfunc(id, di)
183
+ if trace_subfuncs
184
+ @trace_func_subfunccache ||= {}
185
+ if not target = @trace_func_subfunccache[di.address]
186
+ # even if the target is dynamic, its module should be static
187
+ if target = disassembler.get_xrefs_x(di)[0]
188
+ @trace_func_subfunccache[di.address] =
189
+ target = resolve(disassembler.normalize(target))
190
+ end
191
+ end
192
+ # check if the target subfunction is in the same module as the main
193
+ # XXX should check against the list of loaded modules etc
194
+ # XXX call thunk_foo -> jmp [other_module]
195
+ true if target.kind_of? Integer and target & 0xffc0_0000 == id[0] & 0xffc0_0000
196
+ else
197
+ false
198
+ end
199
+ end
200
+
201
+ if gui
202
+ gui.new_command('trace_func', 'trace execution inside a target function') { |arg| trace_func arg }
203
+ gui.new_command('trace_func_once', 'trace one execution inside the target function') { |arg| trace_func arg, true }
204
+ gui.new_command('trace_now', 'trace til the end of the current function') { trace_func pc, true ; gui.wrap_run { continue } }
205
+ gui.new_command('trace', 'start tracing from the current pc until trace_stop') { trace }
206
+ gui.new_command('trace_stop', 'stop tracing') { @trace_terminate = true }
207
+ gui.new_command('trace_subfunctions', 'define if the tracer should enter subfunctions') { |arg|
208
+ case arg.strip
209
+ when 'on', '1', 'yes', 'y'; @trace_subfuncs = true
210
+ else @trace_subfuncs = false
211
+ end
212
+ puts "#{'not ' if not @trace_subfuncs}tracing subfunctions"
213
+ }
214
+ end
@@ -40,7 +40,7 @@ OptionParser.new { |opt|
40
40
  opt.on('--map <mapfile>', 'load a map file (addr <-> name association)') { |f| opts[:map] = f }
41
41
  opt.on('--fast', 'dasm cli args with disassemble_fast_deep') { opts[:fast] = true }
42
42
  opt.on('--decompile') { opts[:decompile] = true }
43
- opt.on('--gui <gtk|win32|qt>') { |g| require 'metasm/gui/' + g }
43
+ opt.on('--gui <gtk|win32|qt>') { |g| ENV['METASM_GUI'] = g }
44
44
  opt.on('--cpu <cpu>', 'the CPU class to use for a shellcode (Ia32, X64, ...)') { |c| opts[:sc_cpu] = c }
45
45
  opt.on('--exe <exe_fmt>', 'the executable file format to use (PE, ELF, ...)') { |c| opts[:exe_fmt] = c }
46
46
  opt.on('--rebase <addr>', 'rebase the loaded file to <addr>') { |a| opts[:rebase] = Integer(a) }
@@ -49,6 +49,8 @@ OptionParser.new { |opt|
49
49
  opt.on('-v', '--verbose') { $VERBOSE = true } # default
50
50
  opt.on('-q', '--no-verbose') { $VERBOSE = false }
51
51
  opt.on('-d', '--debug') { $DEBUG = $VERBOSE = true }
52
+ opt.on('-S <file>', '--session <sessionfile>', 'save user actions in this session file') { |a| opts[:session] = a }
53
+ opt.on('-N', '--new-session', 'start new session, discard old one') { opts[:newsession] = true }
52
54
  }.parse!(ARGV)
53
55
 
54
56
  case exename = ARGV.shift
@@ -66,6 +68,8 @@ when /^(tcp:|udp:)?..+:/
66
68
  else
67
69
  w = Metasm::Gui::DasmWindow.new("#{exename + ' - ' if exename}metasm disassembler")
68
70
  if exename
71
+ opts[:sc_cpu] = eval(opts[:sc_cpu]) if opts[:sc_cpu] =~ /[.(\s:]/
72
+ opts[:exe_fmt] = eval(opts[:exe_fmt]) if opts[:exe_fmt] =~ /[.(\s:]/
69
73
  exe = w.loadfile(exename, opts[:sc_cpu], opts[:exe_fmt])
70
74
  exe.disassembler.rebase(opts[:rebase]) if opts[:rebase]
71
75
  if opts[:autoload]
@@ -73,6 +77,7 @@ else
73
77
  opts[:map] ||= basename + '.map' if File.exist?(basename + '.map')
74
78
  opts[:cheader] ||= basename + '.h' if File.exist?(basename + '.h')
75
79
  (opts[:plugin] ||= []) << (basename + '.rb') if File.exist?(basename + '.rb')
80
+ opts[:session] ||= basename + '.metasm-session'
76
81
  end
77
82
  end
78
83
  end
@@ -80,21 +85,46 @@ end
80
85
  ep = ARGV.map { |arg| (?0..?9).include?(arg[0]) ? Integer(arg) : arg }
81
86
 
82
87
  if exe
83
- dasm = exe.init_disassembler
88
+ dasm = exe.disassembler
84
89
 
85
90
  dasm.load_map opts[:map] if opts[:map]
86
91
  dasm.parse_c_file opts[:cheader] if opts[:cheader]
87
92
  dasm.backtrace_maxblocks_data = -1 if opts[:nodatatrace]
88
93
  dasm.debug_backtrace = true if opts[:debugbacktrace]
89
- dasm.disassemble_fast_deep(*ep) if opts[:fast]
90
94
  dasm.callback_finished = lambda { w.dasm_widget.focus_addr w.dasm_widget.curaddr, :decompile ; dasm.decompiler.finalize } if opts[:decompile]
95
+ dasm.disassemble_fast_deep(*ep) if opts[:fast]
91
96
  elsif dbg
92
97
  dbg.load_map opts[:map] if opts[:map]
93
- opts[:plugin].to_a.each { |p| dbg.load_plugin(p) }
98
+ dbg.disassembler.parse_c_file opts[:cheader] if opts[:cheader]
99
+ opts[:plugin].to_a.each { |p|
100
+ begin
101
+ dbg.load_plugin(p)
102
+ rescue ::Exception
103
+ puts "Error with plugin #{p}: #{$!.class} #{$!}"
104
+ end
105
+ }
94
106
  end
95
107
  if dasm
96
108
  w.display(dasm, ep)
97
- opts[:plugin].to_a.each { |p| dasm.load_plugin(p) }
109
+ opts[:plugin].to_a.each { |p|
110
+ begin
111
+ dasm.load_plugin(p)
112
+ rescue ::Exception
113
+ puts "Error with plugin #{p}: #{$!.class} #{$!}"
114
+ end
115
+ }
116
+
117
+ if opts[:session]
118
+ if File.exist?(opts[:session])
119
+ if opts[:newsession]
120
+ File.unlink(opts[:session])
121
+ else
122
+ puts "replaying session #{opts[:session]}"
123
+ w.widget.replay_session(opts[:session])
124
+ end
125
+ end
126
+ w.widget.save_session opts[:session]
127
+ end
98
128
  end
99
129
 
100
130
  opts[:hookstr].to_a.each { |f| eval f }
@@ -48,10 +48,23 @@ t0 = Time.now if opts[:benchmark]
48
48
  if exename =~ /^live:(.*)/
49
49
  raise 'no such live target' if not target = OS.current.find_process($1)
50
50
  p target if $VERBOSE
51
- exe = Shellcode.decode(target.memory, Metasm.const_get(opts[:sc_cpu]).new)
51
+ opts[:sc_cpu] = eval(opts[:sc_cpu]) if opts[:sc_cpu] =~ /[.(\s:]/
52
+ opts[:sc_cpu] = Metasm.const_get(opts[:sc_cpu]) if opts[:sc_cpu].kind_of(::String)
53
+ opts[:sc_cpu] = opts[:sc_cpu].new if opts[:sc_cpu].kind_of?(::Class)
54
+ exe = Shellcode.decode(target.memory, opts[:sc_cpu])
52
55
  else
53
- exefmt = opts[:exe_fmt] ? Metasm.const_get(opts[:exe_fmt]) : AutoExe.orshellcode { Metasm.const_get(opts[:sc_cpu]).new }
54
- exefmt = exefmt.withcpu(Metasm.const_get(opts[:sc_cpu]).new) if opts[:exe_fmt] == 'Shellcode' and opts[:sc_cpu]
56
+ opts[:sc_cpu] = eval(opts[:sc_cpu]) if opts[:sc_cpu] =~ /[.(\s:]/
57
+ opts[:exe_fmt] = eval(opts[:exe_fmt]) if opts[:exe_fmt] =~ /[.(\s:]/
58
+ if opts[:exe_fmt].kind_of?(::String)
59
+ exefmt = opts[:exe_fmt] = Metasm.const_get(opts[:exe_fmt])
60
+ else
61
+ exefmt = opts[:exe_fmt] || AutoExe.orshellcode {
62
+ opts[:sc_cpu] = Metasm.const_get(opts[:sc_cpu]) if opts[:sc_cpu].kind_of?(::String)
63
+ opts[:sc_cpu] = opts[:sc_cpu].new if opts[:sc_cpu].kind_of?(::Class)
64
+ opts[:sc_cpu]
65
+ }
66
+ end
67
+ exefmt = exefmt.withcpu(opts[:sc_cpu]) if exefmt.kind_of?(::Class) and exefmt.name.to_s.split('::').last == 'Shellcode'
55
68
  exe = exefmt.decode_file(exename)
56
69
  exe.disassembler.rebase(opts[:rebase]) if opts[:rebase]
57
70
  if opts[:autoload]
@@ -62,7 +75,7 @@ else
62
75
  end
63
76
  end
64
77
  # set options
65
- dasm = exe.init_disassembler
78
+ dasm = exe.disassembler
66
79
  makeint = lambda { |addr|
67
80
  case addr
68
81
  when /^[0-9].*h/; addr.to_i(16)
@@ -75,7 +88,13 @@ dasm.parse_c_file opts[:cheader] if opts[:cheader]
75
88
  dasm.backtrace_maxblocks_data = -1 if opts[:nodatatrace]
76
89
  dasm.debug_backtrace = true if opts[:debugbacktrace]
77
90
  opts[:stopaddr].to_a.each { |addr| dasm.decoded[makeint[addr]] = true }
78
- opts[:plugin].to_a.each { |p| dasm.load_plugin p }
91
+ opts[:plugin].to_a.each { |p|
92
+ begin
93
+ dasm.load_plugin p
94
+ rescue ::Exception
95
+ puts "Error with plugin #{p}: #{$!.class} #{$!}"
96
+ end
97
+ }
79
98
  opts[:hookstr].to_a.each { |f| eval f }
80
99
 
81
100
  t1 = Time.now if opts[:benchmark]
@@ -98,7 +117,13 @@ if opts[:decompile]
98
117
  tdc = Time.now if opts[:benchmark]
99
118
  end
100
119
 
101
- opts[:post_plugin].to_a.each { |p| dasm.load_plugin p }
120
+ opts[:post_plugin].to_a.each { |p|
121
+ begin
122
+ dasm.load_plugin p
123
+ rescue ::Exception
124
+ puts "Error with plugin #{p}: #{$!.class} #{$!}"
125
+ end
126
+ }
102
127
 
103
128
  dasm.save_file(opts[:savefile]) if opts[:savefile]
104
129
 
@@ -10,7 +10,7 @@
10
10
  # original entrypoint by disassembling the UPX stub, set breakpoint on it,
11
11
  # run the program, and dump the loaded image to an executable PE.
12
12
  #
13
- # usage: dump_upx.rb <packed.exe> [<dumped.exe>] [<rva iat>]
13
+ # usage: dump_upx.rb <packed.exe> [<dumped.exe>] [<iat (r)va>] [--oep <rva>]
14
14
  #
15
15
 
16
16
  require 'metasm'
@@ -21,17 +21,22 @@ class UPXUnpacker
21
21
  # find the oep by disassembling
22
22
  # run it until the oep
23
23
  # dump the memory image
24
- def initialize(file, dumpfile, iat_rva=nil)
25
- @dumpfile = dumpfile || 'upx-dumped.exe'
26
- @iat = iat_rva
24
+ def initialize(file, oep, iat, dumpfile)
25
+ @dumpfile = dumpfile
26
+ @dumpfile ||= file.chomp('.exe') + '.dump.exe'
27
27
 
28
28
  puts 'disassembling UPX loader...'
29
29
  pe = PE.decode_file(file)
30
- @oep = find_oep(pe)
31
- raise 'cant find oep...' if not @oep
32
- puts "oep found at #{Expression[@oep]}"
33
30
  @baseaddr = pe.optheader.image_base
34
- @iat -= @baseaddr if @iat > @baseaddr # va => rva
31
+
32
+ @oep = oep
33
+ @oep -= @baseaddr if @oep and @oep > @baseaddr # va => rva
34
+ @oep ||= find_oep(pe)
35
+ raise 'cant find oep...' if not @oep
36
+ puts "oep found at #{Expression[@oep]}" if not oep
37
+
38
+ @iat = iat
39
+ @iat -= @baseaddr if @iat and @iat > @baseaddr
35
40
 
36
41
  @dbg = OS.current.create_process(file).debugger
37
42
  puts 'running...'
@@ -40,7 +45,7 @@ class UPXUnpacker
40
45
 
41
46
  # disassemble the upx stub to find a cross-section jump (to the real entrypoint)
42
47
  def find_oep(pe)
43
- dasm = pe.disassemble_fast 'entrypoint'
48
+ dasm = pe.disassemble_fast_deep 'entrypoint'
44
49
 
45
50
  return if not jmp = dasm.decoded.find { |addr, di|
46
51
  # check only once per basic block
@@ -80,7 +85,8 @@ class UPXUnpacker
80
85
  dump.sections.each { |s| s.characteristics |= ['MEM_WRITE'] }
81
86
 
82
87
  # write the PE file to disk
83
- dump.encode_file @dumpfile
88
+ # as UPX strips the relocation information, mark the exe to opt-out from ASLR
89
+ dump.encode_file @dumpfile, 'exe', false
84
90
 
85
91
  puts 'dump complete'
86
92
  ensure
@@ -90,6 +96,12 @@ class UPXUnpacker
90
96
  end
91
97
 
92
98
  if __FILE__ == $0
93
- # args: packed [unpacked] [iat rva]
94
- UPXUnpacker.new(ARGV.shift, ARGV.shift, (Integer(ARGV.shift) rescue nil))
99
+ fn = ARGV.shift
100
+ oep = Integer(ARGV.shift) unless ARGV.empty?
101
+ oep = nil if oep == -1
102
+ iat = Integer(ARGV.shift) unless ARGV.empty?
103
+ iat = nil if iat == -1
104
+ out = ARGV.shift
105
+ abort 'usage: dump <exe> [<oep>] [<iat>] [<outfile>]' if not File.exist?(fn)
106
+ UPXUnpacker.new(fn, oep, iat, out)
95
107
  end
@@ -289,12 +289,15 @@ EOS
289
289
  m ? @cp.dump_definition(m) : @cp.to_s
290
290
  end
291
291
 
292
+ @@optim_hint = {}
293
+ def self.optim_hint; @@optim_hint; end
294
+
292
295
  attr_accessor :optim_hint
293
296
  def initialize(cp=nil)
294
297
  @cp = cp || DynLdr.host_cpu.new_cparser
295
298
  @cp.parse RUBY_H
296
299
  @iter_break = nil
297
- @optim_hint = {}
300
+ @optim_hint = @@optim_hint.dup
298
301
  end
299
302
 
300
303
  # convert a ruby AST to a new C function
@@ -1135,10 +1138,10 @@ EOS
1135
1138
  args = ast[3][1..-1] if ast[3] and ast[3][0] == :array
1136
1139
  arg0 = args[0] if args and args[0]
1137
1140
 
1138
- if arg0 and arg0[0] == :lit and arg0[1].kind_of? Fixnum
1141
+ if arg0 and arg0[0] == :lit and arg0[1].kind_of?(Fixnum) and %w[== > < >= <= + -].include?(op)
1142
+ # TODO or @optim_hint[ast[1]] == 'fixnum'
1139
1143
  # optimize 'x==42', 'x+42', 'x-42'
1140
1144
  o2 = arg0[1]
1141
- return if not %w[== > < >= <= + -].include? op
1142
1145
  if o2 < 0 and ['+', '-'].include? op
1143
1146
  # need o2 >= 0 for overflow detection
1144
1147
  op = {'+' => '-', '-' => '+'}[op]
@@ -1819,6 +1822,12 @@ end
1819
1822
 
1820
1823
  if __FILE__ == $0 or ARGV.delete('ignore_argv0')
1821
1824
 
1825
+ while i = ARGV.index('-e')
1826
+ # setup optim_hint etc
1827
+ ARGV.delete_at(i)
1828
+ eval ARGV.delete_at(i)
1829
+ end
1830
+
1822
1831
  demo = case ARGV.first
1823
1832
  when nil; :test_jit
1824
1833
  when 'asm'; :inlineasm