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,32 @@
|
|
|
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: hilight lines of code based on the opcode name
|
|
8
|
+
if gui
|
|
9
|
+
@gui_opcode_color = {
|
|
10
|
+
:call => :green_bg,
|
|
11
|
+
:jmp => :red_bg,
|
|
12
|
+
:jcc => :orange_bg,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
obg = gui.bg_color_callback # chain old callback
|
|
16
|
+
gui.bg_color_callback = lambda { |a|
|
|
17
|
+
if di = di_at(a) and pr = di.opcode.props
|
|
18
|
+
if pr[:saveip] and (@function[di.block.to_normal.to_a.first] or di.block.to_subfuncret.to_a.first)
|
|
19
|
+
# don't color call+pop
|
|
20
|
+
@gui_opcode_color[:call]
|
|
21
|
+
elsif pr[:stopexec]
|
|
22
|
+
@gui_opcode_color[:jmp]
|
|
23
|
+
elsif pr[:setip]
|
|
24
|
+
@gui_opcode_color[:jcc]
|
|
25
|
+
else
|
|
26
|
+
obg[a] if obg
|
|
27
|
+
end
|
|
28
|
+
else
|
|
29
|
+
obg[a] if obg
|
|
30
|
+
end
|
|
31
|
+
}
|
|
32
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
# This plugin will create a monitoring process running samples/hotfix_gtk_dbg.rb on the current process (to fix a GTK crash when closing a window)
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
mypid = Process.pid
|
|
12
|
+
|
|
13
|
+
if (!Process.fork)
|
|
14
|
+
ARGV.clear
|
|
15
|
+
ARGV << mypid
|
|
16
|
+
$VERBOSE = false
|
|
17
|
+
Kernel.load File.join(Metasmdir, 'samples', 'hotfix_gtk_dbg.rb')
|
|
18
|
+
exit!
|
|
19
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
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
|
|
8
|
+
# walks all disassembled instructions referencing an address
|
|
9
|
+
# if the address is a label, update the instruction to use the label
|
|
10
|
+
# esp. useful after a disassemble_fast, with a .map file
|
|
11
|
+
|
|
12
|
+
def addrtolabel
|
|
13
|
+
bp = prog_binding.invert
|
|
14
|
+
@decoded.each_value { |di|
|
|
15
|
+
next if not di.kind_of?(DecodedInstruction)
|
|
16
|
+
di.each_expr { |e|
|
|
17
|
+
next unless e.kind_of?(Expression)
|
|
18
|
+
if l = bp[e.lexpr]
|
|
19
|
+
add_xref(e.lexpr, Xref.new(:addr, di.address))
|
|
20
|
+
e.lexpr = Expression[l]
|
|
21
|
+
end
|
|
22
|
+
if l = bp[e.rexpr]
|
|
23
|
+
add_xref(e.rexpr, Xref.new(:addr, di.address))
|
|
24
|
+
e.rexpr = (e.lexpr ? Expression[l] : l)
|
|
25
|
+
end
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
if gui
|
|
32
|
+
addrtolabel
|
|
33
|
+
gui.gui_update
|
|
34
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
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: allow loading library signature files (see samples/generate_libsigs.rb)
|
|
8
|
+
|
|
9
|
+
class LibSignature
|
|
10
|
+
attr_accessor :sigs, :siglenmax, :giantregex
|
|
11
|
+
# load signatures from a signature file
|
|
12
|
+
def initialize(file)
|
|
13
|
+
# hash symbolname => signature
|
|
14
|
+
@sigs = {}
|
|
15
|
+
|
|
16
|
+
# populate sigs
|
|
17
|
+
symname = nil
|
|
18
|
+
sig = ''
|
|
19
|
+
File.read(file).each_line { |l|
|
|
20
|
+
case l
|
|
21
|
+
when /^ /
|
|
22
|
+
sig << l.strip
|
|
23
|
+
else
|
|
24
|
+
@sigs[symname] = sig
|
|
25
|
+
symname = l.strip
|
|
26
|
+
sig = ''
|
|
27
|
+
end
|
|
28
|
+
}
|
|
29
|
+
@sigs[symname] = sig
|
|
30
|
+
@sigs.delete nil
|
|
31
|
+
@siglenmax = @sigs.values.map { |v| v.length }.max
|
|
32
|
+
|
|
33
|
+
# compile a giant regex from the signatures
|
|
34
|
+
re = @sigs.values.uniq.map { |sigh|
|
|
35
|
+
sigh.gsub(/../) { |b| b == '..' ? '.' : ('\\x' + b) }
|
|
36
|
+
}.join('|')
|
|
37
|
+
|
|
38
|
+
# 'n' is a magic flag to allow high bytes in the regex (ruby1.9 + utfail)
|
|
39
|
+
@giantregex = Regexp.new re, Regexp::MULTILINE, 'n'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# we found a match on str at off, identify the specific symbol that matched
|
|
43
|
+
# on conflict, only return the first match
|
|
44
|
+
def matched_findsym(str, off)
|
|
45
|
+
str = str[off, @siglenmax].unpack('H*').first
|
|
46
|
+
@sigs.find { |sym, sig| str =~ /^#{sig}/i }[0]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# matches the signatures against a raw string
|
|
50
|
+
# yields offset, symname for each match
|
|
51
|
+
# returns nr of matches found
|
|
52
|
+
def match_chunk(str)
|
|
53
|
+
count = 0
|
|
54
|
+
off = 0
|
|
55
|
+
while o = (str[off..-1] =~ @giantregex)
|
|
56
|
+
count += 1
|
|
57
|
+
off += o
|
|
58
|
+
sym = matched_findsym(str, off)
|
|
59
|
+
yield off, sym
|
|
60
|
+
off += 1
|
|
61
|
+
end
|
|
62
|
+
count
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# matches the signatures against a big raw string
|
|
66
|
+
# yields offset, symname for each match
|
|
67
|
+
# returns nr of matches found
|
|
68
|
+
def match(str)
|
|
69
|
+
chunksz = 1 << 20
|
|
70
|
+
|
|
71
|
+
chunkoff = 0
|
|
72
|
+
count = 0
|
|
73
|
+
while chunkoff < str.length
|
|
74
|
+
chunk = str[chunkoff, chunksz+@siglenmax]
|
|
75
|
+
count += match_chunk(chunk) { |o, sym| yield chunkoff+o, sym if o < chunksz }
|
|
76
|
+
chunkoff += chunksz
|
|
77
|
+
end
|
|
78
|
+
count
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def match_libsigs(sigfile)
|
|
83
|
+
ls = LibSignature.new(sigfile)
|
|
84
|
+
count = 0
|
|
85
|
+
@sections.each { |b, s|
|
|
86
|
+
count += ls.match(s.data) { |off, sym| set_label_at(b+off, sym) }
|
|
87
|
+
}
|
|
88
|
+
count
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
if gui
|
|
92
|
+
gui.openfile('signature file to load') { |f| gui.messagebox "#{match_libsigs(f)} signatures found" }
|
|
93
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
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: allow patching the file from the dasm interface
|
|
8
|
+
# use P to assemble a new instruction at the current address
|
|
9
|
+
|
|
10
|
+
# backup the executable file
|
|
11
|
+
def backup_program_file
|
|
12
|
+
f = @program.filename
|
|
13
|
+
if File.exist?(f) and not File.exist?(f + '.bak')
|
|
14
|
+
File.open(f + '.bak', 'wb') { |wfd|
|
|
15
|
+
File.open(f, 'rb') { |rfd|
|
|
16
|
+
while buf = rfd.read(1024*1024)
|
|
17
|
+
wfd.write buf
|
|
18
|
+
end
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# create a backup and reopen the backend VirtualFile RW
|
|
25
|
+
def reopen_rw(addr=nil, edata=nil)
|
|
26
|
+
if not edata
|
|
27
|
+
sections.each { |k, v| reopen_rw(k, v) }
|
|
28
|
+
return true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
return if not File.writable?(@program.filename)
|
|
32
|
+
backup_program_file
|
|
33
|
+
if not edata.data.kind_of? VirtualFile
|
|
34
|
+
# section too small, loaded as real String
|
|
35
|
+
# force reopen as VFile (allow hexediting in gui)
|
|
36
|
+
return if not off = addr_to_fileoff(addr)
|
|
37
|
+
len = edata.data.length
|
|
38
|
+
edata.data = VirtualFile.read(@program.filename, 'rb+').dup(off, len)
|
|
39
|
+
else
|
|
40
|
+
edata.data.fd.reopen @program.filename, 'rb+'
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
raise "cant find original file" if not @program.filename or not File.exist? @program.filename
|
|
45
|
+
|
|
46
|
+
reopen_rw
|
|
47
|
+
|
|
48
|
+
def patch_instrs(addr, asmsrc)
|
|
49
|
+
sc = Metasm::Shellcode.new(cpu, addr) # pfx needed for autorequire
|
|
50
|
+
sc.assemble(asmsrc, cpu)
|
|
51
|
+
sc.encoded.fixup! prog_binding # allow references to dasm labels in the shellcode
|
|
52
|
+
raw = sc.encode_string
|
|
53
|
+
|
|
54
|
+
if s = get_section_at(addr) and s[0].data.kind_of? VirtualFile
|
|
55
|
+
s[0][s[0].ptr, raw.length] = raw
|
|
56
|
+
elsif o = addr_to_fileoff(addr) # section too small, not loaded as a VirtFile
|
|
57
|
+
backup_program_file
|
|
58
|
+
File.open(@program.filename, 'rb+') { |fd|
|
|
59
|
+
fd.pos = o
|
|
60
|
+
fd.write raw
|
|
61
|
+
}
|
|
62
|
+
s[0][s[0].ptr, raw.length] = raw if s
|
|
63
|
+
else
|
|
64
|
+
return
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
b = split_block(addr)
|
|
68
|
+
|
|
69
|
+
# clear what we had in the rewritten space
|
|
70
|
+
raw.length.times { |rawoff|
|
|
71
|
+
next if not di = di_at(addr+rawoff)
|
|
72
|
+
di.block.list.each { |ldi| @decoded.delete ldi.address }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
disassemble_fast(addr) if b
|
|
76
|
+
if b and @decoded[addr]
|
|
77
|
+
nb = @decoded[addr].block
|
|
78
|
+
nb.from_normal = b.from_normal
|
|
79
|
+
nb.from_subfuncret = b.from_subfuncret
|
|
80
|
+
nb.from_indirect = b.from_indirect
|
|
81
|
+
end
|
|
82
|
+
true
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
if gui
|
|
86
|
+
gui.keyboard_callback[?P] = lambda { |k|
|
|
87
|
+
addr = gui.curaddr
|
|
88
|
+
gui.inputbox('new instructions') { |src|
|
|
89
|
+
src = src.gsub(/;\s+/, "\n")
|
|
90
|
+
patch_instrs(addr, src)
|
|
91
|
+
gui.gui_update
|
|
92
|
+
}
|
|
93
|
+
true
|
|
94
|
+
}
|
|
95
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
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: scan the memory for a 'ret' which could indicate the beginning of the current function
|
|
8
|
+
# (x86 only)
|
|
9
|
+
def scanfuncstart(addr)
|
|
10
|
+
if o = (1..16).find { |off| @decoded[addr-off].kind_of? DecodedInstruction } and @decoded[addr-o].bin_length == o
|
|
11
|
+
addr -= o
|
|
12
|
+
end
|
|
13
|
+
if @decoded[addr].kind_of? DecodedInstruction
|
|
14
|
+
fs = find_function_start(addr)
|
|
15
|
+
return fs if fs != addr
|
|
16
|
+
end
|
|
17
|
+
edata = get_edata_at(addr)
|
|
18
|
+
if o = (1..1000).find { |off|
|
|
19
|
+
@decoded[addr-off-1] or
|
|
20
|
+
edata.data[edata.ptr-off-1] == ?\xcc or
|
|
21
|
+
edata.data[edata.ptr-off-1] == ?\xc3 or
|
|
22
|
+
edata.data[edata.ptr-off-3] == ?\xc2
|
|
23
|
+
}
|
|
24
|
+
o -= @decoded[addr-o-1].bin_length-1 if @decoded[addr-o-1].kind_of? DecodedInstruction
|
|
25
|
+
addr-o
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if gui
|
|
30
|
+
gui.keyboard_callback_ctrl[?P] = lambda { |*a|
|
|
31
|
+
if o = scanfuncstart(gui.curaddr)
|
|
32
|
+
gui.focus_addr(o)
|
|
33
|
+
end
|
|
34
|
+
true
|
|
35
|
+
}
|
|
36
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
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: scan for xrefs to the target address, incl. relative offsets (eg near call/jmp)
|
|
8
|
+
def scanxrefs(target)
|
|
9
|
+
ans = []
|
|
10
|
+
sections.sort.each { |s_addr, edata|
|
|
11
|
+
raw = edata.data.to_str
|
|
12
|
+
(0..raw.length-4).each { |off|
|
|
13
|
+
r = raw[off, 4].unpack('V').first
|
|
14
|
+
ans << (s_addr + off) if (r + off+4 + s_addr)&0xffffffff == target or r == target
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
ans
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
gui.keyboard_callback[?X] = lambda { |*a|
|
|
21
|
+
target = gui.curaddr
|
|
22
|
+
ans = scanxrefs(target)
|
|
23
|
+
list = [['addr']] + ans.map { |off| [Expression[off].to_s] }
|
|
24
|
+
gui.listwindow("scanned xrefs to #{Expression[target]}", list) { |i| gui.focus_addr i[0] }
|
|
25
|
+
true
|
|
26
|
+
} if gui
|
|
@@ -0,0 +1,197 @@
|
|
|
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
|
+
# This file tries to handle simple self-modifying code patterns
|
|
9
|
+
# To be used as a --plugin for a Disassembler object
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
module SMC
|
|
13
|
+
|
|
14
|
+
# a copy-on-write copy of dasm address space (continuous segment only)
|
|
15
|
+
class CoWData
|
|
16
|
+
attr_accessor :startaddr, :data
|
|
17
|
+
def initialize(dasm)
|
|
18
|
+
@dasm = dasm
|
|
19
|
+
@startaddr = 0
|
|
20
|
+
@data = ''
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# return a substring, either from the local cache or from dasm
|
|
24
|
+
# handles overlap
|
|
25
|
+
def [](addr, len)
|
|
26
|
+
if @data.empty?
|
|
27
|
+
s, e = @dasm.get_section_at(addr)
|
|
28
|
+
return if not s
|
|
29
|
+
return s.read(len)
|
|
30
|
+
end
|
|
31
|
+
raddr = addr - @base
|
|
32
|
+
rstart = @startaddr - @base
|
|
33
|
+
if raddr >= rstart and raddr+len <= rstart+@data.length
|
|
34
|
+
@data[0, raddr+len-rstart]
|
|
35
|
+
else
|
|
36
|
+
s, e = @dasm.get_section_at(addr)
|
|
37
|
+
return if not s
|
|
38
|
+
obuf = s.read(len)
|
|
39
|
+
len = obuf.length
|
|
40
|
+
if raddr < rstart and raddr+len > rstart
|
|
41
|
+
olen = [raddr+len-rstart, @data.length].min
|
|
42
|
+
obuf[rstart-raddr, olen] = @data[0, olen]
|
|
43
|
+
elsif raddr < rstart+@data.length and raddr+len > rstart+@data.length
|
|
44
|
+
obuf[0, rstart+@data.length-raddr] = @data[raddr-rstart, rstart+@data.length-raddr]
|
|
45
|
+
end
|
|
46
|
+
obuf
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# set a substring value in the cache
|
|
51
|
+
def []=(addr, len, newdata)
|
|
52
|
+
raise 'len mismatch' if len != newdata.length
|
|
53
|
+
if @data.empty?
|
|
54
|
+
@base = @startaddr = addr
|
|
55
|
+
@data << newdata
|
|
56
|
+
return
|
|
57
|
+
end
|
|
58
|
+
raddr = addr - @base
|
|
59
|
+
rstart = @startaddr - @base
|
|
60
|
+
if raddr+newdata.length < rstart
|
|
61
|
+
s, e = @dasm.get_section_at(addr)
|
|
62
|
+
raise if not s
|
|
63
|
+
obuf = s.read(rstart-(raddr+newdata.length))
|
|
64
|
+
raise if obuf.length != rstart-(raddr+newdata.length)
|
|
65
|
+
newdata += obuf
|
|
66
|
+
elsif raddr > rstart+@data.length
|
|
67
|
+
s, e = @dasm.get_section_at(@startaddr+@data.length)
|
|
68
|
+
raise if not s
|
|
69
|
+
obuf = s.read(raddr-(rstart+@data.length))
|
|
70
|
+
raise if obuf.length != raddr-(rstart+@data.length)
|
|
71
|
+
@data += obuf
|
|
72
|
+
end
|
|
73
|
+
if raddr < rstart
|
|
74
|
+
@data = newdata + @data[raddr+newdata.length-rstart..-1].to_s
|
|
75
|
+
@startaddr = addr
|
|
76
|
+
else
|
|
77
|
+
@data[raddr-rstart, newdata.length] = newdata
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
VirtSections = {}
|
|
83
|
+
|
|
84
|
+
# try to emulate the byte modifications
|
|
85
|
+
# creates a new virtual section in dasm holding decoded data
|
|
86
|
+
# adds the virtual section to the dasm, stores the addresses in VirtSections[dasm]
|
|
87
|
+
# returns true if successful
|
|
88
|
+
def self.emu(dasm, addr)
|
|
89
|
+
puts "emulate SMC @#{Metasm::Expression[addr]}" if $VERBOSE
|
|
90
|
+
|
|
91
|
+
writer = nil
|
|
92
|
+
dasm.each_xref(addr, :w) { |xr| writer = xr.origin }
|
|
93
|
+
return if not dasm.di_at(writer)
|
|
94
|
+
|
|
95
|
+
a_pre, a_entry, a_cond, a_out, loop_bd = find_loop(dasm, writer)
|
|
96
|
+
return if not a_pre
|
|
97
|
+
|
|
98
|
+
# expression checking if we get out of the loop
|
|
99
|
+
loop_again_cond = dasm.cpu.get_jump_condition(dasm.decoded[a_cond])
|
|
100
|
+
loop_again_cond = Expression[:'!', loop_again_cond] if dasm.decoded[a_cond].next_addr != a_out
|
|
101
|
+
|
|
102
|
+
init_bd = {}
|
|
103
|
+
loop_bd.keys.grep(Symbol).each { |reg|
|
|
104
|
+
bt = dasm.backtrace(reg, a_pre, :include_start => true)
|
|
105
|
+
init_bd[reg] = bt.first if bt.length == 1 and bt.first != Metasm::Expression::Unknown and bt.first != Metasm::Expression[reg]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# reject non-determinist memory write
|
|
109
|
+
loop_bd.delete_if { |k, v| k.kind_of? Metasm::Indirection and not dasm.get_section_at(k.pointer.bind(init_bd).reduce) }
|
|
110
|
+
|
|
111
|
+
cow_data = CoWData.new(dasm)
|
|
112
|
+
|
|
113
|
+
puts "emulation running..." if $VERBOSE
|
|
114
|
+
pre_bd = init_bd
|
|
115
|
+
loop do
|
|
116
|
+
# the effects of the loop
|
|
117
|
+
post_bd = loop_bd.inject({}) { |bd, (k, v)|
|
|
118
|
+
if k.kind_of? Metasm::Indirection
|
|
119
|
+
k = k.bind(pre_bd).reduce_rec
|
|
120
|
+
raise "bad ptr #{k}" if not dasm.get_section_at(k.pointer.reduce)
|
|
121
|
+
end
|
|
122
|
+
bd.update k => Metasm::Expression[v.bind(pre_bd).reduce]
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
# the indirections used by the loop
|
|
126
|
+
# read mem from cow_data
|
|
127
|
+
# ignores stacked indirections & keys
|
|
128
|
+
ind_bd = {}
|
|
129
|
+
post_bd.values.map { |v| v.expr_indirections }.flatten.uniq.each { |ind|
|
|
130
|
+
p = ind.pointer.reduce
|
|
131
|
+
raise "bad loop read #{ind}" if not p.kind_of? Integer
|
|
132
|
+
ind_bd[ind] = Metasm::Expression.decode_imm(cow_data[p, ind.len], "u#{ind.len*8}".to_sym, dasm.cpu.endianness)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
post_bd.each { |k, v|
|
|
136
|
+
next if not k.kind_of? Metasm::Indirection
|
|
137
|
+
cow_data[k.pointer.reduce, k.len] = Metasm::Expression.encode_imm(v.bind(ind_bd).reduce, "u#{k.len*8}".to_sym, dasm.cpu.endianness)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
break if loop_again_cond.bind(post_bd).reduce == 0
|
|
141
|
+
|
|
142
|
+
pre_bd = post_bd
|
|
143
|
+
pre_bd.delete_if { |k, v| not k.kind_of? Symbol }
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
puts "emulation done (#{cow_data.data.length} bytes)" if $VERBOSE
|
|
147
|
+
|
|
148
|
+
VirtSections[dasm] ||= {}
|
|
149
|
+
newbase = "smc#{VirtSections[dasm].length}"
|
|
150
|
+
VirtSections[dasm][addr] = newbase
|
|
151
|
+
dasm.add_section(Metasm::EncodedData.new(cow_data.data), newbase)
|
|
152
|
+
dasm.comment[Metasm::Expression[newbase]] = "SelfModifyingCode from #{dasm.decoded[writer]}"
|
|
153
|
+
|
|
154
|
+
true
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# find the loop containing addr
|
|
158
|
+
# only trivial loops handled
|
|
159
|
+
# returns [loop start, last instr before loop, loop conditionnal jump, 1st instr after loop, loop binding]
|
|
160
|
+
def self.find_loop(dasm, addr)
|
|
161
|
+
b = dasm.decoded[addr].block
|
|
162
|
+
return if not b.to_normal.to_a.include? b.address
|
|
163
|
+
b1 = b2 = b
|
|
164
|
+
|
|
165
|
+
pre = (b1.from_normal - [b2.list.last.address]).first
|
|
166
|
+
first = b1.address
|
|
167
|
+
last = b2.list.last.address
|
|
168
|
+
post = (b2.to_normal - [b1.address]).first
|
|
169
|
+
loop_bd = dasm.code_binding(first, post)
|
|
170
|
+
|
|
171
|
+
[pre, first, last, post, loop_bd]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# redirects the code flow from addr to the decoded section
|
|
175
|
+
def self.redirect(dasm, addr)
|
|
176
|
+
return if not VirtSections[dasm] or not newto = Metasm::Expression[VirtSections[dasm][addr]]
|
|
177
|
+
dasm.each_instructionblock { |b|
|
|
178
|
+
next if not b.to_normal.to_a.include? addr
|
|
179
|
+
b.to_normal.map! { |tn| dasm.normalize(tn) == addr ? newto : tn }
|
|
180
|
+
dasm.add_xref(newto, Metasm::Xref.new(:x, b.list.last.address))
|
|
181
|
+
b.list.last.add_comment "x:#{newto}"
|
|
182
|
+
dasm.addrs_todo << [newto, b.list.last.address]
|
|
183
|
+
}
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
if self.kind_of? Metasm::Disassembler
|
|
188
|
+
# setup the smc callbacks
|
|
189
|
+
dasm = self
|
|
190
|
+
list = []
|
|
191
|
+
dasm.callback_selfmodifying = lambda { |addr| list << addr }
|
|
192
|
+
dasm.callback_finished = lambda {
|
|
193
|
+
while addr = list.pop
|
|
194
|
+
SMC.emu(dasm, addr) and SMC.redirect(dasm, addr)
|
|
195
|
+
end
|
|
196
|
+
}
|
|
197
|
+
end
|