metasm 1.0.0 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +3 -0
- data/.gitignore +3 -0
- data/.hgtags +3 -0
- data/Gemfile +3 -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 +3 -1
- 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 +23 -0
- data/{lib/metasm.rb → metasm.rb} +15 -3
- data/{lib/metasm → metasm}/compile_c.rb +15 -9
- data/metasm/cpu/arc.rb +8 -0
- data/metasm/cpu/arc/decode.rb +404 -0
- data/metasm/cpu/arc/main.rb +191 -0
- data/metasm/cpu/arc/opcodes.rb +588 -0
- data/metasm/cpu/arm.rb +14 -0
- data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
- data/{lib/metasm → metasm/cpu}/arm/decode.rb +15 -18
- data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
- data/{lib/metasm → metasm/cpu}/arm/main.rb +3 -6
- 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 +285 -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/mips/compile_c.rb → metasm/cpu/bpf.rb} +4 -2
- data/metasm/cpu/bpf/decode.rb +110 -0
- data/metasm/cpu/bpf/main.rb +60 -0
- data/metasm/cpu/bpf/opcodes.rb +81 -0
- data/metasm/cpu/bpf/render.rb +30 -0
- data/{lib/metasm/ppc.rb → metasm/cpu/cy16.rb} +2 -4
- data/metasm/cpu/cy16/decode.rb +247 -0
- data/metasm/cpu/cy16/main.rb +63 -0
- data/metasm/cpu/cy16/opcodes.rb +78 -0
- data/metasm/cpu/cy16/render.rb +30 -0
- data/metasm/cpu/dalvik.rb +11 -0
- data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +34 -34
- data/{lib/metasm → metasm/cpu}/dalvik/main.rb +71 -4
- data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +21 -12
- data/{lib/metasm/mips.rb → metasm/cpu/ebpf.rb} +3 -4
- data/metasm/cpu/ebpf/debug.rb +61 -0
- data/metasm/cpu/ebpf/decode.rb +142 -0
- data/metasm/cpu/ebpf/main.rb +58 -0
- data/metasm/cpu/ebpf/opcodes.rb +97 -0
- data/metasm/cpu/ebpf/render.rb +36 -0
- data/metasm/cpu/ia32.rb +17 -0
- data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +23 -9
- data/{lib/metasm → metasm/cpu}/ia32/debug.rb +44 -6
- data/{lib/metasm → metasm/cpu}/ia32/decode.rb +342 -128
- data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +75 -53
- data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
- data/{lib/metasm → metasm/cpu}/ia32/main.rb +66 -8
- data/metasm/cpu/ia32/opcodes.rb +1424 -0
- data/{lib/metasm → metasm/cpu}/ia32/parse.rb +55 -17
- data/{lib/metasm → metasm/cpu}/ia32/render.rb +32 -5
- data/metasm/cpu/mcs51.rb +8 -0
- data/metasm/cpu/mcs51/decode.rb +99 -0
- data/metasm/cpu/mcs51/main.rb +87 -0
- data/metasm/cpu/mcs51/opcodes.rb +120 -0
- data/metasm/cpu/mips.rb +14 -0
- data/metasm/cpu/mips/debug.rb +42 -0
- data/{lib/metasm → metasm/cpu}/mips/decode.rb +59 -38
- data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
- data/{lib/metasm → metasm/cpu}/mips/main.rb +13 -6
- data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +87 -18
- 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 +243 -0
- data/metasm/cpu/msp430/main.rb +62 -0
- data/metasm/cpu/msp430/opcodes.rb +101 -0
- data/metasm/cpu/openrisc.rb +11 -0
- data/metasm/cpu/openrisc/debug.rb +106 -0
- data/metasm/cpu/openrisc/decode.rb +182 -0
- data/metasm/cpu/openrisc/decompile.rb +350 -0
- data/metasm/cpu/openrisc/main.rb +70 -0
- data/metasm/cpu/openrisc/opcodes.rb +109 -0
- data/metasm/cpu/openrisc/render.rb +37 -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/metasm/cpu/ppc.rb +11 -0
- data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -37
- 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 +23 -18
- data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -6
- data/metasm/cpu/ppc/parse.rb +55 -0
- data/metasm/cpu/python.rb +8 -0
- data/metasm/cpu/python/decode.rb +116 -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 +50 -23
- data/{lib/metasm → metasm/cpu}/sh4/main.rb +38 -27
- data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
- data/metasm/cpu/st20.rb +9 -0
- data/metasm/cpu/st20/decode.rb +173 -0
- data/metasm/cpu/st20/decompile.rb +283 -0
- data/metasm/cpu/st20/main.rb +37 -0
- data/metasm/cpu/st20/opcodes.rb +140 -0
- data/{lib/metasm/arm.rb → metasm/cpu/webasm.rb} +4 -5
- data/metasm/cpu/webasm/debug.rb +31 -0
- data/metasm/cpu/webasm/decode.rb +321 -0
- data/metasm/cpu/webasm/decompile.rb +386 -0
- data/metasm/cpu/webasm/encode.rb +104 -0
- data/metasm/cpu/webasm/main.rb +81 -0
- data/metasm/cpu/webasm/opcodes.rb +214 -0
- data/metasm/cpu/x86_64.rb +15 -0
- data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +40 -25
- data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
- data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +58 -15
- data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +59 -28
- data/{lib/metasm → metasm/cpu}/x86_64/main.rb +18 -6
- data/metasm/cpu/x86_64/opcodes.rb +138 -0
- data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +12 -4
- data/metasm/cpu/x86_64/render.rb +35 -0
- data/metasm/cpu/z80.rb +9 -0
- data/metasm/cpu/z80/decode.rb +286 -0
- data/metasm/cpu/z80/main.rb +67 -0
- data/metasm/cpu/z80/opcodes.rb +224 -0
- data/metasm/cpu/z80/render.rb +48 -0
- data/{lib/metasm/os/main.rb → metasm/debug.rb} +201 -407
- data/{lib/metasm → metasm}/decode.rb +104 -24
- data/{lib/metasm → metasm}/decompile.rb +804 -478
- data/{lib/metasm → metasm}/disassemble.rb +385 -170
- data/{lib/metasm → metasm}/disassemble_api.rb +684 -105
- data/{lib/metasm → metasm}/dynldr.rb +231 -138
- data/{lib/metasm → metasm}/encode.rb +20 -5
- data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
- data/{lib/metasm → metasm}/exe_format/autoexe.rb +3 -0
- data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
- data/{lib/metasm → metasm}/exe_format/coff.rb +35 -7
- data/{lib/metasm → metasm}/exe_format/coff_decode.rb +70 -23
- data/{lib/metasm → metasm}/exe_format/coff_encode.rb +24 -22
- data/{lib/metasm → metasm}/exe_format/dex.rb +26 -8
- data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/elf.rb +108 -58
- data/{lib/metasm → metasm}/exe_format/elf_decode.rb +202 -36
- data/{lib/metasm → metasm}/exe_format/elf_encode.rb +126 -32
- data/metasm/exe_format/gb.rb +65 -0
- data/metasm/exe_format/javaclass.rb +424 -0
- data/{lib/metasm → metasm}/exe_format/macho.rb +218 -16
- data/{lib/metasm → metasm}/exe_format/main.rb +28 -3
- data/{lib/metasm → metasm}/exe_format/mz.rb +2 -0
- data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
- data/{lib/metasm → metasm}/exe_format/pe.rb +96 -11
- 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/metasm/exe_format/wasm.rb +402 -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 +177 -114
- data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
- data/metasm/gui/dasm_graph.rb +1754 -0
- data/{lib/metasm → metasm}/gui/dasm_hex.rb +16 -12
- data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
- data/{lib/metasm → metasm}/gui/dasm_main.rb +360 -77
- data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
- data/{lib/metasm → metasm}/gui/debug.rb +109 -34
- data/{lib/metasm → metasm}/gui/gtk.rb +174 -44
- data/{lib/metasm → metasm}/gui/qt.rb +14 -4
- data/{lib/metasm → metasm}/gui/win32.rb +180 -43
- data/{lib/metasm → metasm}/gui/x11.rb +59 -59
- data/{lib/metasm → metasm}/main.rb +421 -286
- data/metasm/os/emulator.rb +175 -0
- 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 +335 -0
- data/{lib/metasm → metasm}/os/windows.rb +151 -58
- data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
- data/{lib/metasm → metasm}/parse.rb +49 -36
- data/{lib/metasm → metasm}/parse_c.rb +405 -246
- data/{lib/metasm → metasm}/preprocessor.rb +71 -41
- data/{lib/metasm → metasm}/render.rb +14 -38
- data/misc/hexdump.rb +4 -3
- data/misc/lint.rb +58 -0
- data/misc/objdiff.rb +4 -1
- data/misc/objscan.rb +1 -1
- data/misc/openrisc-parser.rb +79 -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 +29 -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 +48 -7
- data/samples/disassemble.rb +31 -6
- data/samples/dump_upx.rb +24 -12
- data/samples/dynamic_ruby.rb +35 -27
- data/samples/elfencode.rb +15 -0
- data/samples/emubios.rb +251 -0
- data/samples/emudbg.rb +127 -0
- data/samples/exeencode.rb +6 -5
- data/samples/factorize-headers-peimports.rb +1 -1
- data/samples/lindebug.rb +186 -391
- data/samples/metasm-shell.rb +68 -57
- 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 +57 -0
- data/tests/graph_layout.rb +285 -0
- data/tests/ia32.rb +80 -26
- data/tests/mcs51.rb +27 -0
- data/tests/mips.rb +10 -3
- data/tests/preprocessor.rb +18 -0
- data/tests/x86_64.rb +66 -18
- metadata +465 -219
- metadata.gz.sig +2 -0
- 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 -872
- 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,70 @@
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
3
|
+
#
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
5
|
+
|
6
|
+
|
7
|
+
# metasm dasm plugin: retrieve a section section, and disassemble everything it can, skipping existing code and nops
|
8
|
+
# usage: load the plugin, then call (ruby snipped): dasm.dasm_all_section '.text'
|
9
|
+
def dasm_all(addrstart, length, method=:disassemble_fast_deep)
|
10
|
+
s = get_section_at(addrstart)
|
11
|
+
return if not s
|
12
|
+
s = s[0]
|
13
|
+
boff = s.ptr
|
14
|
+
off = 0
|
15
|
+
while off < length
|
16
|
+
if di = di_at(addrstart + off)
|
17
|
+
off += di.bin_length
|
18
|
+
elsif @decoded[addrstart+off]
|
19
|
+
off += 1
|
20
|
+
else
|
21
|
+
s.ptr = boff+off
|
22
|
+
maydi = cpu.decode_instruction(s, 0)
|
23
|
+
if not maydi
|
24
|
+
off += 1
|
25
|
+
elsif maydi.instruction.to_s =~ /nop|lea (.*), \[\1(?:\+0)?\]|mov (.*), \2|int 3/
|
26
|
+
off += maydi.bin_length
|
27
|
+
else
|
28
|
+
puts "dasm_all: found #{Expression[addrstart+off]}" if $VERBOSE
|
29
|
+
send(method, addrstart+off)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
Gui.main_iter if gui and off & 15 == 0
|
33
|
+
end
|
34
|
+
|
35
|
+
count = 0
|
36
|
+
off = 0
|
37
|
+
while off < length
|
38
|
+
addr = addrstart+off
|
39
|
+
if di = di_at(addr)
|
40
|
+
if di.block_head?
|
41
|
+
b = di.block
|
42
|
+
if not @function[addr] and b.from_subfuncret.to_a.empty? and b.from_normal.to_a.empty?
|
43
|
+
l = auto_label_at(addr, 'sub_orph')
|
44
|
+
puts "dasm_all: found orphan function #{l}"
|
45
|
+
@function[addrstart+off] = DecodedFunction.new
|
46
|
+
@function[addrstart+off].finalized = true
|
47
|
+
detect_function_thunk(addr)
|
48
|
+
count += 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
off += di.bin_length
|
52
|
+
else
|
53
|
+
off += 1
|
54
|
+
end
|
55
|
+
Gui.main_iter if gui and off & 15 == 0
|
56
|
+
end
|
57
|
+
|
58
|
+
puts "found #{count} orphan functions" if $VERBOSE
|
59
|
+
|
60
|
+
gui.gui_update if gui
|
61
|
+
end
|
62
|
+
|
63
|
+
def dasm_all_section(name, method=:disassemble_fast_deep)
|
64
|
+
section_info.each { |n, a, l, i|
|
65
|
+
if name == n
|
66
|
+
dasm_all(Expression[a].reduce, l, method)
|
67
|
+
end
|
68
|
+
}
|
69
|
+
true
|
70
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
3
|
+
#
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
5
|
+
|
6
|
+
|
7
|
+
# metasm dasm plugin: try to demangle all labels as c++ names, add them as
|
8
|
+
# comment if successful
|
9
|
+
|
10
|
+
def demangle_all_cppnames
|
11
|
+
cnt = 0
|
12
|
+
prog_binding.each { |name, addr|
|
13
|
+
cname = name.sub(/^thunk_/, '')
|
14
|
+
if dname = demangle_cppname(cname)
|
15
|
+
cnt += 1
|
16
|
+
add_comment(addr, dname)
|
17
|
+
each_xref(addr, :x) { |xr|
|
18
|
+
if di = di_at(xr.origin)
|
19
|
+
di.add_comment dname
|
20
|
+
di.comment.delete "x:#{name}"
|
21
|
+
end
|
22
|
+
}
|
23
|
+
end
|
24
|
+
}
|
25
|
+
cnt
|
26
|
+
end
|
27
|
+
|
28
|
+
if gui
|
29
|
+
demangle_all_cppnames
|
30
|
+
gui.gui_update
|
31
|
+
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
3
|
+
#
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
5
|
+
|
6
|
+
|
7
|
+
#
|
8
|
+
# To use your own patterns, create a script that defines Deobfuscate::Patterns, then eval() this file.
|
9
|
+
# Use your script as argument to --plugin
|
10
|
+
#
|
11
|
+
# This script is to be used with the --plugin option of samples/disassemble(-gtk).rb
|
12
|
+
# It holds methods to ease the definition of instruction patterns that are to be replaced
|
13
|
+
# by another arbitrary instruction sequence, using mostly a regexp syntax
|
14
|
+
#
|
15
|
+
# The pattern search&replace is done every time the disassembler
|
16
|
+
# finds a new instruction, through the callback_newinstr callback.
|
17
|
+
#
|
18
|
+
# The patterns can use shortcuts for frequently-used regexps (like 'any machine registers'),
|
19
|
+
# defined in the PatternMacros hash.
|
20
|
+
#
|
21
|
+
# The patterns are matched first against the sequence of instruction opcode names, then
|
22
|
+
# each instruction is rendered as text (using Instruction#to_s), and the global regexp
|
23
|
+
# is checked.
|
24
|
+
# Backreferences can be used in the substitution instruction sequence, through the %1 ... %9
|
25
|
+
# special values.
|
26
|
+
#
|
27
|
+
# A pattern consists of a sequence of regexp for instructions, separated by ' ; '
|
28
|
+
# Each subregexps should not match multiple instructions (ie a patterns matches a fixed-length
|
29
|
+
# instruction sequence, whose length equals the number of ' ; '-separated regexps)
|
30
|
+
# The first word of each regexp should match only the instruction opcode name.
|
31
|
+
#
|
32
|
+
# The substitution may be a Proc, which will receive |dasm object, matched decodedinstr list| as
|
33
|
+
# arguments, and should return:
|
34
|
+
# a String, holding a sequence of instructions separated by ' ; ', which will be parsed by the CPU (no labels allowed)
|
35
|
+
# nil if the pattern did not match, continue searching
|
36
|
+
# an Array of Instruction/DecodedInstruction. If the array is the original di list, same as returning nil
|
37
|
+
#
|
38
|
+
# If the substitution array is different from the matched sequence, the new instructions are passed
|
39
|
+
# to dasm.replace_instrs, which will patch the disassembler decoded instruction graph ; and each
|
40
|
+
# new instruction is passed through the callback once more, allowing for recursive patterns.
|
41
|
+
#
|
42
|
+
|
43
|
+
module Deobfuscate
|
44
|
+
|
45
|
+
|
46
|
+
# special constructs : %i => an integer (immediate/standard label)
|
47
|
+
# %r => standard x86 register (except esp), all sizes
|
48
|
+
# %m => modr/m 32 (memory indirection or reg)
|
49
|
+
PatternMacros = {
|
50
|
+
'%i' => '(?:-|loc_|sub_|xref_)?[0-9][0-9a-fA-F]*h?',
|
51
|
+
'%r' => '(?:[re]?[abcd]x|[re]?[sd]i|[re]?bp|[abcd][lh])',
|
52
|
+
'%m' => '(?:(?:dword ptr )?\[.*?\]|eax|ebx|ecx|edx|edi|esi|ebp)',
|
53
|
+
} if not defined? PatternMacros
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
# instructions are separated by ' ; '
|
59
|
+
# instruction must be '<simple regexp matching opcode> <arbitrary regexp>'
|
60
|
+
# in the pattern target, %1-%9 are used for backreferences from the regexp match
|
61
|
+
Patterns = {
|
62
|
+
'nop ; (.*)' => '%1', # concat 'nop' into following instruction
|
63
|
+
'mov (%r|esp), \1' => 'nop',
|
64
|
+
'lea (%r|esp), (?:dword ptr )?\[\1(?:\+0)?\]' => 'nop',
|
65
|
+
'(.*)' => lambda { |dasm, list| # remove 'jmp imm' preceding us without interfering with running dasm
|
66
|
+
if pdi = prev_di(dasm, list.last) and pdi.opcode.name == 'jmp' and
|
67
|
+
pdi.instruction.args[0].kind_of? Metasm::Expression
|
68
|
+
dasm.replace_instrs(pdi.address, pdi.address, [])
|
69
|
+
end
|
70
|
+
nil
|
71
|
+
},
|
72
|
+
#'call %i ; pop (%r)' => lambda { |dasm, list| "mov %1, #{list.first.next_addr}" },
|
73
|
+
} if not defined? Patterns
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
# returns an array of strings matching the regexp (only |,?,[], non-nested allowed, no special chars)
|
79
|
+
# expand_regexp['a[bcd]?(ef|gh)'] => [abef acef adef aef abgh acgh adgh agh]
|
80
|
+
def self.expand_regexp(str)
|
81
|
+
case str
|
82
|
+
when nil, '', '.*'; return [str.to_s]
|
83
|
+
when /^\\\./
|
84
|
+
l1, p2 = ['.'], $'
|
85
|
+
when /^(\w+)(\?)?/
|
86
|
+
s1, q, p2 = $1, $2, $'
|
87
|
+
l1 = (q ? [s1, s1.chop] : [s1])
|
88
|
+
when /^\[(.*?)\](\?)?/
|
89
|
+
p1, q, p2 = $1, $2, $'
|
90
|
+
l1 = p1.split(//)
|
91
|
+
l1 << '' if q
|
92
|
+
when /^\((?:\?:)?(.*?)\)(\?)?/
|
93
|
+
p1, q, p2 = $1, $2, $'
|
94
|
+
l1 = p1.split('|').map { |p| expand_regexp(p) }.flatten
|
95
|
+
l1 << '' if q
|
96
|
+
else raise "bad pattern #{str.inspect}"
|
97
|
+
end
|
98
|
+
expand_regexp(p2).map { |s2| l1.map { |s1_| s1_ + s2 } }.flatten.uniq
|
99
|
+
end
|
100
|
+
|
101
|
+
# find the instr preceding adi ; follows from_normal if it is a single element array
|
102
|
+
def self.prev_di(dasm, di)
|
103
|
+
if di.block.list.first != di
|
104
|
+
di.block.list[di.block.list.index(di)-1]
|
105
|
+
elsif di.block.from_normal.to_a.length == 1
|
106
|
+
dasm.decoded[di.block.from_normal.first]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# preprocess the pattern list to optimize matching on each new instruction
|
111
|
+
# last pattern instr opname => prev instr opname => prev instr opname => :pattern => [patterns]
|
112
|
+
def self.generate_precalc(next_hash, next_ops, pattern)
|
113
|
+
if next_ops.empty?
|
114
|
+
next_hash[:pattern] ||= []
|
115
|
+
next_hash[:pattern] << pattern
|
116
|
+
else
|
117
|
+
(expand_regexp(next_ops[-1]) rescue ['.*']).each { |op|
|
118
|
+
nh = next_hash[op] ||= {}
|
119
|
+
generate_precalc(nh, next_ops[0...-1], pattern)
|
120
|
+
}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
PrecalcPatterns = {} if not defined? PrecalcPatterns
|
125
|
+
|
126
|
+
# replace Macros in patterns, do some precalc to speedup pattern matching
|
127
|
+
def self.init
|
128
|
+
PrecalcPatterns.clear
|
129
|
+
Patterns.keys.each { |pat|
|
130
|
+
# replace PatternMacros in patterns
|
131
|
+
newp = pat.dup
|
132
|
+
PatternMacros.each { |mk, mv| newp.gsub!(mk, mv) }
|
133
|
+
Patterns[newp] = Patterns.delete(pat) if pat != newp
|
134
|
+
pat = newp
|
135
|
+
|
136
|
+
# TODO handle instructions with prefix (lock/rep), conditional regexp over multiple instructions..
|
137
|
+
ops = pat.split(' ; ').map { |instr| instr[/^\S+/] }
|
138
|
+
|
139
|
+
generate_precalc(PrecalcPatterns, ops, pat)
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
# the actual disassembler callback
|
144
|
+
# checks the current instruction opname against the end of patterns using precomputed tree, then check previous instr etc
|
145
|
+
# once full pattern may match, convert each instr to string, and run the regexp match
|
146
|
+
# on match, reuse the captures in the pattern target, parse the target, generate decoded instrs, and replace in the dasm graph.
|
147
|
+
# on match, rerun the callback on each replaced instruction (for recursive patterns)
|
148
|
+
def self.newinstr_callback(dasm, di)
|
149
|
+
# compute the merged subtree of t1 and t2
|
150
|
+
# merges patterns if found
|
151
|
+
mergetree = lambda { |t1, t2|
|
152
|
+
if t1 and t2
|
153
|
+
case t1
|
154
|
+
when Array; t1 + t2
|
155
|
+
when Hash; (t1.keys | t2.keys).inject({}) { |t, k| t.update k => mergetree[t1[k], t2[k]] }
|
156
|
+
end
|
157
|
+
else t1 || t2
|
158
|
+
end
|
159
|
+
}
|
160
|
+
|
161
|
+
di_seq = [di]
|
162
|
+
lastdi = di
|
163
|
+
tree = PrecalcPatterns
|
164
|
+
tree = mergetree[tree['.*'], tree[lastdi.instruction.opname]]
|
165
|
+
newinstrs = match = nil
|
166
|
+
# walk the Precalc tree
|
167
|
+
while tree
|
168
|
+
if tree[:pattern]
|
169
|
+
strs = di_seq.map { |pdi| pdi.instruction.to_s }
|
170
|
+
break if tree[:pattern].find { |pat|
|
171
|
+
if match = /^#{pat}$/.match(strs.join(' ; '))
|
172
|
+
newinstrs = Patterns[pat]
|
173
|
+
newinstrs = newinstrs[dasm, di_seq] if newinstrs.kind_of? Proc
|
174
|
+
newinstrs = nil if newinstrs == di_seq
|
175
|
+
else newinstrs = nil
|
176
|
+
end
|
177
|
+
newinstrs
|
178
|
+
} or tree.length == 1
|
179
|
+
end
|
180
|
+
|
181
|
+
if lastdi = prev_di(dasm, lastdi)
|
182
|
+
di_seq.unshift lastdi
|
183
|
+
tree = mergetree[tree['.*'], tree[lastdi.instruction.opname]]
|
184
|
+
else break
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# match found : create instruction stream, replace in dasm, recurse
|
189
|
+
if newinstrs
|
190
|
+
# replace %1-%9 by the matched substrings
|
191
|
+
newinstrs = newinstrs.gsub(/%(\d)/) { match.captures[$1.to_i-1] }.split(' ; ').map { |str| dasm.cpu.parse_instruction(str) } if newinstrs.kind_of? String
|
192
|
+
if newinstrs.last.kind_of? Metasm::Instruction and newinstrs.last.opname != 'jmp' and
|
193
|
+
lastdi.address + di_seq.inject(-di.bin_length) { |len, i| len + i.bin_length } != di.address
|
194
|
+
# ensure that the last instr ends the same place as the original last instr (to allow disassemble_block to continue)
|
195
|
+
newinstrs << dasm.cpu.parse_instruction("jmp #{Metasm::Expression[di.next_addr]}")
|
196
|
+
# nop ; jmp => jmp
|
197
|
+
newinstrs.shift if newinstrs.length >= 2 and newinstrs.first.kind_of? Metasm::Instruction and newinstrs.first.opname == 'nop'
|
198
|
+
end
|
199
|
+
|
200
|
+
# remove instructions from the match to have only 2 linked blocks passed to replace_instrs
|
201
|
+
unused = di_seq[1..-2] || []
|
202
|
+
unused.delete_if { |udi| udi.block.address == di_seq[0].block.address or udi.block.address == di_seq[-1].block.address }
|
203
|
+
dasm.replace_instrs(unused.shift.address, unused.shift.address, []) while unused.length > 1
|
204
|
+
dasm.replace_instrs(unused.first.address, unused.first.address, []) if not unused.empty?
|
205
|
+
|
206
|
+
# patch the dasm graph
|
207
|
+
if dasm.replace_instrs(lastdi.address, di.address, newinstrs, true)
|
208
|
+
puts ' deobfuscate', di_seq, ' into', newinstrs, ' ---' if $DEBUG
|
209
|
+
# recurse, keep the last generated di to return to caller as replacement
|
210
|
+
newinstrs.each { |bdi| di = newinstr_callback(dasm, bdi) || di }
|
211
|
+
else
|
212
|
+
di = nil
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
di
|
217
|
+
end
|
218
|
+
|
219
|
+
# call newinstr_callback on all existing instructions of dasm
|
220
|
+
def self.deobfuscate_existing(dasm)
|
221
|
+
dasm.each_instructionblock { |b|
|
222
|
+
b.list.dup.each { |di| newinstr_callback(dasm, di) }
|
223
|
+
}
|
224
|
+
end
|
225
|
+
|
226
|
+
# calls dasm.merge_blocks(true) on all instruction blocks to merge sequences of blocks
|
227
|
+
def self.merge_blocks(dasm)
|
228
|
+
dasm.each_instructionblock { |b|
|
229
|
+
if pv = dasm.di_at(b.from_normal.to_a.first) and not pv.block.list.last.opcode.props[:setip] and
|
230
|
+
b.from_normal.length == 1 and pv.block.to_normal.to_a.length == 1
|
231
|
+
dasm.merge_blocks(pv.block, b, true)
|
232
|
+
end
|
233
|
+
}
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
if $DEBUG
|
238
|
+
# update DecodedInstr.to_s to include instr length
|
239
|
+
class Metasm::DecodedInstruction
|
240
|
+
def to_s ; "#{Metasm::Expression[address] if address} +#{bin_length} #{instruction}" end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# do the pattern precalc
|
245
|
+
Deobfuscate.init
|
246
|
+
|
247
|
+
if self.kind_of? Metasm::Disassembler
|
248
|
+
dasm = self
|
249
|
+
# setup the newinstr callback
|
250
|
+
dasm.callback_newinstr = lambda { |di| Deobfuscate.newinstr_callback(dasm, di) }
|
251
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
3
|
+
#
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
5
|
+
|
6
|
+
|
7
|
+
# metasm dasm GUI plugin: dumps the text of the current widget to a text file on 'D' keypress
|
8
|
+
# the dump is appended to the file if it exists
|
9
|
+
# works on the listing view (current screen),
|
10
|
+
# on the decompiled view (current function),
|
11
|
+
# on the graph view (selected blocks, in selection order)
|
12
|
+
|
13
|
+
if gui
|
14
|
+
gui.keyboard_callback[?D] = lambda { |a|
|
15
|
+
cv = gui.curview
|
16
|
+
if t = cv.instance_variable_get('@line_text')
|
17
|
+
gui.savefile('dump file') { |f|
|
18
|
+
File.open(f, 'a') { |fd| fd.puts t }
|
19
|
+
}
|
20
|
+
elsif s = cv.instance_variable_get('@selected_boxes')
|
21
|
+
if s.empty?
|
22
|
+
gui.messagebox('select boxes (ctrl+click)')
|
23
|
+
next
|
24
|
+
end
|
25
|
+
gui.savefile('dump file') { |f|
|
26
|
+
File.open(f, 'a') { |fd|
|
27
|
+
s.each { |box|
|
28
|
+
fd.puts box[:line_text_col].map { |strc| strc.transpose[0].join }
|
29
|
+
}
|
30
|
+
fd.puts
|
31
|
+
}
|
32
|
+
}
|
33
|
+
end
|
34
|
+
}
|
35
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
3
|
+
#
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
5
|
+
|
6
|
+
|
7
|
+
# metasm dasm plugin: create a function to export the currently displayed
|
8
|
+
# dasm graph to a .svg file
|
9
|
+
# in the gui, type E to export. Tested only for graph-style view, *may* work in other cases
|
10
|
+
|
11
|
+
raise 'gui only' if not gui
|
12
|
+
|
13
|
+
def graph_to_svg
|
14
|
+
gw = gui.curview.dup
|
15
|
+
class << gw
|
16
|
+
attr_accessor :svgbuf, :svgcol
|
17
|
+
def draw_color(col)
|
18
|
+
col = @default_color_association.fetch(col, col)
|
19
|
+
col = BasicColor.fetch(col, col)
|
20
|
+
@svgcol = "##{col}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def draw_line(x1, y1, x2, y2)
|
24
|
+
bb(x1, y1, x2, y2)
|
25
|
+
svgbuf << %Q{<line x1="#{x1}" y1="#{y1}" x2="#{x2}" y2="#{y2}" stroke="#{@svgcol}" />\n}
|
26
|
+
end
|
27
|
+
def draw_rectangle(x, y, w, h)
|
28
|
+
bb(x, y, x+w, y+h)
|
29
|
+
svgbuf << %Q{<rect x="#{x}" y="#{y}" width="#{w}" height="#{h}" fill="#{@svgcol}" />\n}
|
30
|
+
end
|
31
|
+
def draw_string(x, y, str)
|
32
|
+
bb(x, y, x+str.length*@font_width, y+@font_height)
|
33
|
+
stre = str.gsub('<', '<').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
|
+
}
|