metasm 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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