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,31 @@
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: try to demangle all labels as c++ names, add them as
8
+ # comment if successful
9
+
10
+ def demangle_all_cppnames
11
+ cnt = 0
12
+ prog_binding.each { |name, addr|
13
+ cname = name.sub(/^thunk_/, '')
14
+ if dname = demangle_cppname(cname)
15
+ cnt += 1
16
+ add_comment(addr, dname)
17
+ each_xref(addr, :x) { |xr|
18
+ if di = di_at(xr.origin)
19
+ di.add_comment dname
20
+ di.comment.delete "x:#{name}"
21
+ end
22
+ }
23
+ end
24
+ }
25
+ cnt
26
+ end
27
+
28
+ if gui
29
+ demangle_all_cppnames
30
+ gui.gui_update
31
+ end
@@ -0,0 +1,251 @@
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
+ # To use your own patterns, create a script that defines Deobfuscate::Patterns, then eval() this file.
9
+ # Use your script as argument to --plugin
10
+ #
11
+ # This script is to be used with the --plugin option of samples/disassemble(-gtk).rb
12
+ # It holds methods to ease the definition of instruction patterns that are to be replaced
13
+ # by another arbitrary instruction sequence, using mostly a regexp syntax
14
+ #
15
+ # The pattern search&replace is done every time the disassembler
16
+ # finds a new instruction, through the callback_newinstr callback.
17
+ #
18
+ # The patterns can use shortcuts for frequently-used regexps (like 'any machine registers'),
19
+ # defined in the PatternMacros hash.
20
+ #
21
+ # The patterns are matched first against the sequence of instruction opcode names, then
22
+ # each instruction is rendered as text (using Instruction#to_s), and the global regexp
23
+ # is checked.
24
+ # Backreferences can be used in the substitution instruction sequence, through the %1 ... %9
25
+ # special values.
26
+ #
27
+ # A pattern consists of a sequence of regexp for instructions, separated by ' ; '
28
+ # Each subregexps should not match multiple instructions (ie a patterns matches a fixed-length
29
+ # instruction sequence, whose length equals the number of ' ; '-separated regexps)
30
+ # The first word of each regexp should match only the instruction opcode name.
31
+ #
32
+ # The substitution may be a Proc, which will receive |dasm object, matched decodedinstr list| as
33
+ # arguments, and should return:
34
+ # a String, holding a sequence of instructions separated by ' ; ', which will be parsed by the CPU (no labels allowed)
35
+ # nil if the pattern did not match, continue searching
36
+ # an Array of Instruction/DecodedInstruction. If the array is the original di list, same as returning nil
37
+ #
38
+ # If the substitution array is different from the matched sequence, the new instructions are passed
39
+ # to dasm.replace_instrs, which will patch the disassembler decoded instruction graph ; and each
40
+ # new instruction is passed through the callback once more, allowing for recursive patterns.
41
+ #
42
+
43
+ module Deobfuscate
44
+
45
+
46
+ # special constructs : %i => an integer (immediate/standard label)
47
+ # %r => standard x86 register (except esp), all sizes
48
+ # %m => modr/m 32 (memory indirection or reg)
49
+ PatternMacros = {
50
+ '%i' => '(?:-|loc_|sub_|xref_)?[0-9][0-9a-fA-F]*h?',
51
+ '%r' => '(?:[re]?[abcd]x|[re]?[sd]i|[re]?bp|[abcd][lh])',
52
+ '%m' => '(?:(?:dword ptr )?\[.*?\]|eax|ebx|ecx|edx|edi|esi|ebp)',
53
+ } if not defined? PatternMacros
54
+
55
+
56
+
57
+
58
+ # instructions are separated by ' ; '
59
+ # instruction must be '<simple regexp matching opcode> <arbitrary regexp>'
60
+ # in the pattern target, %1-%9 are used for backreferences from the regexp match
61
+ Patterns = {
62
+ 'nop ; (.*)' => '%1', # concat 'nop' into following instruction
63
+ 'mov (%r|esp), \1' => 'nop',
64
+ 'lea (%r|esp), (?:dword ptr )?\[\1(?:\+0)?\]' => 'nop',
65
+ '(.*)' => lambda { |dasm, list| # remove 'jmp imm' preceding us without interfering with running dasm
66
+ if pdi = prev_di(dasm, list.last) and pdi.opcode.name == 'jmp' and
67
+ pdi.instruction.args[0].kind_of? Metasm::Expression
68
+ dasm.replace_instrs(pdi.address, pdi.address, [])
69
+ end
70
+ nil
71
+ },
72
+ #'call %i ; pop (%r)' => lambda { |dasm, list| "mov %1, #{list.first.next_addr}" },
73
+ } if not defined? Patterns
74
+
75
+
76
+
77
+
78
+ # returns an array of strings matching the regexp (only |,?,[], non-nested allowed, no special chars)
79
+ # expand_regexp['a[bcd]?(ef|gh)'] => [abef acef adef aef abgh acgh adgh agh]
80
+ def self.expand_regexp(str)
81
+ case str
82
+ when nil, '', '.*'; return [str.to_s]
83
+ when /^\\\./
84
+ l1, p2 = ['.'], $'
85
+ when /^(\w+)(\?)?/
86
+ s1, q, p2 = $1, $2, $'
87
+ l1 = (q ? [s1, s1.chop] : [s1])
88
+ when /^\[(.*?)\](\?)?/
89
+ p1, q, p2 = $1, $2, $'
90
+ l1 = p1.split(//)
91
+ l1 << '' if q
92
+ when /^\((?:\?:)?(.*?)\)(\?)?/
93
+ p1, q, p2 = $1, $2, $'
94
+ l1 = p1.split('|').map { |p| expand_regexp(p) }.flatten
95
+ l1 << '' if q
96
+ else raise "bad pattern #{str.inspect}"
97
+ end
98
+ expand_regexp(p2).map { |s2| l1.map { |s1_| s1_ + s2 } }.flatten.uniq
99
+ end
100
+
101
+ # find the instr preceding adi ; follows from_normal if it is a single element array
102
+ def self.prev_di(dasm, di)
103
+ if di.block.list.first != di
104
+ di.block.list[di.block.list.index(di)-1]
105
+ elsif di.block.from_normal.to_a.length == 1
106
+ dasm.decoded[di.block.from_normal.first]
107
+ end
108
+ end
109
+
110
+ # preprocess the pattern list to optimize matching on each new instruction
111
+ # last pattern instr opname => prev instr opname => prev instr opname => :pattern => [patterns]
112
+ def self.generate_precalc(next_hash, next_ops, pattern)
113
+ if next_ops.empty?
114
+ next_hash[:pattern] ||= []
115
+ next_hash[:pattern] << pattern
116
+ else
117
+ (expand_regexp(next_ops[-1]) rescue ['.*']).each { |op|
118
+ nh = next_hash[op] ||= {}
119
+ generate_precalc(nh, next_ops[0...-1], pattern)
120
+ }
121
+ end
122
+ end
123
+
124
+ PrecalcPatterns = {} if not defined? PrecalcPatterns
125
+
126
+ # replace Macros in patterns, do some precalc to speedup pattern matching
127
+ def self.init
128
+ PrecalcPatterns.clear
129
+ Patterns.keys.each { |pat|
130
+ # replace PatternMacros in patterns
131
+ newp = pat.dup
132
+ PatternMacros.each { |mk, mv| newp.gsub!(mk, mv) }
133
+ Patterns[newp] = Patterns.delete(pat) if pat != newp
134
+ pat = newp
135
+
136
+ # TODO handle instructions with prefix (lock/rep), conditional regexp over multiple instructions..
137
+ ops = pat.split(' ; ').map { |instr| instr[/^\S+/] }
138
+
139
+ generate_precalc(PrecalcPatterns, ops, pat)
140
+ }
141
+ end
142
+
143
+ # the actual disassembler callback
144
+ # checks the current instruction opname against the end of patterns using precomputed tree, then check previous instr etc
145
+ # once full pattern may match, convert each instr to string, and run the regexp match
146
+ # on match, reuse the captures in the pattern target, parse the target, generate decoded instrs, and replace in the dasm graph.
147
+ # on match, rerun the callback on each replaced instruction (for recursive patterns)
148
+ def self.newinstr_callback(dasm, di)
149
+ # compute the merged subtree of t1 and t2
150
+ # merges patterns if found
151
+ mergetree = lambda { |t1, t2|
152
+ if t1 and t2
153
+ case t1
154
+ when Array; t1 + t2
155
+ when Hash; (t1.keys | t2.keys).inject({}) { |t, k| t.update k => mergetree[t1[k], t2[k]] }
156
+ end
157
+ else t1 || t2
158
+ end
159
+ }
160
+
161
+ di_seq = [di]
162
+ lastdi = di
163
+ tree = PrecalcPatterns
164
+ tree = mergetree[tree['.*'], tree[lastdi.instruction.opname]]
165
+ newinstrs = match = nil
166
+ # walk the Precalc tree
167
+ while tree
168
+ if tree[:pattern]
169
+ strs = di_seq.map { |pdi| pdi.instruction.to_s }
170
+ break if tree[:pattern].find { |pat|
171
+ if match = /^#{pat}$/.match(strs.join(' ; '))
172
+ newinstrs = Patterns[pat]
173
+ newinstrs = newinstrs[dasm, di_seq] if newinstrs.kind_of? Proc
174
+ newinstrs = nil if newinstrs == di_seq
175
+ else newinstrs = nil
176
+ end
177
+ newinstrs
178
+ } or tree.length == 1
179
+ end
180
+
181
+ if lastdi = prev_di(dasm, lastdi)
182
+ di_seq.unshift lastdi
183
+ tree = mergetree[tree['.*'], tree[lastdi.instruction.opname]]
184
+ else break
185
+ end
186
+ end
187
+
188
+ # match found : create instruction stream, replace in dasm, recurse
189
+ if newinstrs
190
+ # replace %1-%9 by the matched substrings
191
+ newinstrs = newinstrs.gsub(/%(\d)/) { match.captures[$1.to_i-1] }.split(' ; ').map { |str| dasm.cpu.parse_instruction(str) } if newinstrs.kind_of? String
192
+ if newinstrs.last.kind_of? Metasm::Instruction and newinstrs.last.opname != 'jmp' and
193
+ lastdi.address + di_seq.inject(-di.bin_length) { |len, i| len + i.bin_length } != di.address
194
+ # ensure that the last instr ends the same place as the original last instr (to allow disassemble_block to continue)
195
+ newinstrs << dasm.cpu.parse_instruction("jmp #{Metasm::Expression[di.next_addr]}")
196
+ # nop ; jmp => jmp
197
+ newinstrs.shift if newinstrs.length >= 2 and newinstrs.first.kind_of? Metasm::Instruction and newinstrs.first.opname == 'nop'
198
+ end
199
+
200
+ # remove instructions from the match to have only 2 linked blocks passed to replace_instrs
201
+ unused = di_seq[1..-2] || []
202
+ unused.delete_if { |udi| udi.block.address == di_seq[0].block.address or udi.block.address == di_seq[-1].block.address }
203
+ dasm.replace_instrs(unused.shift.address, unused.shift.address, []) while unused.length > 1
204
+ dasm.replace_instrs(unused.first.address, unused.first.address, []) if not unused.empty?
205
+
206
+ # patch the dasm graph
207
+ if dasm.replace_instrs(lastdi.address, di.address, newinstrs, true)
208
+ puts ' deobfuscate', di_seq, ' into', newinstrs, ' ---' if $DEBUG
209
+ # recurse, keep the last generated di to return to caller as replacement
210
+ newinstrs.each { |bdi| di = newinstr_callback(dasm, bdi) || di }
211
+ else
212
+ di = nil
213
+ end
214
+ end
215
+
216
+ di
217
+ end
218
+
219
+ # call newinstr_callback on all existing instructions of dasm
220
+ def self.deobfuscate_existing(dasm)
221
+ dasm.each_instructionblock { |b|
222
+ b.list.dup.each { |di| newinstr_callback(dasm, di) }
223
+ }
224
+ end
225
+
226
+ # calls dasm.merge_blocks(true) on all instruction blocks to merge sequences of blocks
227
+ def self.merge_blocks(dasm)
228
+ dasm.each_instructionblock { |b|
229
+ if pv = dasm.di_at(b.from_normal.to_a.first) and not pv.block.list.last.opcode.props[:setip] and
230
+ b.from_normal.length == 1 and pv.block.to_normal.to_a.length == 1
231
+ dasm.merge_blocks(pv.block, b, true)
232
+ end
233
+ }
234
+ end
235
+ end
236
+
237
+ if $DEBUG
238
+ # update DecodedInstr.to_s to include instr length
239
+ class Metasm::DecodedInstruction
240
+ def to_s ; "#{Metasm::Expression[address] if address} +#{bin_length} #{instruction}" end
241
+ end
242
+ end
243
+
244
+ # do the pattern precalc
245
+ Deobfuscate.init
246
+
247
+ if self.kind_of? Metasm::Disassembler
248
+ dasm = self
249
+ # setup the newinstr callback
250
+ dasm.callback_newinstr = lambda { |di| Deobfuscate.newinstr_callback(dasm, di) }
251
+ end
@@ -0,0 +1,35 @@
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: dumps the text of the current widget to a text file on 'D' keypress
8
+ # the dump is appended to the file if it exists
9
+ # works on the listing view (current screen),
10
+ # on the decompiled view (current function),
11
+ # on the graph view (selected blocks, in selection order)
12
+
13
+ if gui
14
+ gui.keyboard_callback[?D] = lambda { |a|
15
+ cv = gui.curview
16
+ if t = cv.instance_variable_get('@line_text')
17
+ gui.savefile('dump file') { |f|
18
+ File.open(f, 'a') { |fd| fd.puts t }
19
+ }
20
+ elsif s = cv.instance_variable_get('@selected_boxes')
21
+ if s.empty?
22
+ gui.messagebox('select boxes (ctrl+click)')
23
+ next
24
+ end
25
+ gui.savefile('dump file') { |f|
26
+ File.open(f, 'a') { |fd|
27
+ s.each { |box|
28
+ fd.puts box[:line_text_col].map { |strc| strc.transpose[0].join }
29
+ }
30
+ fd.puts
31
+ }
32
+ }
33
+ end
34
+ }
35
+ end
@@ -0,0 +1,86 @@
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: create a function to export the currently displayed
8
+ # dasm graph to a .svg file
9
+ # in the gui, type E to export. Tested only for graph-style view, *may* work in other cases
10
+
11
+ raise 'gui only' if not gui
12
+
13
+ def graph_to_svg
14
+ gw = gui.curview.dup
15
+ class << gw
16
+ attr_accessor :svgbuf, :svgcol
17
+ def draw_color(col)
18
+ col = @default_color_association.fetch(col, col)
19
+ col = BasicColor.fetch(col, col)
20
+ @svgcol = "##{col}"
21
+ end
22
+
23
+ def draw_line(x1, y1, x2, y2)
24
+ bb(x1, y1, x2, y2)
25
+ svgbuf << %Q{<line x1="#{x1}" y1="#{y1}" x2="#{x2}" y2="#{y2}" stroke="#{@svgcol}" />\n}
26
+ end
27
+ def draw_rectangle(x, y, w, h)
28
+ bb(x, y, x+w, y+h)
29
+ svgbuf << %Q{<rect x="#{x}" y="#{y}" width="#{w}" height="#{h}" fill="#{@svgcol}" />\n}
30
+ end
31
+ def draw_string(x, y, str)
32
+ bb(x, y, x+str.length*@font_width, y+@font_height)
33
+ stre = str.gsub('<', '&lt;').gsub('>', '&gt;')
34
+ svgbuf << %Q{<text x="#{(0...str.length).map { |i| x+i*@font_width }.join(',')}" y="#{y+@font_height*0.7}" stroke="#{@svgcol}">#{stre}</text>\n}
35
+ end
36
+
37
+ def draw_rectangle_color(c, *a)
38
+ draw_color(c)
39
+ draw_rectangle(*a)
40
+ end
41
+ def draw_line_color(c, *a)
42
+ draw_color(c)
43
+ draw_line(*a)
44
+ end
45
+ def draw_string_color(c, *a)
46
+ draw_color(c)
47
+ draw_string(*a)
48
+ end
49
+
50
+ def focus?; false; end
51
+ def view_x; @svgvx ||= @curcontext.boundingbox[0]-20; end
52
+ def view_y; @svgvy ||= @curcontext.boundingbox[1]-20; end
53
+ def width; @svgvw ||= (@curcontext ? (@curcontext.boundingbox[2]-@curcontext.boundingbox[0])*@zoom+20 : 800); end
54
+ def height; @svgvh ||= (@curcontext ? (@curcontext.boundingbox[3]-@curcontext.boundingbox[1])*@zoom+20 : 600); end
55
+ def svgcuraddr; @curcontext ? @curcontext.root_addrs.first : current_address; end
56
+
57
+ # drawing bounding box (for the background rectangle)
58
+ attr_accessor :bbx, :bby, :bbxm, :bbym
59
+ def bb(x1, y1, x2, y2)
60
+ @bbx = [x1, x2, @bbx].compact.min
61
+ @bbxm = [x1, x2, @bbxm].compact.max
62
+ @bby = [y1, y2, @bby].compact.min
63
+ @bbym = [y1, y2, @bbym].compact.max
64
+ end
65
+ end
66
+ ret = gw.svgbuf = ''
67
+ gw.paint
68
+
69
+ ret[0, 0] = <<EOS
70
+ <?xml version="1.0" standalone="no"?>
71
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
72
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
73
+ <svg xmlns="http://www.w3.org/2000/svg" font-family="courier, monospace">
74
+ <desc>Graph of #{get_label_at(gw.svgcuraddr) || Expression[gw.svgcuraddr]}</desc>
75
+ <rect x="#{gw.bbx-10}" y="#{gw.bby-10}" width="#{gw.bbxm-gw.bbx+20}" height="#{gw.bbym-gw.bby+20}" fill="#{gw.draw_color(:background)}" />"
76
+ EOS
77
+ ret << %Q{</svg>}
78
+ end
79
+
80
+ gui.keyboard_callback[?E] = lambda { |*a|
81
+ gui.savefile('svg target') { |f|
82
+ svg = graph_to_svg
83
+ File.open(f, 'w') { |fd| fd.write svg }
84
+ }
85
+ true
86
+ }
@@ -0,0 +1,75 @@
1
+ # This file is part of Metasm, the Ruby assembly manipulation suite
2
+ # Copyright (C) 2006-2011 Yoann GUILLOT
3
+ #
4
+ # Licence is LGPL, see LICENCE in the top-level directory
5
+
6
+
7
+ # metasm dasm plugin
8
+ # scan for a given asm instruction sequence (all encodings)
9
+ # add the G dasm-gui shortcut, the input change ';' for line splits
10
+
11
+ def findgadget_asm_to_regex(asm)
12
+ fullre = ''
13
+ asm = asm.gsub(';', "\n")
14
+
15
+ sc = Shellcode.new(@cpu)
16
+ sc.parse asm
17
+ sc.source.each { |i|
18
+ case i
19
+ when Data
20
+ opts_edata = i.encode(@cpu.endianness)
21
+ when Instruction
22
+ opts_edata = @cpu.encode_instruction(sc, i)
23
+ else
24
+ raise "cant scan for #{i}"
25
+ end
26
+
27
+ opts_edata = [opts_edata] if opts_edata.kind_of?(EncodedData)
28
+
29
+ opts_re = opts_edata.map { |ed|
30
+ # Regexp.escape ed.data, with relocs replaced with '.'
31
+ re = ''
32
+ off = 0
33
+ ed.reloc.sort.each { |o, rel|
34
+ re << Regexp.escape(ed.data[off...o])
35
+ re << ('.' * rel.length)
36
+ off = o + rel.length
37
+ }
38
+ re << Regexp.escape(ed.data[off..-1])
39
+ }
40
+ fullre << '(' << opts_re.join('|') << ')'
41
+ }
42
+
43
+ Regexp.new(fullre, Regexp::MULTILINE, 'n')
44
+ end
45
+
46
+ # parse asm to a regexp, return the list of addresses matching
47
+ def findgadget_asm(asm)
48
+ pattern_scan(findgadget_asm_to_regex(asm))
49
+ end
50
+
51
+ def findgadget_prompt
52
+ gui.inputbox("source for the gadget - separate with ;") { |asm|
53
+ lst = findgadget_asm(asm)
54
+ list = [['address', 'section']]
55
+ sections = section_info
56
+ list += lst.map { |addr|
57
+ # [name, addr, len, misc]
58
+ if s = sections.find { |s_| s_[1] <= addr and s_[1] + s_[2] > addr }
59
+ s = s[0]
60
+ else
61
+ s = '?'
62
+ end
63
+ [Expression[addr], s]
64
+ }
65
+ gui.listwindow("gadgetscan for #{asm}", list) { |args| gui.focus_addr(args[0]) }
66
+ }
67
+ end
68
+
69
+ if gui
70
+ gui.keyboard_callback[?G] = lambda { |*a| findgadget_prompt }
71
+ w = gui.toplevel
72
+ w.addsubmenu(w.find_menu('Actions'), 'Scan for _Gadget', 'G') { findgadget_prompt }
73
+ w.update_menu
74
+ :success
75
+ end