metasm 1.0.0 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (276) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +3 -0
  4. data/.gitignore +3 -0
  5. data/.hgtags +3 -0
  6. data/Gemfile +3 -0
  7. data/INSTALL +61 -0
  8. data/LICENCE +458 -0
  9. data/README +29 -21
  10. data/Rakefile +10 -0
  11. data/TODO +10 -12
  12. data/doc/code_organisation.txt +3 -1
  13. data/doc/core/DynLdr.txt +247 -0
  14. data/doc/core/ExeFormat.txt +43 -0
  15. data/doc/core/Expression.txt +220 -0
  16. data/doc/core/GNUExports.txt +27 -0
  17. data/doc/core/Ia32.txt +236 -0
  18. data/doc/core/SerialStruct.txt +108 -0
  19. data/doc/core/VirtualString.txt +145 -0
  20. data/doc/core/WindowsExports.txt +61 -0
  21. data/doc/core/index.txt +1 -0
  22. data/doc/style.css +6 -3
  23. data/doc/usage/debugger.txt +327 -0
  24. data/doc/usage/index.txt +1 -0
  25. data/doc/use_cases.txt +2 -2
  26. data/metasm.gemspec +23 -0
  27. data/{lib/metasm.rb → metasm.rb} +15 -3
  28. data/{lib/metasm → metasm}/compile_c.rb +15 -9
  29. data/metasm/cpu/arc.rb +8 -0
  30. data/metasm/cpu/arc/decode.rb +404 -0
  31. data/metasm/cpu/arc/main.rb +191 -0
  32. data/metasm/cpu/arc/opcodes.rb +588 -0
  33. data/metasm/cpu/arm.rb +14 -0
  34. data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
  35. data/{lib/metasm → metasm/cpu}/arm/decode.rb +15 -18
  36. data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
  37. data/{lib/metasm → metasm/cpu}/arm/main.rb +3 -6
  38. data/metasm/cpu/arm/opcodes.rb +324 -0
  39. data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
  40. data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
  41. data/metasm/cpu/arm64.rb +15 -0
  42. data/metasm/cpu/arm64/debug.rb +38 -0
  43. data/metasm/cpu/arm64/decode.rb +285 -0
  44. data/metasm/cpu/arm64/encode.rb +41 -0
  45. data/metasm/cpu/arm64/main.rb +105 -0
  46. data/metasm/cpu/arm64/opcodes.rb +232 -0
  47. data/metasm/cpu/arm64/parse.rb +20 -0
  48. data/metasm/cpu/arm64/render.rb +95 -0
  49. data/{lib/metasm/mips/compile_c.rb → metasm/cpu/bpf.rb} +4 -2
  50. data/metasm/cpu/bpf/decode.rb +110 -0
  51. data/metasm/cpu/bpf/main.rb +60 -0
  52. data/metasm/cpu/bpf/opcodes.rb +81 -0
  53. data/metasm/cpu/bpf/render.rb +30 -0
  54. data/{lib/metasm/ppc.rb → metasm/cpu/cy16.rb} +2 -4
  55. data/metasm/cpu/cy16/decode.rb +247 -0
  56. data/metasm/cpu/cy16/main.rb +63 -0
  57. data/metasm/cpu/cy16/opcodes.rb +78 -0
  58. data/metasm/cpu/cy16/render.rb +30 -0
  59. data/metasm/cpu/dalvik.rb +11 -0
  60. data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +34 -34
  61. data/{lib/metasm → metasm/cpu}/dalvik/main.rb +71 -4
  62. data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +21 -12
  63. data/{lib/metasm/mips.rb → metasm/cpu/ebpf.rb} +3 -4
  64. data/metasm/cpu/ebpf/debug.rb +61 -0
  65. data/metasm/cpu/ebpf/decode.rb +142 -0
  66. data/metasm/cpu/ebpf/main.rb +58 -0
  67. data/metasm/cpu/ebpf/opcodes.rb +97 -0
  68. data/metasm/cpu/ebpf/render.rb +36 -0
  69. data/metasm/cpu/ia32.rb +17 -0
  70. data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +23 -9
  71. data/{lib/metasm → metasm/cpu}/ia32/debug.rb +44 -6
  72. data/{lib/metasm → metasm/cpu}/ia32/decode.rb +342 -128
  73. data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +75 -53
  74. data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
  75. data/{lib/metasm → metasm/cpu}/ia32/main.rb +66 -8
  76. data/metasm/cpu/ia32/opcodes.rb +1424 -0
  77. data/{lib/metasm → metasm/cpu}/ia32/parse.rb +55 -17
  78. data/{lib/metasm → metasm/cpu}/ia32/render.rb +32 -5
  79. data/metasm/cpu/mcs51.rb +8 -0
  80. data/metasm/cpu/mcs51/decode.rb +99 -0
  81. data/metasm/cpu/mcs51/main.rb +87 -0
  82. data/metasm/cpu/mcs51/opcodes.rb +120 -0
  83. data/metasm/cpu/mips.rb +14 -0
  84. data/metasm/cpu/mips/debug.rb +42 -0
  85. data/{lib/metasm → metasm/cpu}/mips/decode.rb +59 -38
  86. data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
  87. data/{lib/metasm → metasm/cpu}/mips/main.rb +13 -6
  88. data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +87 -18
  89. data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
  90. data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
  91. data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
  92. data/metasm/cpu/msp430/decode.rb +243 -0
  93. data/metasm/cpu/msp430/main.rb +62 -0
  94. data/metasm/cpu/msp430/opcodes.rb +101 -0
  95. data/metasm/cpu/openrisc.rb +11 -0
  96. data/metasm/cpu/openrisc/debug.rb +106 -0
  97. data/metasm/cpu/openrisc/decode.rb +182 -0
  98. data/metasm/cpu/openrisc/decompile.rb +350 -0
  99. data/metasm/cpu/openrisc/main.rb +70 -0
  100. data/metasm/cpu/openrisc/opcodes.rb +109 -0
  101. data/metasm/cpu/openrisc/render.rb +37 -0
  102. data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
  103. data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
  104. data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
  105. data/metasm/cpu/ppc.rb +11 -0
  106. data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -37
  107. data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
  108. data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
  109. data/{lib/metasm → metasm/cpu}/ppc/main.rb +23 -18
  110. data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -6
  111. data/metasm/cpu/ppc/parse.rb +55 -0
  112. data/metasm/cpu/python.rb +8 -0
  113. data/metasm/cpu/python/decode.rb +116 -0
  114. data/metasm/cpu/python/main.rb +36 -0
  115. data/metasm/cpu/python/opcodes.rb +180 -0
  116. data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
  117. data/{lib/metasm → metasm/cpu}/sh4/decode.rb +50 -23
  118. data/{lib/metasm → metasm/cpu}/sh4/main.rb +38 -27
  119. data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
  120. data/metasm/cpu/st20.rb +9 -0
  121. data/metasm/cpu/st20/decode.rb +173 -0
  122. data/metasm/cpu/st20/decompile.rb +283 -0
  123. data/metasm/cpu/st20/main.rb +37 -0
  124. data/metasm/cpu/st20/opcodes.rb +140 -0
  125. data/{lib/metasm/arm.rb → metasm/cpu/webasm.rb} +4 -5
  126. data/metasm/cpu/webasm/debug.rb +31 -0
  127. data/metasm/cpu/webasm/decode.rb +321 -0
  128. data/metasm/cpu/webasm/decompile.rb +386 -0
  129. data/metasm/cpu/webasm/encode.rb +104 -0
  130. data/metasm/cpu/webasm/main.rb +81 -0
  131. data/metasm/cpu/webasm/opcodes.rb +214 -0
  132. data/metasm/cpu/x86_64.rb +15 -0
  133. data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +40 -25
  134. data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
  135. data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +58 -15
  136. data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +59 -28
  137. data/{lib/metasm → metasm/cpu}/x86_64/main.rb +18 -6
  138. data/metasm/cpu/x86_64/opcodes.rb +138 -0
  139. data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +12 -4
  140. data/metasm/cpu/x86_64/render.rb +35 -0
  141. data/metasm/cpu/z80.rb +9 -0
  142. data/metasm/cpu/z80/decode.rb +286 -0
  143. data/metasm/cpu/z80/main.rb +67 -0
  144. data/metasm/cpu/z80/opcodes.rb +224 -0
  145. data/metasm/cpu/z80/render.rb +48 -0
  146. data/{lib/metasm/os/main.rb → metasm/debug.rb} +201 -407
  147. data/{lib/metasm → metasm}/decode.rb +104 -24
  148. data/{lib/metasm → metasm}/decompile.rb +804 -478
  149. data/{lib/metasm → metasm}/disassemble.rb +385 -170
  150. data/{lib/metasm → metasm}/disassemble_api.rb +684 -105
  151. data/{lib/metasm → metasm}/dynldr.rb +231 -138
  152. data/{lib/metasm → metasm}/encode.rb +20 -5
  153. data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
  154. data/{lib/metasm → metasm}/exe_format/autoexe.rb +3 -0
  155. data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
  156. data/{lib/metasm → metasm}/exe_format/coff.rb +35 -7
  157. data/{lib/metasm → metasm}/exe_format/coff_decode.rb +70 -23
  158. data/{lib/metasm → metasm}/exe_format/coff_encode.rb +24 -22
  159. data/{lib/metasm → metasm}/exe_format/dex.rb +26 -8
  160. data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
  161. data/{lib/metasm → metasm}/exe_format/elf.rb +108 -58
  162. data/{lib/metasm → metasm}/exe_format/elf_decode.rb +202 -36
  163. data/{lib/metasm → metasm}/exe_format/elf_encode.rb +126 -32
  164. data/metasm/exe_format/gb.rb +65 -0
  165. data/metasm/exe_format/javaclass.rb +424 -0
  166. data/{lib/metasm → metasm}/exe_format/macho.rb +218 -16
  167. data/{lib/metasm → metasm}/exe_format/main.rb +28 -3
  168. data/{lib/metasm → metasm}/exe_format/mz.rb +2 -0
  169. data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
  170. data/{lib/metasm → metasm}/exe_format/pe.rb +96 -11
  171. data/metasm/exe_format/pyc.rb +167 -0
  172. data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
  173. data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
  174. data/metasm/exe_format/shellcode_rwx.rb +114 -0
  175. data/metasm/exe_format/swf.rb +205 -0
  176. data/metasm/exe_format/wasm.rb +402 -0
  177. data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
  178. data/metasm/exe_format/zip.rb +335 -0
  179. data/metasm/gui.rb +13 -0
  180. data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
  181. data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
  182. data/{lib/metasm → metasm}/gui/dasm_decomp.rb +177 -114
  183. data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
  184. data/metasm/gui/dasm_graph.rb +1754 -0
  185. data/{lib/metasm → metasm}/gui/dasm_hex.rb +16 -12
  186. data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
  187. data/{lib/metasm → metasm}/gui/dasm_main.rb +360 -77
  188. data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
  189. data/{lib/metasm → metasm}/gui/debug.rb +109 -34
  190. data/{lib/metasm → metasm}/gui/gtk.rb +174 -44
  191. data/{lib/metasm → metasm}/gui/qt.rb +14 -4
  192. data/{lib/metasm → metasm}/gui/win32.rb +180 -43
  193. data/{lib/metasm → metasm}/gui/x11.rb +59 -59
  194. data/{lib/metasm → metasm}/main.rb +421 -286
  195. data/metasm/os/emulator.rb +175 -0
  196. data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
  197. data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
  198. data/{lib/metasm → metasm}/os/linux.rb +628 -151
  199. data/metasm/os/main.rb +335 -0
  200. data/{lib/metasm → metasm}/os/windows.rb +151 -58
  201. data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
  202. data/{lib/metasm → metasm}/parse.rb +49 -36
  203. data/{lib/metasm → metasm}/parse_c.rb +405 -246
  204. data/{lib/metasm → metasm}/preprocessor.rb +71 -41
  205. data/{lib/metasm → metasm}/render.rb +14 -38
  206. data/misc/hexdump.rb +4 -3
  207. data/misc/lint.rb +58 -0
  208. data/misc/objdiff.rb +4 -1
  209. data/misc/objscan.rb +1 -1
  210. data/misc/openrisc-parser.rb +79 -0
  211. data/misc/txt2html.rb +9 -7
  212. data/samples/bindiff.rb +3 -4
  213. data/samples/dasm-plugins/bindiff.rb +15 -0
  214. data/samples/dasm-plugins/bookmark.rb +133 -0
  215. data/samples/dasm-plugins/c_constants.rb +57 -0
  216. data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
  217. data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
  218. data/samples/dasm-plugins/dasm_all.rb +70 -0
  219. data/samples/dasm-plugins/demangle_cpp.rb +31 -0
  220. data/samples/dasm-plugins/deobfuscate.rb +251 -0
  221. data/samples/dasm-plugins/dump_text.rb +35 -0
  222. data/samples/dasm-plugins/export_graph_svg.rb +86 -0
  223. data/samples/dasm-plugins/findgadget.rb +75 -0
  224. data/samples/dasm-plugins/hl_opcode.rb +32 -0
  225. data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
  226. data/samples/dasm-plugins/imm2off.rb +34 -0
  227. data/samples/dasm-plugins/match_libsigs.rb +93 -0
  228. data/samples/dasm-plugins/patch_file.rb +95 -0
  229. data/samples/dasm-plugins/scanfuncstart.rb +36 -0
  230. data/samples/dasm-plugins/scanxrefs.rb +29 -0
  231. data/samples/dasm-plugins/selfmodify.rb +197 -0
  232. data/samples/dasm-plugins/stringsxrefs.rb +28 -0
  233. data/samples/dasmnavig.rb +1 -1
  234. data/samples/dbg-apihook.rb +24 -9
  235. data/samples/dbg-plugins/heapscan.rb +283 -0
  236. data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
  237. data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
  238. data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
  239. data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
  240. data/samples/dbg-plugins/heapscan/winheap.h +174 -0
  241. data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
  242. data/samples/dbg-plugins/trace_func.rb +214 -0
  243. data/samples/disassemble-gui.rb +48 -7
  244. data/samples/disassemble.rb +31 -6
  245. data/samples/dump_upx.rb +24 -12
  246. data/samples/dynamic_ruby.rb +35 -27
  247. data/samples/elfencode.rb +15 -0
  248. data/samples/emubios.rb +251 -0
  249. data/samples/emudbg.rb +127 -0
  250. data/samples/exeencode.rb +6 -5
  251. data/samples/factorize-headers-peimports.rb +1 -1
  252. data/samples/lindebug.rb +186 -391
  253. data/samples/metasm-shell.rb +68 -57
  254. data/samples/peldr.rb +2 -2
  255. data/tests/all.rb +1 -1
  256. data/tests/arc.rb +26 -0
  257. data/tests/dynldr.rb +22 -4
  258. data/tests/expression.rb +57 -0
  259. data/tests/graph_layout.rb +285 -0
  260. data/tests/ia32.rb +80 -26
  261. data/tests/mcs51.rb +27 -0
  262. data/tests/mips.rb +10 -3
  263. data/tests/preprocessor.rb +18 -0
  264. data/tests/x86_64.rb +66 -18
  265. metadata +465 -219
  266. metadata.gz.sig +2 -0
  267. data/lib/metasm/arm/opcodes.rb +0 -177
  268. data/lib/metasm/gui.rb +0 -23
  269. data/lib/metasm/gui/dasm_graph.rb +0 -1354
  270. data/lib/metasm/ia32.rb +0 -14
  271. data/lib/metasm/ia32/opcodes.rb +0 -872
  272. data/lib/metasm/ppc/parse.rb +0 -52
  273. data/lib/metasm/x86_64.rb +0 -12
  274. data/lib/metasm/x86_64/opcodes.rb +0 -118
  275. data/samples/gdbclient.rb +0 -583
  276. data/samples/rubstop.rb +0 -399
@@ -0,0 +1,70 @@
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: retrieve a section section, and disassemble everything it can, skipping existing code and nops
8
+ # usage: load the plugin, then call (ruby snipped): dasm.dasm_all_section '.text'
9
+ def dasm_all(addrstart, length, method=:disassemble_fast_deep)
10
+ s = get_section_at(addrstart)
11
+ return if not s
12
+ s = s[0]
13
+ boff = s.ptr
14
+ off = 0
15
+ while off < length
16
+ if di = di_at(addrstart + off)
17
+ off += di.bin_length
18
+ elsif @decoded[addrstart+off]
19
+ off += 1
20
+ else
21
+ s.ptr = boff+off
22
+ maydi = cpu.decode_instruction(s, 0)
23
+ if not maydi
24
+ off += 1
25
+ elsif maydi.instruction.to_s =~ /nop|lea (.*), \[\1(?:\+0)?\]|mov (.*), \2|int 3/
26
+ off += maydi.bin_length
27
+ else
28
+ puts "dasm_all: found #{Expression[addrstart+off]}" if $VERBOSE
29
+ send(method, addrstart+off)
30
+ end
31
+ end
32
+ Gui.main_iter if gui and off & 15 == 0
33
+ end
34
+
35
+ count = 0
36
+ off = 0
37
+ while off < length
38
+ addr = addrstart+off
39
+ if di = di_at(addr)
40
+ if di.block_head?
41
+ b = di.block
42
+ if not @function[addr] and b.from_subfuncret.to_a.empty? and b.from_normal.to_a.empty?
43
+ l = auto_label_at(addr, 'sub_orph')
44
+ puts "dasm_all: found orphan function #{l}"
45
+ @function[addrstart+off] = DecodedFunction.new
46
+ @function[addrstart+off].finalized = true
47
+ detect_function_thunk(addr)
48
+ count += 1
49
+ end
50
+ end
51
+ off += di.bin_length
52
+ else
53
+ off += 1
54
+ end
55
+ Gui.main_iter if gui and off & 15 == 0
56
+ end
57
+
58
+ puts "found #{count} orphan functions" if $VERBOSE
59
+
60
+ gui.gui_update if gui
61
+ end
62
+
63
+ def dasm_all_section(name, method=:disassemble_fast_deep)
64
+ section_info.each { |n, a, l, i|
65
+ if name == n
66
+ dasm_all(Expression[a].reduce, l, method)
67
+ end
68
+ }
69
+ true
70
+ end
@@ -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
+ }