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,32 @@
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 dasm GUI plugin: hilight lines of code based on the opcode name
8
+ if gui
9
+ @gui_opcode_color = {
10
+ :call => :green_bg,
11
+ :jmp => :red_bg,
12
+ :jcc => :orange_bg,
13
+ }
14
+
15
+ obg = gui.bg_color_callback # chain old callback
16
+ gui.bg_color_callback = lambda { |a|
17
+ if di = di_at(a) and pr = di.opcode.props
18
+ if pr[:saveip] and (@function[di.block.to_normal.to_a.first] or di.block.to_subfuncret.to_a.first)
19
+ # don't color call+pop
20
+ @gui_opcode_color[:call]
21
+ elsif pr[:stopexec]
22
+ @gui_opcode_color[:jmp]
23
+ elsif pr[:setip]
24
+ @gui_opcode_color[:jcc]
25
+ else
26
+ obg[a] if obg
27
+ end
28
+ else
29
+ obg[a] if obg
30
+ end
31
+ }
32
+ end
@@ -0,0 +1,19 @@
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
+ #
8
+ # This plugin will create a monitoring process running samples/hotfix_gtk_dbg.rb on the current process (to fix a GTK crash when closing a window)
9
+ #
10
+
11
+ mypid = Process.pid
12
+
13
+ if (!Process.fork)
14
+ ARGV.clear
15
+ ARGV << mypid
16
+ $VERBOSE = false
17
+ Kernel.load File.join(Metasmdir, 'samples', 'hotfix_gtk_dbg.rb')
18
+ exit!
19
+ end
@@ -0,0 +1,34 @@
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 dasm plugin
8
+ # walks all disassembled instructions referencing an address
9
+ # if the address is a label, update the instruction to use the label
10
+ # esp. useful after a disassemble_fast, with a .map file
11
+
12
+ def addrtolabel
13
+ bp = prog_binding.invert
14
+ @decoded.each_value { |di|
15
+ next if not di.kind_of?(DecodedInstruction)
16
+ di.each_expr { |e|
17
+ next unless e.kind_of?(Expression)
18
+ if l = bp[e.lexpr]
19
+ add_xref(e.lexpr, Xref.new(:addr, di.address))
20
+ e.lexpr = Expression[l]
21
+ end
22
+ if l = bp[e.rexpr]
23
+ add_xref(e.rexpr, Xref.new(:addr, di.address))
24
+ e.rexpr = (e.lexpr ? Expression[l] : l)
25
+ end
26
+ }
27
+ }
28
+ nil
29
+ end
30
+
31
+ if gui
32
+ addrtolabel
33
+ gui.gui_update
34
+ end
@@ -0,0 +1,93 @@
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 dasm plugin: allow loading library signature files (see samples/generate_libsigs.rb)
8
+
9
+ class LibSignature
10
+ attr_accessor :sigs, :siglenmax, :giantregex
11
+ # load signatures from a signature file
12
+ def initialize(file)
13
+ # hash symbolname => signature
14
+ @sigs = {}
15
+
16
+ # populate sigs
17
+ symname = nil
18
+ sig = ''
19
+ File.read(file).each_line { |l|
20
+ case l
21
+ when /^ /
22
+ sig << l.strip
23
+ else
24
+ @sigs[symname] = sig
25
+ symname = l.strip
26
+ sig = ''
27
+ end
28
+ }
29
+ @sigs[symname] = sig
30
+ @sigs.delete nil
31
+ @siglenmax = @sigs.values.map { |v| v.length }.max
32
+
33
+ # compile a giant regex from the signatures
34
+ re = @sigs.values.uniq.map { |sigh|
35
+ sigh.gsub(/../) { |b| b == '..' ? '.' : ('\\x' + b) }
36
+ }.join('|')
37
+
38
+ # 'n' is a magic flag to allow high bytes in the regex (ruby1.9 + utfail)
39
+ @giantregex = Regexp.new re, Regexp::MULTILINE, 'n'
40
+ end
41
+
42
+ # we found a match on str at off, identify the specific symbol that matched
43
+ # on conflict, only return the first match
44
+ def matched_findsym(str, off)
45
+ str = str[off, @siglenmax].unpack('H*').first
46
+ @sigs.find { |sym, sig| str =~ /^#{sig}/i }[0]
47
+ end
48
+
49
+ # matches the signatures against a raw string
50
+ # yields offset, symname for each match
51
+ # returns nr of matches found
52
+ def match_chunk(str)
53
+ count = 0
54
+ off = 0
55
+ while o = (str[off..-1] =~ @giantregex)
56
+ count += 1
57
+ off += o
58
+ sym = matched_findsym(str, off)
59
+ yield off, sym
60
+ off += 1
61
+ end
62
+ count
63
+ end
64
+
65
+ # matches the signatures against a big raw string
66
+ # yields offset, symname for each match
67
+ # returns nr of matches found
68
+ def match(str)
69
+ chunksz = 1 << 20
70
+
71
+ chunkoff = 0
72
+ count = 0
73
+ while chunkoff < str.length
74
+ chunk = str[chunkoff, chunksz+@siglenmax]
75
+ count += match_chunk(chunk) { |o, sym| yield chunkoff+o, sym if o < chunksz }
76
+ chunkoff += chunksz
77
+ end
78
+ count
79
+ end
80
+ end
81
+
82
+ def match_libsigs(sigfile)
83
+ ls = LibSignature.new(sigfile)
84
+ count = 0
85
+ @sections.each { |b, s|
86
+ count += ls.match(s.data) { |off, sym| set_label_at(b+off, sym) }
87
+ }
88
+ count
89
+ end
90
+
91
+ if gui
92
+ gui.openfile('signature file to load') { |f| gui.messagebox "#{match_libsigs(f)} signatures found" }
93
+ end
@@ -0,0 +1,95 @@
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 dasm plugin: allow patching the file from the dasm interface
8
+ # use P to assemble a new instruction at the current address
9
+
10
+ # backup the executable file
11
+ def backup_program_file
12
+ f = @program.filename
13
+ if File.exist?(f) and not File.exist?(f + '.bak')
14
+ File.open(f + '.bak', 'wb') { |wfd|
15
+ File.open(f, 'rb') { |rfd|
16
+ while buf = rfd.read(1024*1024)
17
+ wfd.write buf
18
+ end
19
+ }
20
+ }
21
+ end
22
+ end
23
+
24
+ # create a backup and reopen the backend VirtualFile RW
25
+ def reopen_rw(addr=nil, edata=nil)
26
+ if not edata
27
+ sections.each { |k, v| reopen_rw(k, v) }
28
+ return true
29
+ end
30
+
31
+ return if not File.writable?(@program.filename)
32
+ backup_program_file
33
+ if not edata.data.kind_of? VirtualFile
34
+ # section too small, loaded as real String
35
+ # force reopen as VFile (allow hexediting in gui)
36
+ return if not off = addr_to_fileoff(addr)
37
+ len = edata.data.length
38
+ edata.data = VirtualFile.read(@program.filename, 'rb+').dup(off, len)
39
+ else
40
+ edata.data.fd.reopen @program.filename, 'rb+'
41
+ end
42
+ end
43
+
44
+ raise "cant find original file" if not @program.filename or not File.exist? @program.filename
45
+
46
+ reopen_rw
47
+
48
+ def patch_instrs(addr, asmsrc)
49
+ sc = Metasm::Shellcode.new(cpu, addr) # pfx needed for autorequire
50
+ sc.assemble(asmsrc, cpu)
51
+ sc.encoded.fixup! prog_binding # allow references to dasm labels in the shellcode
52
+ raw = sc.encode_string
53
+
54
+ if s = get_section_at(addr) and s[0].data.kind_of? VirtualFile
55
+ s[0][s[0].ptr, raw.length] = raw
56
+ elsif o = addr_to_fileoff(addr) # section too small, not loaded as a VirtFile
57
+ backup_program_file
58
+ File.open(@program.filename, 'rb+') { |fd|
59
+ fd.pos = o
60
+ fd.write raw
61
+ }
62
+ s[0][s[0].ptr, raw.length] = raw if s
63
+ else
64
+ return
65
+ end
66
+
67
+ b = split_block(addr)
68
+
69
+ # clear what we had in the rewritten space
70
+ raw.length.times { |rawoff|
71
+ next if not di = di_at(addr+rawoff)
72
+ di.block.list.each { |ldi| @decoded.delete ldi.address }
73
+ }
74
+
75
+ disassemble_fast(addr) if b
76
+ if b and @decoded[addr]
77
+ nb = @decoded[addr].block
78
+ nb.from_normal = b.from_normal
79
+ nb.from_subfuncret = b.from_subfuncret
80
+ nb.from_indirect = b.from_indirect
81
+ end
82
+ true
83
+ end
84
+
85
+ if gui
86
+ gui.keyboard_callback[?P] = lambda { |k|
87
+ addr = gui.curaddr
88
+ gui.inputbox('new instructions') { |src|
89
+ src = src.gsub(/;\s+/, "\n")
90
+ patch_instrs(addr, src)
91
+ gui.gui_update
92
+ }
93
+ true
94
+ }
95
+ end
@@ -0,0 +1,36 @@
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 dasm plugin: scan the memory for a 'ret' which could indicate the beginning of the current function
8
+ # (x86 only)
9
+ def scanfuncstart(addr)
10
+ if o = (1..16).find { |off| @decoded[addr-off].kind_of? DecodedInstruction } and @decoded[addr-o].bin_length == o
11
+ addr -= o
12
+ end
13
+ if @decoded[addr].kind_of? DecodedInstruction
14
+ fs = find_function_start(addr)
15
+ return fs if fs != addr
16
+ end
17
+ edata = get_edata_at(addr)
18
+ if o = (1..1000).find { |off|
19
+ @decoded[addr-off-1] or
20
+ edata.data[edata.ptr-off-1] == ?\xcc or
21
+ edata.data[edata.ptr-off-1] == ?\xc3 or
22
+ edata.data[edata.ptr-off-3] == ?\xc2
23
+ }
24
+ o -= @decoded[addr-o-1].bin_length-1 if @decoded[addr-o-1].kind_of? DecodedInstruction
25
+ addr-o
26
+ end
27
+ end
28
+
29
+ if gui
30
+ gui.keyboard_callback_ctrl[?P] = lambda { |*a|
31
+ if o = scanfuncstart(gui.curaddr)
32
+ gui.focus_addr(o)
33
+ end
34
+ true
35
+ }
36
+ end
@@ -0,0 +1,26 @@
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 dasm plugin: scan for xrefs to the target address, incl. relative offsets (eg near call/jmp)
8
+ def scanxrefs(target)
9
+ ans = []
10
+ sections.sort.each { |s_addr, edata|
11
+ raw = edata.data.to_str
12
+ (0..raw.length-4).each { |off|
13
+ r = raw[off, 4].unpack('V').first
14
+ ans << (s_addr + off) if (r + off+4 + s_addr)&0xffffffff == target or r == target
15
+ }
16
+ }
17
+ ans
18
+ end
19
+
20
+ gui.keyboard_callback[?X] = lambda { |*a|
21
+ target = gui.curaddr
22
+ ans = scanxrefs(target)
23
+ list = [['addr']] + ans.map { |off| [Expression[off].to_s] }
24
+ gui.listwindow("scanned xrefs to #{Expression[target]}", list) { |i| gui.focus_addr i[0] }
25
+ true
26
+ } if gui
@@ -0,0 +1,197 @@
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
+ #
8
+ # This file tries to handle simple self-modifying code patterns
9
+ # To be used as a --plugin for a Disassembler object
10
+ #
11
+
12
+ module SMC
13
+
14
+ # a copy-on-write copy of dasm address space (continuous segment only)
15
+ class CoWData
16
+ attr_accessor :startaddr, :data
17
+ def initialize(dasm)
18
+ @dasm = dasm
19
+ @startaddr = 0
20
+ @data = ''
21
+ end
22
+
23
+ # return a substring, either from the local cache or from dasm
24
+ # handles overlap
25
+ def [](addr, len)
26
+ if @data.empty?
27
+ s, e = @dasm.get_section_at(addr)
28
+ return if not s
29
+ return s.read(len)
30
+ end
31
+ raddr = addr - @base
32
+ rstart = @startaddr - @base
33
+ if raddr >= rstart and raddr+len <= rstart+@data.length
34
+ @data[0, raddr+len-rstart]
35
+ else
36
+ s, e = @dasm.get_section_at(addr)
37
+ return if not s
38
+ obuf = s.read(len)
39
+ len = obuf.length
40
+ if raddr < rstart and raddr+len > rstart
41
+ olen = [raddr+len-rstart, @data.length].min
42
+ obuf[rstart-raddr, olen] = @data[0, olen]
43
+ elsif raddr < rstart+@data.length and raddr+len > rstart+@data.length
44
+ obuf[0, rstart+@data.length-raddr] = @data[raddr-rstart, rstart+@data.length-raddr]
45
+ end
46
+ obuf
47
+ end
48
+ end
49
+
50
+ # set a substring value in the cache
51
+ def []=(addr, len, newdata)
52
+ raise 'len mismatch' if len != newdata.length
53
+ if @data.empty?
54
+ @base = @startaddr = addr
55
+ @data << newdata
56
+ return
57
+ end
58
+ raddr = addr - @base
59
+ rstart = @startaddr - @base
60
+ if raddr+newdata.length < rstart
61
+ s, e = @dasm.get_section_at(addr)
62
+ raise if not s
63
+ obuf = s.read(rstart-(raddr+newdata.length))
64
+ raise if obuf.length != rstart-(raddr+newdata.length)
65
+ newdata += obuf
66
+ elsif raddr > rstart+@data.length
67
+ s, e = @dasm.get_section_at(@startaddr+@data.length)
68
+ raise if not s
69
+ obuf = s.read(raddr-(rstart+@data.length))
70
+ raise if obuf.length != raddr-(rstart+@data.length)
71
+ @data += obuf
72
+ end
73
+ if raddr < rstart
74
+ @data = newdata + @data[raddr+newdata.length-rstart..-1].to_s
75
+ @startaddr = addr
76
+ else
77
+ @data[raddr-rstart, newdata.length] = newdata
78
+ end
79
+ end
80
+ end
81
+
82
+ VirtSections = {}
83
+
84
+ # try to emulate the byte modifications
85
+ # creates a new virtual section in dasm holding decoded data
86
+ # adds the virtual section to the dasm, stores the addresses in VirtSections[dasm]
87
+ # returns true if successful
88
+ def self.emu(dasm, addr)
89
+ puts "emulate SMC @#{Metasm::Expression[addr]}" if $VERBOSE
90
+
91
+ writer = nil
92
+ dasm.each_xref(addr, :w) { |xr| writer = xr.origin }
93
+ return if not dasm.di_at(writer)
94
+
95
+ a_pre, a_entry, a_cond, a_out, loop_bd = find_loop(dasm, writer)
96
+ return if not a_pre
97
+
98
+ # expression checking if we get out of the loop
99
+ loop_again_cond = dasm.cpu.get_jump_condition(dasm.decoded[a_cond])
100
+ loop_again_cond = Expression[:'!', loop_again_cond] if dasm.decoded[a_cond].next_addr != a_out
101
+
102
+ init_bd = {}
103
+ loop_bd.keys.grep(Symbol).each { |reg|
104
+ bt = dasm.backtrace(reg, a_pre, :include_start => true)
105
+ init_bd[reg] = bt.first if bt.length == 1 and bt.first != Metasm::Expression::Unknown and bt.first != Metasm::Expression[reg]
106
+ }
107
+
108
+ # reject non-determinist memory write
109
+ loop_bd.delete_if { |k, v| k.kind_of? Metasm::Indirection and not dasm.get_section_at(k.pointer.bind(init_bd).reduce) }
110
+
111
+ cow_data = CoWData.new(dasm)
112
+
113
+ puts "emulation running..." if $VERBOSE
114
+ pre_bd = init_bd
115
+ loop do
116
+ # the effects of the loop
117
+ post_bd = loop_bd.inject({}) { |bd, (k, v)|
118
+ if k.kind_of? Metasm::Indirection
119
+ k = k.bind(pre_bd).reduce_rec
120
+ raise "bad ptr #{k}" if not dasm.get_section_at(k.pointer.reduce)
121
+ end
122
+ bd.update k => Metasm::Expression[v.bind(pre_bd).reduce]
123
+ }
124
+
125
+ # the indirections used by the loop
126
+ # read mem from cow_data
127
+ # ignores stacked indirections & keys
128
+ ind_bd = {}
129
+ post_bd.values.map { |v| v.expr_indirections }.flatten.uniq.each { |ind|
130
+ p = ind.pointer.reduce
131
+ raise "bad loop read #{ind}" if not p.kind_of? Integer
132
+ ind_bd[ind] = Metasm::Expression.decode_imm(cow_data[p, ind.len], "u#{ind.len*8}".to_sym, dasm.cpu.endianness)
133
+ }
134
+
135
+ post_bd.each { |k, v|
136
+ next if not k.kind_of? Metasm::Indirection
137
+ cow_data[k.pointer.reduce, k.len] = Metasm::Expression.encode_imm(v.bind(ind_bd).reduce, "u#{k.len*8}".to_sym, dasm.cpu.endianness)
138
+ }
139
+
140
+ break if loop_again_cond.bind(post_bd).reduce == 0
141
+
142
+ pre_bd = post_bd
143
+ pre_bd.delete_if { |k, v| not k.kind_of? Symbol }
144
+ end
145
+
146
+ puts "emulation done (#{cow_data.data.length} bytes)" if $VERBOSE
147
+
148
+ VirtSections[dasm] ||= {}
149
+ newbase = "smc#{VirtSections[dasm].length}"
150
+ VirtSections[dasm][addr] = newbase
151
+ dasm.add_section(Metasm::EncodedData.new(cow_data.data), newbase)
152
+ dasm.comment[Metasm::Expression[newbase]] = "SelfModifyingCode from #{dasm.decoded[writer]}"
153
+
154
+ true
155
+ end
156
+
157
+ # find the loop containing addr
158
+ # only trivial loops handled
159
+ # returns [loop start, last instr before loop, loop conditionnal jump, 1st instr after loop, loop binding]
160
+ def self.find_loop(dasm, addr)
161
+ b = dasm.decoded[addr].block
162
+ return if not b.to_normal.to_a.include? b.address
163
+ b1 = b2 = b
164
+
165
+ pre = (b1.from_normal - [b2.list.last.address]).first
166
+ first = b1.address
167
+ last = b2.list.last.address
168
+ post = (b2.to_normal - [b1.address]).first
169
+ loop_bd = dasm.code_binding(first, post)
170
+
171
+ [pre, first, last, post, loop_bd]
172
+ end
173
+
174
+ # redirects the code flow from addr to the decoded section
175
+ def self.redirect(dasm, addr)
176
+ return if not VirtSections[dasm] or not newto = Metasm::Expression[VirtSections[dasm][addr]]
177
+ dasm.each_instructionblock { |b|
178
+ next if not b.to_normal.to_a.include? addr
179
+ b.to_normal.map! { |tn| dasm.normalize(tn) == addr ? newto : tn }
180
+ dasm.add_xref(newto, Metasm::Xref.new(:x, b.list.last.address))
181
+ b.list.last.add_comment "x:#{newto}"
182
+ dasm.addrs_todo << [newto, b.list.last.address]
183
+ }
184
+ end
185
+ end
186
+
187
+ if self.kind_of? Metasm::Disassembler
188
+ # setup the smc callbacks
189
+ dasm = self
190
+ list = []
191
+ dasm.callback_selfmodifying = lambda { |addr| list << addr }
192
+ dasm.callback_finished = lambda {
193
+ while addr = list.pop
194
+ SMC.emu(dasm, addr) and SMC.redirect(dasm, addr)
195
+ end
196
+ }
197
+ end