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
@@ -30,7 +30,7 @@ class NDS < ExeFormat
|
|
30
30
|
mem :secareadisable, 8
|
31
31
|
words :endoff, :headersz
|
32
32
|
mem :reserved4, 56
|
33
|
-
|
33
|
+
mem :ninlogo, 156
|
34
34
|
half :logoCRC, 0xcf56
|
35
35
|
half :headerCRC
|
36
36
|
end
|
@@ -70,14 +70,17 @@ class NDS < ExeFormat
|
|
70
70
|
def decode_byte(edata = @encoded) edata.decode_imm(:u8, @endianness) end
|
71
71
|
def decode_half(edata = @encoded) edata.decode_imm(:u16, @endianness) end
|
72
72
|
def decode_word(edata = @encoded) edata.decode_imm(:u32, @endianness) end
|
73
|
+
def sizeof_byte ; 1 ; end
|
74
|
+
def sizeof_half ; 2 ; end
|
75
|
+
def sizeof_word ; 4 ; end
|
73
76
|
|
74
77
|
|
75
78
|
attr_accessor :header, :icon, :arm9, :arm7
|
76
79
|
attr_accessor :files, :fat
|
77
80
|
|
78
|
-
def initialize(
|
79
|
-
@endianness = endianness
|
80
|
-
|
81
|
+
def initialize(cpu=nil)
|
82
|
+
@endianness = (cpu ? cpu.endianness : :little)
|
83
|
+
super(cpu)
|
81
84
|
end
|
82
85
|
|
83
86
|
# decodes the header from the current offset in self.encoded
|
@@ -11,8 +11,9 @@ require 'metasm/exe_format/coff'
|
|
11
11
|
module Metasm
|
12
12
|
class PE < COFF
|
13
13
|
MAGIC = "PE\0\0" # 0x50450000
|
14
|
+
MAGIC.force_encoding('BINARY') if MAGIC.respond_to?(:force_encoding)
|
14
15
|
|
15
|
-
attr_accessor :coff_offset, :signature, :mz
|
16
|
+
attr_accessor :coff_offset, :signature, :mz, :productid
|
16
17
|
|
17
18
|
def initialize(*a)
|
18
19
|
super(*a)
|
@@ -26,7 +27,14 @@ class PE < COFF
|
|
26
27
|
def decode_header
|
27
28
|
@cursection ||= self
|
28
29
|
@encoded.ptr = 0x3c
|
29
|
-
|
30
|
+
lfanew = decode_word(@encoded)
|
31
|
+
|
32
|
+
try_rich_sz = [0x400, lfanew].min
|
33
|
+
rich_ary = []
|
34
|
+
rich_ary << decode_word(@encoded) while @encoded.ptr < try_rich_sz
|
35
|
+
@productid = decode_productid(rich_ary)
|
36
|
+
|
37
|
+
@encoded.ptr = lfanew
|
30
38
|
@signature = @encoded.read(4)
|
31
39
|
raise InvalidExeFormat, "Invalid PE signature #{@signature.inspect}" if @signature != MAGIC
|
32
40
|
@coff_offset = @encoded.ptr
|
@@ -38,6 +46,20 @@ class PE < COFF
|
|
38
46
|
super()
|
39
47
|
end
|
40
48
|
|
49
|
+
RICH_MAGIC = 0x68636952 # "Rich"
|
50
|
+
def decode_productid(ary)
|
51
|
+
return unless idx = ary.index(RICH_MAGIC) and xorkey = ary[idx+1]
|
52
|
+
ary = ary[0, idx-1].map { |dw| dw ^ xorkey }
|
53
|
+
return unless idx = ary.rindex(0x536E6144) # "DanS"
|
54
|
+
ary = ary[idx+2..-1]
|
55
|
+
out = []
|
56
|
+
until ary.empty?
|
57
|
+
ar1 = ary.shift
|
58
|
+
out << { :id => ar1 >> 16, :ver => ar1 & 0xffff, :count => ary.shift.to_i }
|
59
|
+
end
|
60
|
+
out
|
61
|
+
end
|
62
|
+
|
41
63
|
# creates a default MZ file to be used in the PE header
|
42
64
|
# this one is specially crafted to fit in the 0x3c bytes before the signature
|
43
65
|
def encode_default_mz_header
|
@@ -215,7 +237,7 @@ EOS
|
|
215
237
|
# TODO seh prototype (args => context)
|
216
238
|
# TODO hook on (non)resolution of :w xref
|
217
239
|
def get_xrefs_x(dasm, di)
|
218
|
-
if @cpu.shortname =~
|
240
|
+
if @cpu.shortname =~ /^ia32|^x64/ and a = di.instruction.args.first and a.kind_of?(Ia32::ModRM) and a.seg and a.seg.val == 4 and
|
219
241
|
w = get_xrefs_rw(dasm, di).find { |type, ptr, len| type == :w and ptr.externals.include? 'segment_base_fs' } and
|
220
242
|
dasm.backtrace(Expression[w[1], :-, 'segment_base_fs'], di.address).to_a.include?(Expression[0])
|
221
243
|
sehptr = w[1]
|
@@ -225,8 +247,8 @@ EOS
|
|
225
247
|
puts "backtrace seh from #{di} => #{a.map { |addr| Expression[addr] }.join(', ')}" if $VERBOSE
|
226
248
|
a.each { |aa|
|
227
249
|
next if aa == Expression::Unknown
|
228
|
-
|
229
|
-
dasm.addrs_todo <<
|
250
|
+
dasm.auto_label_at(aa, 'seh', 'loc', 'sub')
|
251
|
+
dasm.addrs_todo << { :addr => aa }
|
230
252
|
}
|
231
253
|
super(dasm, di)
|
232
254
|
else
|
@@ -243,17 +265,19 @@ EOS
|
|
243
265
|
old_cp = d.c_parser
|
244
266
|
d.c_parser = nil
|
245
267
|
d.parse_c '__stdcall void *GetProcAddress(int, char *);'
|
246
|
-
d.
|
268
|
+
d.parse_c '__stdcall void ExitProcess(int) __attribute__((noreturn));'
|
269
|
+
d.c_parser.lexer.define_weak('__MS_X86_64_ABI__') if @cpu.shortname == 'x64'
|
247
270
|
gpa = @cpu.decode_c_function_prototype(d.c_parser, 'GetProcAddress')
|
271
|
+
epr = @cpu.decode_c_function_prototype(d.c_parser, 'ExitProcess')
|
248
272
|
d.c_parser = old_cp
|
249
273
|
d.parse_c ''
|
250
|
-
d.c_parser.lexer.define_weak('__MS_X86_64_ABI__') if @cpu.
|
274
|
+
d.c_parser.lexer.define_weak('__MS_X86_64_ABI__') if @cpu.shortname == 'x64'
|
251
275
|
@getprocaddr_unknown = []
|
252
276
|
gpa.btbind_callback = lambda { |dasm, bind, funcaddr, calladdr, expr, origin, maxdepth|
|
253
277
|
break bind if @getprocaddr_unknown.include? [dasm, calladdr] or not Expression[expr].externals.include? :eax
|
254
278
|
sz = @cpu.size/8
|
255
279
|
break bind if not dasm.decoded[calladdr]
|
256
|
-
if @cpu.
|
280
|
+
if @cpu.shortname == 'x64'
|
257
281
|
arg2 = :rdx
|
258
282
|
else
|
259
283
|
arg2 = Indirection[[:esp, :+, 2*sz], sz, calladdr]
|
@@ -268,6 +292,7 @@ EOS
|
|
268
292
|
bind
|
269
293
|
}
|
270
294
|
d.function[Expression['GetProcAddress']] = gpa
|
295
|
+
d.function[Expression['ExitProcess']] = epr
|
271
296
|
d.function[:default] = @cpu.disassembler_default_func
|
272
297
|
end
|
273
298
|
d
|
@@ -294,6 +319,62 @@ EOS
|
|
294
319
|
} if export
|
295
320
|
syms
|
296
321
|
end
|
322
|
+
|
323
|
+
# compute the pe-sha1 or pe-sha256 of the binary
|
324
|
+
# argument should be a Digest::SHA1 (from digest/sha1) or a Digest::SHA256 (from digest/sha2)
|
325
|
+
# returns the hex checksum
|
326
|
+
def pehash(digest)
|
327
|
+
off0 = 0
|
328
|
+
off1 = @coff_offset + @header.sizeof(self) + @optheader.offsetof(self, :checksum)
|
329
|
+
|
330
|
+
dir_ct_idx = DIRECTORIES.index('certificate_table')
|
331
|
+
if @optheader.numrva > dir_ct_idx
|
332
|
+
off2 = @coff_offset + @header.sizeof(self) + @optheader.sizeof(self) + 8*dir_ct_idx
|
333
|
+
ct_size = @encoded.data[off2, 8].unpack('V*')[1]
|
334
|
+
off3 = @encoded.length - ct_size
|
335
|
+
else
|
336
|
+
off4 = @encoded.length
|
337
|
+
end
|
338
|
+
|
339
|
+
digest << @encoded.data[off0 ... off1].to_str
|
340
|
+
digest << @encoded.data[off1+4 ... off2].to_str if off2
|
341
|
+
digest << @encoded.data[off2+8 ... off3].to_str if off2 and off3 > off2+8
|
342
|
+
digest << @encoded.data[off1+4 ... off4].to_str if off4
|
343
|
+
digest << ("\0" * (8 - (@encoded.length & 7))) if @encoded.length & 7 != 0
|
344
|
+
|
345
|
+
digest.hexdigest
|
346
|
+
end
|
347
|
+
|
348
|
+
def self.pehash(path, digest)
|
349
|
+
decode_file_header(path).pehash(digest)
|
350
|
+
end
|
351
|
+
|
352
|
+
# compute Mandiant "importhash"
|
353
|
+
def imphash
|
354
|
+
lst = []
|
355
|
+
@imports.to_a.each { |id|
|
356
|
+
ln = id.libname.downcase.sub(/.(dll|sys|ocx)$/, '')
|
357
|
+
id.imports.each { |i|
|
358
|
+
if not i.name and ordtable = WindowsExports::IMPORT_HASH[ln]
|
359
|
+
iname = ordtable[i.ordinal]
|
360
|
+
else
|
361
|
+
iname = i.name
|
362
|
+
end
|
363
|
+
iname ||= "ord#{i.ordinal}"
|
364
|
+
|
365
|
+
lst << "#{ln}.#{iname}"
|
366
|
+
}
|
367
|
+
}
|
368
|
+
|
369
|
+
require 'digest/md5'
|
370
|
+
Digest::MD5.hexdigest(lst.join(',').downcase)
|
371
|
+
end
|
372
|
+
|
373
|
+
def self.imphash(path)
|
374
|
+
pe = decode_file_header(path)
|
375
|
+
pe.decode_imports
|
376
|
+
pe.imphash
|
377
|
+
end
|
297
378
|
end
|
298
379
|
|
299
380
|
# an instance of a PE file, loaded in memory
|
@@ -312,7 +393,7 @@ class LoadedPE < PE
|
|
312
393
|
|
313
394
|
# reads a loaded PE from memory, returns a PE object
|
314
395
|
# dumps the header, optheader and all sections ; try to rebuild IAT (#memdump_imports)
|
315
|
-
def self.memdump(memory, baseaddr, entrypoint
|
396
|
+
def self.memdump(memory, baseaddr, entrypoint=nil, iat_p=nil)
|
316
397
|
loaded = LoadedPE.load memory[baseaddr, 0x1000_0000]
|
317
398
|
loaded.load_address = baseaddr
|
318
399
|
loaded.decode
|
@@ -372,7 +453,6 @@ class LoadedPE < PE
|
|
372
453
|
else
|
373
454
|
# read imported pointer from the import structure
|
374
455
|
while not ptr = imports.first.iat.shift
|
375
|
-
load_dll = nil
|
376
456
|
imports.shift
|
377
457
|
break if imports.empty?
|
378
458
|
iat_p = imports.first.iat_p
|
@@ -415,6 +495,7 @@ class LoadedPE < PE
|
|
415
495
|
puts 'unknown ptr %x' % ptr if $DEBUG
|
416
496
|
# allow holes in the unk_iat_p table
|
417
497
|
break if not unk_iat_p or failcnt > 4
|
498
|
+
loaded_dll = nil
|
418
499
|
failcnt += 1
|
419
500
|
next
|
420
501
|
end
|
@@ -422,7 +503,7 @@ class LoadedPE < PE
|
|
422
503
|
end
|
423
504
|
|
424
505
|
# dumped last importdirectory is correct, append the import field
|
425
|
-
|
506
|
+
i = ImportDirectory::Import.new
|
426
507
|
if e.name
|
427
508
|
puts e.name if $DEBUG
|
428
509
|
i.name = e.name
|
@@ -433,5 +514,9 @@ class LoadedPE < PE
|
|
433
514
|
dump.imports.last.imports << i
|
434
515
|
end
|
435
516
|
end
|
517
|
+
|
518
|
+
def pehash(digest)
|
519
|
+
raise "cannot compute a PEhash from memory image"
|
520
|
+
end
|
436
521
|
end
|
437
522
|
end
|
@@ -0,0 +1,167 @@
|
|
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
|
+
require 'metasm/exe_format/main'
|
8
|
+
require 'metasm/encode'
|
9
|
+
require 'metasm/decode'
|
10
|
+
|
11
|
+
|
12
|
+
module Metasm
|
13
|
+
# Python preparsed module (.pyc)
|
14
|
+
class PYC < ExeFormat
|
15
|
+
# 1 magic per python version...
|
16
|
+
# file = MAGIC(u16) \r \n timestamp(u32) data
|
17
|
+
MAGICS = [
|
18
|
+
62211 # 62211 = python2.7a0
|
19
|
+
]
|
20
|
+
|
21
|
+
class Header < SerialStruct
|
22
|
+
half :version
|
23
|
+
half :rn
|
24
|
+
word :timestamp
|
25
|
+
end
|
26
|
+
|
27
|
+
def decode_half(edata=@encoded) edata.decode_imm(:u16, @endianness) end
|
28
|
+
def decode_word(edata=@encoded) edata.decode_imm(:u32, @endianness) end
|
29
|
+
def decode_long(edata=@encoded) edata.decode_imm(:i32, @endianness) end
|
30
|
+
def sizeof_half ; 2 ; end
|
31
|
+
def sizeof_word ; 4 ; end
|
32
|
+
def sizeof_long ; 4 ; end
|
33
|
+
|
34
|
+
# file header
|
35
|
+
attr_accessor :header
|
36
|
+
# the marshalled object
|
37
|
+
attr_accessor :root
|
38
|
+
# list of all code objects
|
39
|
+
attr_accessor :all_code
|
40
|
+
|
41
|
+
def initialize()
|
42
|
+
@endianness = :little
|
43
|
+
@encoded = EncodedData.new
|
44
|
+
super()
|
45
|
+
end
|
46
|
+
|
47
|
+
def decode_header
|
48
|
+
@header = Header.decode(self)
|
49
|
+
end
|
50
|
+
|
51
|
+
def decode_pymarshal
|
52
|
+
case c = @encoded.read(1)
|
53
|
+
when '0' # NULL
|
54
|
+
:null
|
55
|
+
when 'N' # None
|
56
|
+
nil
|
57
|
+
when 'F' # False
|
58
|
+
false
|
59
|
+
when 'T' # True
|
60
|
+
true
|
61
|
+
#when 'S' # stopiter TODO
|
62
|
+
#when '.' # ellipsis TODO
|
63
|
+
when 'i' # long (i32)
|
64
|
+
decode_long
|
65
|
+
when 'I' # long (i64)
|
66
|
+
decode_word | (decode_long << 32)
|
67
|
+
when 'f' # float (ascii)
|
68
|
+
@encoded.read(@encoded.read(1).unpack('C').first).to_f
|
69
|
+
when 'g' # float (binary)
|
70
|
+
@encoded.read(8).unpack('d').first # XXX check
|
71
|
+
when 'x' # complex (f f)
|
72
|
+
{ :type => :complex,
|
73
|
+
:real => @encoded.read(@encoded.read(1).unpack('C').first).to_f,
|
74
|
+
:imag => @encoded.read(@encoded.read(1).unpack('C').first).to_f }
|
75
|
+
when 'y' # complex (g g)
|
76
|
+
{ :type => :complex,
|
77
|
+
:real => @encoded.read(8).unpack('d').first,
|
78
|
+
:imag => @encoded.read(8).unpack('d').first }
|
79
|
+
when 'l' # long (i32?)
|
80
|
+
decode_long
|
81
|
+
when 's' # string: len (long), data
|
82
|
+
@encoded.read(decode_long)
|
83
|
+
when 't' # 'interned': string with possible backreference later
|
84
|
+
s = @encoded.read(decode_long)
|
85
|
+
@references << s
|
86
|
+
s
|
87
|
+
when 'R' # stringref (see 't')
|
88
|
+
@references[decode_long]
|
89
|
+
when '(' # tuple (frozen Array): length l*objs
|
90
|
+
obj = []
|
91
|
+
decode_long.times { obj << decode_pymarshal }
|
92
|
+
obj
|
93
|
+
when '[' # list (Array)
|
94
|
+
obj = []
|
95
|
+
decode_long.times { obj << decode_pymarshal }
|
96
|
+
obj
|
97
|
+
when '{' # dict (Hash)
|
98
|
+
obj = {}
|
99
|
+
loop do
|
100
|
+
k = decode_pymarshal
|
101
|
+
break if k == :null
|
102
|
+
obj[k] = decode_pymarshal
|
103
|
+
end
|
104
|
+
{ :type => hash, :hash => obj } # XXX to avoid confusion with code, etc
|
105
|
+
when 'c' # code
|
106
|
+
# XXX format varies with version (header.signature)
|
107
|
+
obj = {}
|
108
|
+
obj[:type] = :code
|
109
|
+
obj[:argcount] = decode_long
|
110
|
+
#obj[:kwonly_argcount] = decode_long # not in py2.7
|
111
|
+
obj[:nlocals] = decode_long
|
112
|
+
obj[:stacksize] = decode_long
|
113
|
+
obj[:flags] = decode_long # TODO bit-decode this one
|
114
|
+
|
115
|
+
obj[:fileoff] = @encoded.ptr + 5 # XXX assume :code is a 's'
|
116
|
+
obj[:code] = decode_pymarshal
|
117
|
+
obj[:consts] = decode_pymarshal
|
118
|
+
obj[:names] = decode_pymarshal
|
119
|
+
obj[:varnames] = decode_pymarshal
|
120
|
+
obj[:freevars] = decode_pymarshal
|
121
|
+
obj[:cellvars] = decode_pymarshal
|
122
|
+
obj[:filename] = decode_pymarshal
|
123
|
+
obj[:name] = decode_pymarshal
|
124
|
+
obj[:firstlineno] = decode_long
|
125
|
+
obj[:lnotab] = decode_pymarshal
|
126
|
+
@all_code << obj
|
127
|
+
obj
|
128
|
+
when 'u' # unicode
|
129
|
+
@encoded.read(decode_long)
|
130
|
+
#when '?' # unknown TODO
|
131
|
+
#when '<' # set TODO
|
132
|
+
#when '>' # set (frozen) TODO
|
133
|
+
else
|
134
|
+
raise "unsupported python marshal #{c.inspect}"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def decode
|
139
|
+
decode_header
|
140
|
+
@all_code = []
|
141
|
+
@references = []
|
142
|
+
@root = decode_pymarshal
|
143
|
+
@references = nil
|
144
|
+
end
|
145
|
+
|
146
|
+
def cpu_from_headers
|
147
|
+
Python.new(self)
|
148
|
+
end
|
149
|
+
|
150
|
+
def each_section
|
151
|
+
yield @encoded, 0
|
152
|
+
end
|
153
|
+
|
154
|
+
def get_default_entrypoints
|
155
|
+
if @root.kind_of? Hash and @root[:type] == :code
|
156
|
+
[@root[:fileoff]]
|
157
|
+
else
|
158
|
+
[]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# return the :code part which contains off
|
163
|
+
def code_at_off(off)
|
164
|
+
@all_code.find { |c| c[:fileoff] <= off and c[:fileoff] + c[:code].length > off }
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -13,19 +13,20 @@ class SerialStruct
|
|
13
13
|
NAME=0
|
14
14
|
DECODE=1
|
15
15
|
ENCODE=2
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
SIZEOF=3
|
17
|
+
DEFVAL=4
|
18
|
+
ENUM=5
|
19
|
+
BITS=6
|
19
20
|
|
20
21
|
class << self
|
21
22
|
# defines a new field
|
22
23
|
# adds an accessor
|
23
|
-
def new_field(name, decode, encode, defval, enum=nil, bits=nil)
|
24
|
+
def new_field(name, decode, encode, sizeof, defval, enum=nil, bits=nil)
|
24
25
|
if name
|
25
26
|
attr_accessor name
|
26
27
|
name = "@#{name}".to_sym
|
27
28
|
end
|
28
|
-
(@@fields[self] ||= []) << [name, decode, encode, defval, enum, bits]
|
29
|
+
(@@fields[self] ||= []) << [name, decode, encode, sizeof, defval, enum, bits]
|
29
30
|
end
|
30
31
|
|
31
32
|
# creates a field constructor for a simple integer
|
@@ -34,7 +35,7 @@ class << self
|
|
34
35
|
recv = class << self ; self ; end
|
35
36
|
types.each { |type|
|
36
37
|
recv.send(:define_method, type) { |name, *args|
|
37
|
-
new_field(name, "decode_#{type}".to_sym, "encode_#{type}".to_sym, args[0] || 0, args[1])
|
38
|
+
new_field(name, "decode_#{type}".to_sym, "encode_#{type}".to_sym, "sizeof_#{type}".to_sym, args[0] || 0, args[1])
|
38
39
|
}
|
39
40
|
|
40
41
|
# shortcut to define multiple fields of this type with default values
|
@@ -46,24 +47,33 @@ class << self
|
|
46
47
|
|
47
48
|
# standard fields:
|
48
49
|
|
50
|
+
# virtual field, handled explicitly in a custom encode/decode
|
51
|
+
def virtual(*a)
|
52
|
+
a.each { |f|
|
53
|
+
new_field(f, nil, nil, nil, nil)
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
49
57
|
# a fixed-size memory chunk
|
50
58
|
def mem(name, len, defval='')
|
51
|
-
new_field(name, lambda { |exe, me| exe.curencoded.read(len) }, lambda { |exe, me, val| val[0, len].ljust(len, 0.chr) }, defval)
|
59
|
+
new_field(name, lambda { |exe, me| exe.curencoded.read(len) }, lambda { |exe, me, val| d = val[0, len].ljust(len, 0.chr) ; d.force_encoding('BINARY') if d.respond_to?(:force_encoding) ; d }, lambda { |exe, me| len }, defval)
|
52
60
|
end
|
53
61
|
# a fixed-size string, 0-padded
|
54
62
|
def str(name, len, defval='')
|
55
|
-
e = lambda { |exe, me, val| val[0, len].ljust(len, 0.chr) }
|
56
63
|
d = lambda { |exe, me| v = exe.curencoded.read(len) ; v = v[0, v.index(?\0)] if v.index(?\0) ; v }
|
57
|
-
|
64
|
+
e = lambda { |exe, me, val| val[0, len].ljust(len, 0.chr) }
|
65
|
+
s = lambda { |exe, me| len }
|
66
|
+
new_field(name, d, e, s, defval)
|
58
67
|
end
|
59
68
|
# 0-terminated string
|
60
69
|
def strz(name, defval='')
|
61
70
|
d = lambda { |exe, me|
|
62
|
-
|
71
|
+
ed = exe.curencoded
|
63
72
|
ed.read(ed.data.index(?\0, ed.ptr)-ed.ptr+1).chop
|
64
73
|
}
|
65
74
|
e = lambda { |exe, me, val| val + 0.chr }
|
66
|
-
|
75
|
+
s = lambda { |exe, val| val.length + 1 }
|
76
|
+
new_field(name, d, e, s, defval)
|
67
77
|
end
|
68
78
|
|
69
79
|
# field access
|
@@ -93,7 +103,7 @@ class << self
|
|
93
103
|
d = lambda { |exe, me| @bitfield_val = exe.send("decode_#{inttype}") }
|
94
104
|
# reset a temp var
|
95
105
|
e = lambda { |exe, me, val| @bitfield_val = 0 ; nil }
|
96
|
-
new_field(nil, d, e, nil)
|
106
|
+
new_field(nil, d, e, 0, nil)
|
97
107
|
|
98
108
|
h = h.sort
|
99
109
|
h.length.times { |i|
|
@@ -107,7 +117,7 @@ class << self
|
|
107
117
|
d = lambda { |exe, me| (@bitfield_val >> off) & mask }
|
108
118
|
# update the temp var with the field value, return nil
|
109
119
|
e = lambda { |exe, me, val| @bitfield_val |= (val & mask) << off ; nil }
|
110
|
-
|
120
|
+
new_field(name, d, e, 0, 0)
|
111
121
|
}
|
112
122
|
|
113
123
|
# free the temp var
|
@@ -118,11 +128,13 @@ class << self
|
|
118
128
|
@bitfield_val = nil
|
119
129
|
exe.send("encode_#{inttype}", val)
|
120
130
|
}
|
121
|
-
|
131
|
+
s = lambda { |exe, me| exe.send("sizeof_#{inttype}") }
|
132
|
+
new_field(nil, d, e, s, nil)
|
122
133
|
end
|
123
134
|
|
124
135
|
# inject a hook to be run during the decoding process
|
125
136
|
def decode_hook(before=nil, &b)
|
137
|
+
@@fields[self] ||= []
|
126
138
|
idx = (before ? @@fields[self].index(fld_get(before)) : -1)
|
127
139
|
@@fields[self].insert(idx, [nil, b])
|
128
140
|
end
|
@@ -209,6 +221,39 @@ end # class methods
|
|
209
221
|
ed
|
210
222
|
end
|
211
223
|
|
224
|
+
# size of the structure = fields.sum { size of field }
|
225
|
+
def sizeof(exe)
|
226
|
+
struct_fields(exe).inject(0) { |off, f|
|
227
|
+
case sz = f[SIZEOF]
|
228
|
+
when Proc; sz = sz[exe, self]
|
229
|
+
when Symbol; sz = exe.send(sz)
|
230
|
+
when Array; sz = exe.send(*sz)
|
231
|
+
when nil; sz = 0
|
232
|
+
end
|
233
|
+
off + sz
|
234
|
+
}
|
235
|
+
end
|
236
|
+
|
237
|
+
# offset (in bytes) of the structure member
|
238
|
+
# for bitfields, return the byte offset of the whole bitfield
|
239
|
+
def offsetof(exe, fld)
|
240
|
+
fld2 = fld
|
241
|
+
fld2 = "@#{fld}".to_sym if fld.to_s[0] != ?@
|
242
|
+
off = 0
|
243
|
+
struct_fields(exe).each { |f|
|
244
|
+
return off if f[NAME] == fld or f[NAME] == fld2
|
245
|
+
|
246
|
+
case sz = f[SIZEOF]
|
247
|
+
when Proc; sz = sz[exe, self]
|
248
|
+
when Symbol; sz = exe.send(sz)
|
249
|
+
when Array; sz = exe.send(*sz)
|
250
|
+
when nil; sz = 0
|
251
|
+
end
|
252
|
+
off += sz
|
253
|
+
}
|
254
|
+
raise 'unknown field'
|
255
|
+
end
|
256
|
+
|
212
257
|
# shortcut to create a new instance and decode it
|
213
258
|
def self.decode(*a)
|
214
259
|
s = new
|
@@ -216,6 +261,14 @@ end # class methods
|
|
216
261
|
s
|
217
262
|
end
|
218
263
|
|
264
|
+
def self.sizeof(exe)
|
265
|
+
new.sizeof(exe)
|
266
|
+
end
|
267
|
+
|
268
|
+
def self.offsetof(exe, fld)
|
269
|
+
new.offsetof(exe, fld)
|
270
|
+
end
|
271
|
+
|
219
272
|
def dump(e, a)
|
220
273
|
case e
|
221
274
|
when Integer; e >= 0x100 ? '0x%X'%e : e
|