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