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,214 @@
|
|
|
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 debugger plugin
|
|
8
|
+
# adds a 'trace_func' method to the debugger
|
|
9
|
+
# the methods sets a breakpoint at the beginning of a function, and logs the execution of the instruction blocks
|
|
10
|
+
# does not descend in subfunctions
|
|
11
|
+
|
|
12
|
+
# setup the initial breakpoint at func start
|
|
13
|
+
def trace_func(addr, oneshot = false)
|
|
14
|
+
@trace_terminate = false
|
|
15
|
+
# distinguish different hits on the same function entry
|
|
16
|
+
counter = 0
|
|
17
|
+
bp = bpx(addr, oneshot) {
|
|
18
|
+
counter += 1
|
|
19
|
+
id = [disassembler.normalize(addr), counter, func_retaddr]
|
|
20
|
+
trace_func_newtrace(id)
|
|
21
|
+
trace_func_block(id)
|
|
22
|
+
continue
|
|
23
|
+
}
|
|
24
|
+
if addr == pc
|
|
25
|
+
del_bp bp if oneshot
|
|
26
|
+
bp.action.call
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# start tracing now, and stop only when @trace_terminate is set
|
|
31
|
+
def trace
|
|
32
|
+
@trace_subfuncs = true
|
|
33
|
+
@trace_terminate = false
|
|
34
|
+
id = [pc, 0, 0]
|
|
35
|
+
trace_func_newtrace(id)
|
|
36
|
+
trace_func_block(id)
|
|
37
|
+
continue
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# we hit the beginning of a block we want to trace
|
|
41
|
+
def trace_func_block(id)
|
|
42
|
+
blockaddr = pc
|
|
43
|
+
if b = trace_get_block(blockaddr)
|
|
44
|
+
trace_func_add_block(id, blockaddr)
|
|
45
|
+
if b.list.length == 1
|
|
46
|
+
trace_func_blockend(id, blockaddr)
|
|
47
|
+
else
|
|
48
|
+
bpx(b.list.last.address, true) {
|
|
49
|
+
finished = trace_func_blockend(id, blockaddr)
|
|
50
|
+
continue if not finished
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
else
|
|
54
|
+
# invalid opcode ?
|
|
55
|
+
trace_func_blockend(id, blockaddr)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# we are at the end of a traced block, find whats next
|
|
60
|
+
def trace_func_blockend(id, blockaddr)
|
|
61
|
+
if di = disassembler.di_at(pc)
|
|
62
|
+
if end_stepout(di) and trace_func_istraceend(id, di)
|
|
63
|
+
# trace ends there
|
|
64
|
+
trace_func_finish(id)
|
|
65
|
+
return true
|
|
66
|
+
elsif di.opcode.props[:saveip] and not trace_func_entersubfunc(id, di)
|
|
67
|
+
# call to a subfunction
|
|
68
|
+
bpx(di.next_addr, true) {
|
|
69
|
+
trace_func_block(id)
|
|
70
|
+
continue
|
|
71
|
+
}
|
|
72
|
+
continue
|
|
73
|
+
else
|
|
74
|
+
singlestep {
|
|
75
|
+
newaddr = pc
|
|
76
|
+
trace_func_block(id)
|
|
77
|
+
|
|
78
|
+
trace_func_linkdasm(di.address, newaddr)
|
|
79
|
+
continue
|
|
80
|
+
}
|
|
81
|
+
end
|
|
82
|
+
else
|
|
83
|
+
# XXX should link in the dasm somehow
|
|
84
|
+
singlestep {
|
|
85
|
+
trace_func_block(id)
|
|
86
|
+
continue
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
false
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# retrieve an instructionblock, disassemble if needed
|
|
93
|
+
def trace_get_block(addr)
|
|
94
|
+
# TODO trace all blocks from addr for which we know the target, stop on call / jmp [foo]
|
|
95
|
+
disassembler.disassemble_fast_block(addr)
|
|
96
|
+
if di = disassembler.di_at(addr)
|
|
97
|
+
di.block
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# update the blocks links in the disassembler
|
|
102
|
+
def trace_func_linkdasm(from_addr, new_addr)
|
|
103
|
+
di = disassembler.di_at(from_addr)
|
|
104
|
+
ndi = disassembler.di_at(new_addr)
|
|
105
|
+
|
|
106
|
+
return if not di
|
|
107
|
+
|
|
108
|
+
# is it a subfunction return ?
|
|
109
|
+
if end_stepout(di) and cdi = (1..8).map { |i|
|
|
110
|
+
disassembler.di_at(new_addr - i)
|
|
111
|
+
}.compact.find { |cdi_|
|
|
112
|
+
cdi_.opcode.props[:saveip] and cdi_.next_addr == new_addr
|
|
113
|
+
}
|
|
114
|
+
cdi.block.add_to_subfuncret new_addr
|
|
115
|
+
ndi.block.add_from_subfuncret cdi.address if ndi
|
|
116
|
+
cdi.block.each_to_normal { |f|
|
|
117
|
+
disassembler.function[f] ||= DecodedFunction.new if disassembler.di_at(f)
|
|
118
|
+
}
|
|
119
|
+
else
|
|
120
|
+
di.block.add_to_normal new_addr
|
|
121
|
+
ndi.block.add_from_normal from_addr if ndi
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
################################################################################################
|
|
126
|
+
# you can redefine the following functions in another plugin to handle trace events differently
|
|
127
|
+
|
|
128
|
+
# a new trace is about to begin
|
|
129
|
+
def trace_func_newtrace(id)
|
|
130
|
+
@trace_func_counter ||= {}
|
|
131
|
+
@trace_func_counter[id] = 0
|
|
132
|
+
|
|
133
|
+
puts "start tracing #{Expression[id[0]]}"
|
|
134
|
+
|
|
135
|
+
# setup a bg_color_callback on the disassembler
|
|
136
|
+
if not defined? @trace_func_dasmcolor
|
|
137
|
+
@trace_func_dasmcolor = true
|
|
138
|
+
return if not disassembler.gui
|
|
139
|
+
oldcb = disassembler.gui.bg_color_callback
|
|
140
|
+
disassembler.gui.bg_color_callback = lambda { |addr|
|
|
141
|
+
if oldcb and c = oldcb[addr]
|
|
142
|
+
c
|
|
143
|
+
elsif di = disassembler.di_at(addr) and di.block.list.first.comment.to_s =~ /functrace/
|
|
144
|
+
'ff0'
|
|
145
|
+
end
|
|
146
|
+
}
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# a new block is added to a trace
|
|
151
|
+
def trace_func_add_block(id, blockaddr)
|
|
152
|
+
@trace_func_counter[id] += 1
|
|
153
|
+
if di = disassembler.di_at(blockaddr)
|
|
154
|
+
di.add_comment "functrace #{@trace_func_counter[id]}"
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# the trace is finished
|
|
159
|
+
def trace_func_finish(id)
|
|
160
|
+
puts "finished tracing #{Expression[id[0]]}"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def trace_subfuncs=(v) @trace_subfuncs = v end
|
|
164
|
+
def trace_subfuncs; @trace_subfuncs ||= false end
|
|
165
|
+
|
|
166
|
+
# the tracer is on a end-of-func instruction, should the trace end ?
|
|
167
|
+
def trace_func_istraceend(id, di)
|
|
168
|
+
if trace_subfuncs
|
|
169
|
+
if @trace_terminate
|
|
170
|
+
true
|
|
171
|
+
elsif id[2] == 0 # trace VS func_trace
|
|
172
|
+
elsif target = disassembler.get_xrefs_x(di)[0]
|
|
173
|
+
# check the current return address against the one saved at trace start
|
|
174
|
+
resolve(disassembler.normalize(target)) == id[2]
|
|
175
|
+
end
|
|
176
|
+
else
|
|
177
|
+
true
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# the tracer is on a subfunction call instruction, should it trace into or stepover ?
|
|
182
|
+
def trace_func_entersubfunc(id, di)
|
|
183
|
+
if trace_subfuncs
|
|
184
|
+
@trace_func_subfunccache ||= {}
|
|
185
|
+
if not target = @trace_func_subfunccache[di.address]
|
|
186
|
+
# even if the target is dynamic, its module should be static
|
|
187
|
+
if target = disassembler.get_xrefs_x(di)[0]
|
|
188
|
+
@trace_func_subfunccache[di.address] =
|
|
189
|
+
target = resolve(disassembler.normalize(target))
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
# check if the target subfunction is in the same module as the main
|
|
193
|
+
# XXX should check against the list of loaded modules etc
|
|
194
|
+
# XXX call thunk_foo -> jmp [other_module]
|
|
195
|
+
true if target.kind_of? Integer and target & 0xffc0_0000 == id[0] & 0xffc0_0000
|
|
196
|
+
else
|
|
197
|
+
false
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
if gui
|
|
202
|
+
gui.new_command('trace_func', 'trace execution inside a target function') { |arg| trace_func arg }
|
|
203
|
+
gui.new_command('trace_func_once', 'trace one execution inside the target function') { |arg| trace_func arg, true }
|
|
204
|
+
gui.new_command('trace_now', 'trace til the end of the current function') { trace_func pc, true ; gui.wrap_run { continue } }
|
|
205
|
+
gui.new_command('trace', 'start tracing from the current pc until trace_stop') { trace }
|
|
206
|
+
gui.new_command('trace_stop', 'stop tracing') { @trace_terminate = true }
|
|
207
|
+
gui.new_command('trace_subfunctions', 'define if the tracer should enter subfunctions') { |arg|
|
|
208
|
+
case arg.strip
|
|
209
|
+
when 'on', '1', 'yes', 'y'; @trace_subfuncs = true
|
|
210
|
+
else @trace_subfuncs = false
|
|
211
|
+
end
|
|
212
|
+
puts "#{'not ' if not @trace_subfuncs}tracing subfunctions"
|
|
213
|
+
}
|
|
214
|
+
end
|
data/samples/disassemble-gui.rb
CHANGED
|
@@ -40,7 +40,7 @@ OptionParser.new { |opt|
|
|
|
40
40
|
opt.on('--map <mapfile>', 'load a map file (addr <-> name association)') { |f| opts[:map] = f }
|
|
41
41
|
opt.on('--fast', 'dasm cli args with disassemble_fast_deep') { opts[:fast] = true }
|
|
42
42
|
opt.on('--decompile') { opts[:decompile] = true }
|
|
43
|
-
opt.on('--gui <gtk|win32|qt>') { |g|
|
|
43
|
+
opt.on('--gui <gtk|win32|qt>') { |g| ENV['METASM_GUI'] = g }
|
|
44
44
|
opt.on('--cpu <cpu>', 'the CPU class to use for a shellcode (Ia32, X64, ...)') { |c| opts[:sc_cpu] = c }
|
|
45
45
|
opt.on('--exe <exe_fmt>', 'the executable file format to use (PE, ELF, ...)') { |c| opts[:exe_fmt] = c }
|
|
46
46
|
opt.on('--rebase <addr>', 'rebase the loaded file to <addr>') { |a| opts[:rebase] = Integer(a) }
|
|
@@ -49,6 +49,8 @@ OptionParser.new { |opt|
|
|
|
49
49
|
opt.on('-v', '--verbose') { $VERBOSE = true } # default
|
|
50
50
|
opt.on('-q', '--no-verbose') { $VERBOSE = false }
|
|
51
51
|
opt.on('-d', '--debug') { $DEBUG = $VERBOSE = true }
|
|
52
|
+
opt.on('-S <file>', '--session <sessionfile>', 'save user actions in this session file') { |a| opts[:session] = a }
|
|
53
|
+
opt.on('-N', '--new-session', 'start new session, discard old one') { opts[:newsession] = true }
|
|
52
54
|
}.parse!(ARGV)
|
|
53
55
|
|
|
54
56
|
case exename = ARGV.shift
|
|
@@ -66,6 +68,8 @@ when /^(tcp:|udp:)?..+:/
|
|
|
66
68
|
else
|
|
67
69
|
w = Metasm::Gui::DasmWindow.new("#{exename + ' - ' if exename}metasm disassembler")
|
|
68
70
|
if exename
|
|
71
|
+
opts[:sc_cpu] = eval(opts[:sc_cpu]) if opts[:sc_cpu] =~ /[.(\s:]/
|
|
72
|
+
opts[:exe_fmt] = eval(opts[:exe_fmt]) if opts[:exe_fmt] =~ /[.(\s:]/
|
|
69
73
|
exe = w.loadfile(exename, opts[:sc_cpu], opts[:exe_fmt])
|
|
70
74
|
exe.disassembler.rebase(opts[:rebase]) if opts[:rebase]
|
|
71
75
|
if opts[:autoload]
|
|
@@ -73,6 +77,7 @@ else
|
|
|
73
77
|
opts[:map] ||= basename + '.map' if File.exist?(basename + '.map')
|
|
74
78
|
opts[:cheader] ||= basename + '.h' if File.exist?(basename + '.h')
|
|
75
79
|
(opts[:plugin] ||= []) << (basename + '.rb') if File.exist?(basename + '.rb')
|
|
80
|
+
opts[:session] ||= basename + '.metasm-session'
|
|
76
81
|
end
|
|
77
82
|
end
|
|
78
83
|
end
|
|
@@ -80,21 +85,46 @@ end
|
|
|
80
85
|
ep = ARGV.map { |arg| (?0..?9).include?(arg[0]) ? Integer(arg) : arg }
|
|
81
86
|
|
|
82
87
|
if exe
|
|
83
|
-
dasm = exe.
|
|
88
|
+
dasm = exe.disassembler
|
|
84
89
|
|
|
85
90
|
dasm.load_map opts[:map] if opts[:map]
|
|
86
91
|
dasm.parse_c_file opts[:cheader] if opts[:cheader]
|
|
87
92
|
dasm.backtrace_maxblocks_data = -1 if opts[:nodatatrace]
|
|
88
93
|
dasm.debug_backtrace = true if opts[:debugbacktrace]
|
|
89
|
-
dasm.disassemble_fast_deep(*ep) if opts[:fast]
|
|
90
94
|
dasm.callback_finished = lambda { w.dasm_widget.focus_addr w.dasm_widget.curaddr, :decompile ; dasm.decompiler.finalize } if opts[:decompile]
|
|
95
|
+
dasm.disassemble_fast_deep(*ep) if opts[:fast]
|
|
91
96
|
elsif dbg
|
|
92
97
|
dbg.load_map opts[:map] if opts[:map]
|
|
93
|
-
opts[:
|
|
98
|
+
dbg.disassembler.parse_c_file opts[:cheader] if opts[:cheader]
|
|
99
|
+
opts[:plugin].to_a.each { |p|
|
|
100
|
+
begin
|
|
101
|
+
dbg.load_plugin(p)
|
|
102
|
+
rescue ::Exception
|
|
103
|
+
puts "Error with plugin #{p}: #{$!.class} #{$!}"
|
|
104
|
+
end
|
|
105
|
+
}
|
|
94
106
|
end
|
|
95
107
|
if dasm
|
|
96
108
|
w.display(dasm, ep)
|
|
97
|
-
opts[:plugin].to_a.each { |p|
|
|
109
|
+
opts[:plugin].to_a.each { |p|
|
|
110
|
+
begin
|
|
111
|
+
dasm.load_plugin(p)
|
|
112
|
+
rescue ::Exception
|
|
113
|
+
puts "Error with plugin #{p}: #{$!.class} #{$!}"
|
|
114
|
+
end
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if opts[:session]
|
|
118
|
+
if File.exist?(opts[:session])
|
|
119
|
+
if opts[:newsession]
|
|
120
|
+
File.unlink(opts[:session])
|
|
121
|
+
else
|
|
122
|
+
puts "replaying session #{opts[:session]}"
|
|
123
|
+
w.widget.replay_session(opts[:session])
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
w.widget.save_session opts[:session]
|
|
127
|
+
end
|
|
98
128
|
end
|
|
99
129
|
|
|
100
130
|
opts[:hookstr].to_a.each { |f| eval f }
|
data/samples/disassemble.rb
CHANGED
|
@@ -48,10 +48,23 @@ t0 = Time.now if opts[:benchmark]
|
|
|
48
48
|
if exename =~ /^live:(.*)/
|
|
49
49
|
raise 'no such live target' if not target = OS.current.find_process($1)
|
|
50
50
|
p target if $VERBOSE
|
|
51
|
-
|
|
51
|
+
opts[:sc_cpu] = eval(opts[:sc_cpu]) if opts[:sc_cpu] =~ /[.(\s:]/
|
|
52
|
+
opts[:sc_cpu] = Metasm.const_get(opts[:sc_cpu]) if opts[:sc_cpu].kind_of(::String)
|
|
53
|
+
opts[:sc_cpu] = opts[:sc_cpu].new if opts[:sc_cpu].kind_of?(::Class)
|
|
54
|
+
exe = Shellcode.decode(target.memory, opts[:sc_cpu])
|
|
52
55
|
else
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
opts[:sc_cpu] = eval(opts[:sc_cpu]) if opts[:sc_cpu] =~ /[.(\s:]/
|
|
57
|
+
opts[:exe_fmt] = eval(opts[:exe_fmt]) if opts[:exe_fmt] =~ /[.(\s:]/
|
|
58
|
+
if opts[:exe_fmt].kind_of?(::String)
|
|
59
|
+
exefmt = opts[:exe_fmt] = Metasm.const_get(opts[:exe_fmt])
|
|
60
|
+
else
|
|
61
|
+
exefmt = opts[:exe_fmt] || AutoExe.orshellcode {
|
|
62
|
+
opts[:sc_cpu] = Metasm.const_get(opts[:sc_cpu]) if opts[:sc_cpu].kind_of?(::String)
|
|
63
|
+
opts[:sc_cpu] = opts[:sc_cpu].new if opts[:sc_cpu].kind_of?(::Class)
|
|
64
|
+
opts[:sc_cpu]
|
|
65
|
+
}
|
|
66
|
+
end
|
|
67
|
+
exefmt = exefmt.withcpu(opts[:sc_cpu]) if exefmt.kind_of?(::Class) and exefmt.name.to_s.split('::').last == 'Shellcode'
|
|
55
68
|
exe = exefmt.decode_file(exename)
|
|
56
69
|
exe.disassembler.rebase(opts[:rebase]) if opts[:rebase]
|
|
57
70
|
if opts[:autoload]
|
|
@@ -62,7 +75,7 @@ else
|
|
|
62
75
|
end
|
|
63
76
|
end
|
|
64
77
|
# set options
|
|
65
|
-
dasm = exe.
|
|
78
|
+
dasm = exe.disassembler
|
|
66
79
|
makeint = lambda { |addr|
|
|
67
80
|
case addr
|
|
68
81
|
when /^[0-9].*h/; addr.to_i(16)
|
|
@@ -75,7 +88,13 @@ dasm.parse_c_file opts[:cheader] if opts[:cheader]
|
|
|
75
88
|
dasm.backtrace_maxblocks_data = -1 if opts[:nodatatrace]
|
|
76
89
|
dasm.debug_backtrace = true if opts[:debugbacktrace]
|
|
77
90
|
opts[:stopaddr].to_a.each { |addr| dasm.decoded[makeint[addr]] = true }
|
|
78
|
-
opts[:plugin].to_a.each { |p|
|
|
91
|
+
opts[:plugin].to_a.each { |p|
|
|
92
|
+
begin
|
|
93
|
+
dasm.load_plugin p
|
|
94
|
+
rescue ::Exception
|
|
95
|
+
puts "Error with plugin #{p}: #{$!.class} #{$!}"
|
|
96
|
+
end
|
|
97
|
+
}
|
|
79
98
|
opts[:hookstr].to_a.each { |f| eval f }
|
|
80
99
|
|
|
81
100
|
t1 = Time.now if opts[:benchmark]
|
|
@@ -98,7 +117,13 @@ if opts[:decompile]
|
|
|
98
117
|
tdc = Time.now if opts[:benchmark]
|
|
99
118
|
end
|
|
100
119
|
|
|
101
|
-
opts[:post_plugin].to_a.each { |p|
|
|
120
|
+
opts[:post_plugin].to_a.each { |p|
|
|
121
|
+
begin
|
|
122
|
+
dasm.load_plugin p
|
|
123
|
+
rescue ::Exception
|
|
124
|
+
puts "Error with plugin #{p}: #{$!.class} #{$!}"
|
|
125
|
+
end
|
|
126
|
+
}
|
|
102
127
|
|
|
103
128
|
dasm.save_file(opts[:savefile]) if opts[:savefile]
|
|
104
129
|
|
data/samples/dump_upx.rb
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
# original entrypoint by disassembling the UPX stub, set breakpoint on it,
|
|
11
11
|
# run the program, and dump the loaded image to an executable PE.
|
|
12
12
|
#
|
|
13
|
-
# usage: dump_upx.rb <packed.exe> [<dumped.exe>] [<
|
|
13
|
+
# usage: dump_upx.rb <packed.exe> [<dumped.exe>] [<iat (r)va>] [--oep <rva>]
|
|
14
14
|
#
|
|
15
15
|
|
|
16
16
|
require 'metasm'
|
|
@@ -21,17 +21,22 @@ class UPXUnpacker
|
|
|
21
21
|
# find the oep by disassembling
|
|
22
22
|
# run it until the oep
|
|
23
23
|
# dump the memory image
|
|
24
|
-
def initialize(file,
|
|
25
|
-
@dumpfile = dumpfile
|
|
26
|
-
@
|
|
24
|
+
def initialize(file, oep, iat, dumpfile)
|
|
25
|
+
@dumpfile = dumpfile
|
|
26
|
+
@dumpfile ||= file.chomp('.exe') + '.dump.exe'
|
|
27
27
|
|
|
28
28
|
puts 'disassembling UPX loader...'
|
|
29
29
|
pe = PE.decode_file(file)
|
|
30
|
-
@oep = find_oep(pe)
|
|
31
|
-
raise 'cant find oep...' if not @oep
|
|
32
|
-
puts "oep found at #{Expression[@oep]}"
|
|
33
30
|
@baseaddr = pe.optheader.image_base
|
|
34
|
-
|
|
31
|
+
|
|
32
|
+
@oep = oep
|
|
33
|
+
@oep -= @baseaddr if @oep and @oep > @baseaddr # va => rva
|
|
34
|
+
@oep ||= find_oep(pe)
|
|
35
|
+
raise 'cant find oep...' if not @oep
|
|
36
|
+
puts "oep found at #{Expression[@oep]}" if not oep
|
|
37
|
+
|
|
38
|
+
@iat = iat
|
|
39
|
+
@iat -= @baseaddr if @iat and @iat > @baseaddr
|
|
35
40
|
|
|
36
41
|
@dbg = OS.current.create_process(file).debugger
|
|
37
42
|
puts 'running...'
|
|
@@ -40,7 +45,7 @@ class UPXUnpacker
|
|
|
40
45
|
|
|
41
46
|
# disassemble the upx stub to find a cross-section jump (to the real entrypoint)
|
|
42
47
|
def find_oep(pe)
|
|
43
|
-
dasm = pe.
|
|
48
|
+
dasm = pe.disassemble_fast_deep 'entrypoint'
|
|
44
49
|
|
|
45
50
|
return if not jmp = dasm.decoded.find { |addr, di|
|
|
46
51
|
# check only once per basic block
|
|
@@ -80,7 +85,8 @@ class UPXUnpacker
|
|
|
80
85
|
dump.sections.each { |s| s.characteristics |= ['MEM_WRITE'] }
|
|
81
86
|
|
|
82
87
|
# write the PE file to disk
|
|
83
|
-
|
|
88
|
+
# as UPX strips the relocation information, mark the exe to opt-out from ASLR
|
|
89
|
+
dump.encode_file @dumpfile, 'exe', false
|
|
84
90
|
|
|
85
91
|
puts 'dump complete'
|
|
86
92
|
ensure
|
|
@@ -90,6 +96,12 @@ class UPXUnpacker
|
|
|
90
96
|
end
|
|
91
97
|
|
|
92
98
|
if __FILE__ == $0
|
|
93
|
-
|
|
94
|
-
|
|
99
|
+
fn = ARGV.shift
|
|
100
|
+
oep = Integer(ARGV.shift) unless ARGV.empty?
|
|
101
|
+
oep = nil if oep == -1
|
|
102
|
+
iat = Integer(ARGV.shift) unless ARGV.empty?
|
|
103
|
+
iat = nil if iat == -1
|
|
104
|
+
out = ARGV.shift
|
|
105
|
+
abort 'usage: dump <exe> [<oep>] [<iat>] [<outfile>]' if not File.exist?(fn)
|
|
106
|
+
UPXUnpacker.new(fn, oep, iat, out)
|
|
95
107
|
end
|
data/samples/dynamic_ruby.rb
CHANGED
|
@@ -289,12 +289,15 @@ EOS
|
|
|
289
289
|
m ? @cp.dump_definition(m) : @cp.to_s
|
|
290
290
|
end
|
|
291
291
|
|
|
292
|
+
@@optim_hint = {}
|
|
293
|
+
def self.optim_hint; @@optim_hint; end
|
|
294
|
+
|
|
292
295
|
attr_accessor :optim_hint
|
|
293
296
|
def initialize(cp=nil)
|
|
294
297
|
@cp = cp || DynLdr.host_cpu.new_cparser
|
|
295
298
|
@cp.parse RUBY_H
|
|
296
299
|
@iter_break = nil
|
|
297
|
-
@optim_hint =
|
|
300
|
+
@optim_hint = @@optim_hint.dup
|
|
298
301
|
end
|
|
299
302
|
|
|
300
303
|
# convert a ruby AST to a new C function
|
|
@@ -1135,10 +1138,10 @@ EOS
|
|
|
1135
1138
|
args = ast[3][1..-1] if ast[3] and ast[3][0] == :array
|
|
1136
1139
|
arg0 = args[0] if args and args[0]
|
|
1137
1140
|
|
|
1138
|
-
if arg0 and arg0[0] == :lit and arg0[1].kind_of?
|
|
1141
|
+
if arg0 and arg0[0] == :lit and arg0[1].kind_of?(Fixnum) and %w[== > < >= <= + -].include?(op)
|
|
1142
|
+
# TODO or @optim_hint[ast[1]] == 'fixnum'
|
|
1139
1143
|
# optimize 'x==42', 'x+42', 'x-42'
|
|
1140
1144
|
o2 = arg0[1]
|
|
1141
|
-
return if not %w[== > < >= <= + -].include? op
|
|
1142
1145
|
if o2 < 0 and ['+', '-'].include? op
|
|
1143
1146
|
# need o2 >= 0 for overflow detection
|
|
1144
1147
|
op = {'+' => '-', '-' => '+'}[op]
|
|
@@ -1819,6 +1822,12 @@ end
|
|
|
1819
1822
|
|
|
1820
1823
|
if __FILE__ == $0 or ARGV.delete('ignore_argv0')
|
|
1821
1824
|
|
|
1825
|
+
while i = ARGV.index('-e')
|
|
1826
|
+
# setup optim_hint etc
|
|
1827
|
+
ARGV.delete_at(i)
|
|
1828
|
+
eval ARGV.delete_at(i)
|
|
1829
|
+
end
|
|
1830
|
+
|
|
1822
1831
|
demo = case ARGV.first
|
|
1823
1832
|
when nil; :test_jit
|
|
1824
1833
|
when 'asm'; :inlineasm
|