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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.hgtags +3 -0
- data/Gemfile +1 -0
- data/INSTALL +61 -0
- data/LICENCE +458 -0
- data/README +29 -21
- data/Rakefile +10 -0
- data/TODO +10 -12
- data/doc/code_organisation.txt +2 -0
- data/doc/core/DynLdr.txt +247 -0
- data/doc/core/ExeFormat.txt +43 -0
- data/doc/core/Expression.txt +220 -0
- data/doc/core/GNUExports.txt +27 -0
- data/doc/core/Ia32.txt +236 -0
- data/doc/core/SerialStruct.txt +108 -0
- data/doc/core/VirtualString.txt +145 -0
- data/doc/core/WindowsExports.txt +61 -0
- data/doc/core/index.txt +1 -0
- data/doc/style.css +6 -3
- data/doc/usage/debugger.txt +327 -0
- data/doc/usage/index.txt +1 -0
- data/doc/use_cases.txt +2 -2
- data/metasm.gemspec +22 -0
- data/{lib/metasm.rb → metasm.rb} +11 -3
- data/{lib/metasm → metasm}/compile_c.rb +13 -7
- data/metasm/cpu/arc.rb +8 -0
- data/metasm/cpu/arc/decode.rb +425 -0
- data/metasm/cpu/arc/main.rb +191 -0
- data/metasm/cpu/arc/opcodes.rb +588 -0
- data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
- data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
- data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
- data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
- data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
- data/metasm/cpu/arm/opcodes.rb +324 -0
- data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
- data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
- data/metasm/cpu/arm64.rb +15 -0
- data/metasm/cpu/arm64/debug.rb +38 -0
- data/metasm/cpu/arm64/decode.rb +289 -0
- data/metasm/cpu/arm64/encode.rb +41 -0
- data/metasm/cpu/arm64/main.rb +105 -0
- data/metasm/cpu/arm64/opcodes.rb +232 -0
- data/metasm/cpu/arm64/parse.rb +20 -0
- data/metasm/cpu/arm64/render.rb +95 -0
- data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
- data/metasm/cpu/bpf/decode.rb +142 -0
- data/metasm/cpu/bpf/main.rb +60 -0
- data/metasm/cpu/bpf/opcodes.rb +81 -0
- data/metasm/cpu/bpf/render.rb +41 -0
- data/metasm/cpu/cy16.rb +9 -0
- data/metasm/cpu/cy16/decode.rb +253 -0
- data/metasm/cpu/cy16/main.rb +63 -0
- data/metasm/cpu/cy16/opcodes.rb +78 -0
- data/metasm/cpu/cy16/render.rb +41 -0
- data/metasm/cpu/dalvik.rb +11 -0
- data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
- data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
- data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
- data/metasm/cpu/ia32.rb +17 -0
- data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
- data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
- data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
- data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
- data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
- data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
- data/metasm/cpu/ia32/opcodes.rb +1424 -0
- data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
- data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
- data/metasm/cpu/mips.rb +14 -0
- data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
- data/metasm/cpu/mips/debug.rb +42 -0
- data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
- data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
- data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
- data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
- data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
- data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
- data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
- data/metasm/cpu/msp430/decode.rb +247 -0
- data/metasm/cpu/msp430/main.rb +62 -0
- data/metasm/cpu/msp430/opcodes.rb +101 -0
- data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
- data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
- data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
- data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
- data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
- data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
- data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
- data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
- data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
- data/metasm/cpu/ppc/parse.rb +55 -0
- data/metasm/cpu/python.rb +8 -0
- data/metasm/cpu/python/decode.rb +136 -0
- data/metasm/cpu/python/main.rb +36 -0
- data/metasm/cpu/python/opcodes.rb +180 -0
- data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
- data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
- data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
- data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
- data/metasm/cpu/x86_64.rb +15 -0
- data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
- data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
- data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
- data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
- data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
- data/metasm/cpu/x86_64/opcodes.rb +136 -0
- data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
- data/metasm/cpu/x86_64/render.rb +35 -0
- data/metasm/cpu/z80.rb +9 -0
- data/metasm/cpu/z80/decode.rb +313 -0
- data/metasm/cpu/z80/main.rb +67 -0
- data/metasm/cpu/z80/opcodes.rb +224 -0
- data/metasm/cpu/z80/render.rb +59 -0
- data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
- data/{lib/metasm → metasm}/decode.rb +35 -4
- data/{lib/metasm → metasm}/decompile.rb +15 -16
- data/{lib/metasm → metasm}/disassemble.rb +201 -45
- data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
- data/{lib/metasm → metasm}/dynldr.rb +220 -133
- data/{lib/metasm → metasm}/encode.rb +10 -1
- data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
- data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
- data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
- data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
- data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
- data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
- data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
- data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
- data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
- data/metasm/exe_format/gb.rb +65 -0
- data/metasm/exe_format/javaclass.rb +424 -0
- data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
- data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
- data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
- data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
- data/metasm/exe_format/pyc.rb +167 -0
- data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
- data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
- data/metasm/exe_format/shellcode_rwx.rb +114 -0
- data/metasm/exe_format/swf.rb +205 -0
- data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
- data/metasm/exe_format/zip.rb +335 -0
- data/metasm/gui.rb +13 -0
- data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
- data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
- data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
- data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
- data/metasm/gui/dasm_graph.rb +1695 -0
- data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
- data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
- data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
- data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
- data/{lib/metasm → metasm}/gui/debug.rb +93 -27
- data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
- data/{lib/metasm → metasm}/gui/qt.rb +12 -2
- data/{lib/metasm → metasm}/gui/win32.rb +179 -42
- data/{lib/metasm → metasm}/gui/x11.rb +59 -59
- data/{lib/metasm → metasm}/main.rb +389 -264
- data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
- data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
- data/{lib/metasm → metasm}/os/linux.rb +628 -151
- data/metasm/os/main.rb +330 -0
- data/{lib/metasm → metasm}/os/windows.rb +132 -42
- data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
- data/{lib/metasm → metasm}/parse.rb +26 -24
- data/{lib/metasm → metasm}/parse_c.rb +221 -116
- data/{lib/metasm → metasm}/preprocessor.rb +55 -40
- data/{lib/metasm → metasm}/render.rb +14 -38
- data/misc/hexdump.rb +2 -1
- data/misc/lint.rb +58 -0
- data/misc/txt2html.rb +9 -7
- data/samples/bindiff.rb +3 -4
- data/samples/dasm-plugins/bindiff.rb +15 -0
- data/samples/dasm-plugins/bookmark.rb +133 -0
- data/samples/dasm-plugins/c_constants.rb +57 -0
- data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
- data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
- data/samples/dasm-plugins/dasm_all.rb +70 -0
- data/samples/dasm-plugins/demangle_cpp.rb +31 -0
- data/samples/dasm-plugins/deobfuscate.rb +251 -0
- data/samples/dasm-plugins/dump_text.rb +35 -0
- data/samples/dasm-plugins/export_graph_svg.rb +86 -0
- data/samples/dasm-plugins/findgadget.rb +75 -0
- data/samples/dasm-plugins/hl_opcode.rb +32 -0
- data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
- data/samples/dasm-plugins/imm2off.rb +34 -0
- data/samples/dasm-plugins/match_libsigs.rb +93 -0
- data/samples/dasm-plugins/patch_file.rb +95 -0
- data/samples/dasm-plugins/scanfuncstart.rb +36 -0
- data/samples/dasm-plugins/scanxrefs.rb +26 -0
- data/samples/dasm-plugins/selfmodify.rb +197 -0
- data/samples/dasm-plugins/stringsxrefs.rb +28 -0
- data/samples/dasmnavig.rb +1 -1
- data/samples/dbg-apihook.rb +24 -9
- data/samples/dbg-plugins/heapscan.rb +283 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
- data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
- data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
- data/samples/dbg-plugins/heapscan/winheap.h +174 -0
- data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
- data/samples/dbg-plugins/trace_func.rb +214 -0
- data/samples/disassemble-gui.rb +35 -5
- data/samples/disassemble.rb +31 -6
- data/samples/dump_upx.rb +24 -12
- data/samples/dynamic_ruby.rb +12 -3
- data/samples/exeencode.rb +6 -5
- data/samples/factorize-headers-peimports.rb +1 -1
- data/samples/lindebug.rb +175 -381
- data/samples/metasm-shell.rb +1 -2
- data/samples/peldr.rb +2 -2
- data/tests/all.rb +1 -1
- data/tests/arc.rb +26 -0
- data/tests/dynldr.rb +22 -4
- data/tests/expression.rb +55 -0
- data/tests/graph_layout.rb +285 -0
- data/tests/ia32.rb +79 -26
- data/tests/mips.rb +9 -2
- data/tests/x86_64.rb +66 -18
- metadata +330 -218
- data/lib/metasm/arm/opcodes.rb +0 -177
- data/lib/metasm/gui.rb +0 -23
- data/lib/metasm/gui/dasm_graph.rb +0 -1354
- data/lib/metasm/ia32.rb +0 -14
- data/lib/metasm/ia32/opcodes.rb +0 -873
- data/lib/metasm/ppc/parse.rb +0 -52
- data/lib/metasm/x86_64.rb +0 -12
- data/lib/metasm/x86_64/opcodes.rb +0 -118
- data/samples/gdbclient.rb +0 -583
- 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('<', '<').gsub('>', '>')
|
|
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
|