metasm 1.0.0
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.
- data/BUGS +11 -0
- data/CREDITS +17 -0
- data/README +270 -0
- data/TODO +114 -0
- data/doc/code_organisation.txt +146 -0
- data/doc/const_missing.txt +16 -0
- data/doc/core_classes.txt +75 -0
- data/doc/feature_list.txt +53 -0
- data/doc/index.txt +59 -0
- data/doc/install_notes.txt +170 -0
- data/doc/style.css +3 -0
- data/doc/use_cases.txt +18 -0
- data/lib/metasm.rb +80 -0
- data/lib/metasm/arm.rb +12 -0
- data/lib/metasm/arm/debug.rb +39 -0
- data/lib/metasm/arm/decode.rb +167 -0
- data/lib/metasm/arm/encode.rb +77 -0
- data/lib/metasm/arm/main.rb +75 -0
- data/lib/metasm/arm/opcodes.rb +177 -0
- data/lib/metasm/arm/parse.rb +130 -0
- data/lib/metasm/arm/render.rb +55 -0
- data/lib/metasm/compile_c.rb +1457 -0
- data/lib/metasm/dalvik.rb +8 -0
- data/lib/metasm/dalvik/decode.rb +196 -0
- data/lib/metasm/dalvik/main.rb +60 -0
- data/lib/metasm/dalvik/opcodes.rb +366 -0
- data/lib/metasm/decode.rb +213 -0
- data/lib/metasm/decompile.rb +2659 -0
- data/lib/metasm/disassemble.rb +2068 -0
- data/lib/metasm/disassemble_api.rb +1280 -0
- data/lib/metasm/dynldr.rb +1329 -0
- data/lib/metasm/encode.rb +333 -0
- data/lib/metasm/exe_format/a_out.rb +194 -0
- data/lib/metasm/exe_format/autoexe.rb +82 -0
- data/lib/metasm/exe_format/bflt.rb +189 -0
- data/lib/metasm/exe_format/coff.rb +455 -0
- data/lib/metasm/exe_format/coff_decode.rb +901 -0
- data/lib/metasm/exe_format/coff_encode.rb +1078 -0
- data/lib/metasm/exe_format/dex.rb +457 -0
- data/lib/metasm/exe_format/dol.rb +145 -0
- data/lib/metasm/exe_format/elf.rb +923 -0
- data/lib/metasm/exe_format/elf_decode.rb +979 -0
- data/lib/metasm/exe_format/elf_encode.rb +1375 -0
- data/lib/metasm/exe_format/macho.rb +827 -0
- data/lib/metasm/exe_format/main.rb +228 -0
- data/lib/metasm/exe_format/mz.rb +164 -0
- data/lib/metasm/exe_format/nds.rb +172 -0
- data/lib/metasm/exe_format/pe.rb +437 -0
- data/lib/metasm/exe_format/serialstruct.rb +246 -0
- data/lib/metasm/exe_format/shellcode.rb +114 -0
- data/lib/metasm/exe_format/xcoff.rb +167 -0
- data/lib/metasm/gui.rb +23 -0
- data/lib/metasm/gui/cstruct.rb +373 -0
- data/lib/metasm/gui/dasm_coverage.rb +199 -0
- data/lib/metasm/gui/dasm_decomp.rb +369 -0
- data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
- data/lib/metasm/gui/dasm_graph.rb +1354 -0
- data/lib/metasm/gui/dasm_hex.rb +543 -0
- data/lib/metasm/gui/dasm_listing.rb +599 -0
- data/lib/metasm/gui/dasm_main.rb +906 -0
- data/lib/metasm/gui/dasm_opcodes.rb +291 -0
- data/lib/metasm/gui/debug.rb +1228 -0
- data/lib/metasm/gui/gtk.rb +884 -0
- data/lib/metasm/gui/qt.rb +495 -0
- data/lib/metasm/gui/win32.rb +3004 -0
- data/lib/metasm/gui/x11.rb +621 -0
- data/lib/metasm/ia32.rb +14 -0
- data/lib/metasm/ia32/compile_c.rb +1523 -0
- data/lib/metasm/ia32/debug.rb +193 -0
- data/lib/metasm/ia32/decode.rb +1167 -0
- data/lib/metasm/ia32/decompile.rb +564 -0
- data/lib/metasm/ia32/encode.rb +314 -0
- data/lib/metasm/ia32/main.rb +233 -0
- data/lib/metasm/ia32/opcodes.rb +872 -0
- data/lib/metasm/ia32/parse.rb +327 -0
- data/lib/metasm/ia32/render.rb +91 -0
- data/lib/metasm/main.rb +1193 -0
- data/lib/metasm/mips.rb +11 -0
- data/lib/metasm/mips/compile_c.rb +7 -0
- data/lib/metasm/mips/decode.rb +253 -0
- data/lib/metasm/mips/encode.rb +51 -0
- data/lib/metasm/mips/main.rb +72 -0
- data/lib/metasm/mips/opcodes.rb +443 -0
- data/lib/metasm/mips/parse.rb +51 -0
- data/lib/metasm/mips/render.rb +43 -0
- data/lib/metasm/os/gnu_exports.rb +270 -0
- data/lib/metasm/os/linux.rb +1112 -0
- data/lib/metasm/os/main.rb +1686 -0
- data/lib/metasm/os/remote.rb +527 -0
- data/lib/metasm/os/windows.rb +2027 -0
- data/lib/metasm/os/windows_exports.rb +745 -0
- data/lib/metasm/parse.rb +876 -0
- data/lib/metasm/parse_c.rb +3938 -0
- data/lib/metasm/pic16c/decode.rb +42 -0
- data/lib/metasm/pic16c/main.rb +17 -0
- data/lib/metasm/pic16c/opcodes.rb +68 -0
- data/lib/metasm/ppc.rb +11 -0
- data/lib/metasm/ppc/decode.rb +264 -0
- data/lib/metasm/ppc/decompile.rb +251 -0
- data/lib/metasm/ppc/encode.rb +51 -0
- data/lib/metasm/ppc/main.rb +129 -0
- data/lib/metasm/ppc/opcodes.rb +410 -0
- data/lib/metasm/ppc/parse.rb +52 -0
- data/lib/metasm/preprocessor.rb +1277 -0
- data/lib/metasm/render.rb +130 -0
- data/lib/metasm/sh4.rb +8 -0
- data/lib/metasm/sh4/decode.rb +336 -0
- data/lib/metasm/sh4/main.rb +292 -0
- data/lib/metasm/sh4/opcodes.rb +381 -0
- data/lib/metasm/x86_64.rb +12 -0
- data/lib/metasm/x86_64/compile_c.rb +1025 -0
- data/lib/metasm/x86_64/debug.rb +59 -0
- data/lib/metasm/x86_64/decode.rb +268 -0
- data/lib/metasm/x86_64/encode.rb +264 -0
- data/lib/metasm/x86_64/main.rb +135 -0
- data/lib/metasm/x86_64/opcodes.rb +118 -0
- data/lib/metasm/x86_64/parse.rb +68 -0
- data/misc/bottleneck.rb +61 -0
- data/misc/cheader-findpppath.rb +58 -0
- data/misc/hexdiff.rb +74 -0
- data/misc/hexdump.rb +55 -0
- data/misc/metasm-all.rb +13 -0
- data/misc/objdiff.rb +47 -0
- data/misc/objscan.rb +40 -0
- data/misc/pdfparse.rb +661 -0
- data/misc/ppc_pdf2oplist.rb +192 -0
- data/misc/tcp_proxy_hex.rb +84 -0
- data/misc/txt2html.rb +440 -0
- data/samples/a.out.rb +31 -0
- data/samples/asmsyntax.rb +77 -0
- data/samples/bindiff.rb +555 -0
- data/samples/compilation-steps.rb +49 -0
- data/samples/cparser_makestackoffset.rb +55 -0
- data/samples/dasm-backtrack.rb +38 -0
- data/samples/dasmnavig.rb +318 -0
- data/samples/dbg-apihook.rb +228 -0
- data/samples/dbghelp.rb +143 -0
- data/samples/disassemble-gui.rb +102 -0
- data/samples/disassemble.rb +133 -0
- data/samples/dump_upx.rb +95 -0
- data/samples/dynamic_ruby.rb +1929 -0
- data/samples/elf_list_needed.rb +46 -0
- data/samples/elf_listexports.rb +33 -0
- data/samples/elfencode.rb +25 -0
- data/samples/exeencode.rb +128 -0
- data/samples/factorize-headers-elfimports.rb +77 -0
- data/samples/factorize-headers-peimports.rb +109 -0
- data/samples/factorize-headers.rb +43 -0
- data/samples/gdbclient.rb +583 -0
- data/samples/generate_libsigs.rb +102 -0
- data/samples/hotfix_gtk_dbg.rb +59 -0
- data/samples/install_win_env.rb +78 -0
- data/samples/lindebug.rb +924 -0
- data/samples/linux_injectsyscall.rb +95 -0
- data/samples/machoencode.rb +31 -0
- data/samples/metasm-shell.rb +91 -0
- data/samples/pe-hook.rb +69 -0
- data/samples/pe-ia32-cpuid.rb +203 -0
- data/samples/pe-mips.rb +35 -0
- data/samples/pe-shutdown.rb +78 -0
- data/samples/pe-testrelocs.rb +51 -0
- data/samples/pe-testrsrc.rb +24 -0
- data/samples/pe_listexports.rb +31 -0
- data/samples/peencode.rb +19 -0
- data/samples/peldr.rb +494 -0
- data/samples/preprocess-flatten.rb +19 -0
- data/samples/r0trace.rb +308 -0
- data/samples/rubstop.rb +399 -0
- data/samples/scan_pt_gnu_stack.rb +54 -0
- data/samples/scanpeexports.rb +62 -0
- data/samples/shellcode-c.rb +40 -0
- data/samples/shellcode-dynlink.rb +146 -0
- data/samples/source.asm +34 -0
- data/samples/struct_offset.rb +47 -0
- data/samples/testpe.rb +32 -0
- data/samples/testraw.rb +45 -0
- data/samples/win32genloader.rb +132 -0
- data/samples/win32hooker-advanced.rb +169 -0
- data/samples/win32hooker.rb +96 -0
- data/samples/win32livedasm.rb +33 -0
- data/samples/win32remotescan.rb +133 -0
- data/samples/wintrace.rb +92 -0
- data/tests/all.rb +8 -0
- data/tests/dasm.rb +39 -0
- data/tests/dynldr.rb +35 -0
- data/tests/encodeddata.rb +132 -0
- data/tests/ia32.rb +82 -0
- data/tests/mips.rb +116 -0
- data/tests/parse_c.rb +239 -0
- data/tests/preprocessor.rb +269 -0
- data/tests/x86_64.rb +62 -0
- metadata +255 -0
|
@@ -0,0 +1,82 @@
|
|
|
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
|
+
require 'metasm/exe_format/main'
|
|
7
|
+
|
|
8
|
+
module Metasm
|
|
9
|
+
# special class that decodes a PE, ELF, MachO or UnivBinary file from its signature
|
|
10
|
+
# XXX UnivBinary is not a real ExeFormat, just a container..
|
|
11
|
+
class AutoExe < ExeFormat
|
|
12
|
+
class UnknownSignature < InvalidExeFormat ; end
|
|
13
|
+
|
|
14
|
+
# actually calls autoexe_load for the detected filetype from #execlass_from_signature
|
|
15
|
+
def self.load(str, *a, &b)
|
|
16
|
+
s = str
|
|
17
|
+
s = str.data if s.kind_of? EncodedData
|
|
18
|
+
execlass_from_signature(s).autoexe_load(str, *a, &b)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# match the actual exe class from the raw file inspection using the registered signature list
|
|
22
|
+
# calls #unknown_signature if nothing matches
|
|
23
|
+
def self.execlass_from_signature(raw)
|
|
24
|
+
m = @signatures.find { |sig, exe|
|
|
25
|
+
case sig
|
|
26
|
+
when String; raw[0, sig.length] == sig
|
|
27
|
+
when Proc; sig[raw]
|
|
28
|
+
end
|
|
29
|
+
}
|
|
30
|
+
e = m ? m[1] : unknown_signature(raw)
|
|
31
|
+
case e
|
|
32
|
+
when String; Metasm.const_get(e)
|
|
33
|
+
when Proc; e.call
|
|
34
|
+
else e
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# register a new binary file signature
|
|
39
|
+
def self.register_signature(sig, exe=nil, &b)
|
|
40
|
+
(@signatures ||= []) << [sig, exe || b]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.init_signatures(sig=[])
|
|
44
|
+
@signatures = sig
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# this function is called when no signature matches
|
|
48
|
+
def self.unknown_signature(raw)
|
|
49
|
+
raise UnknownSignature, "unrecognized executable file format #{raw[0, 4].unpack('H*').first.inspect}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# raw signature copies (avoid triggering exefmt autorequire)
|
|
53
|
+
init_signatures
|
|
54
|
+
register_signature("\x7fELF") { ELF }
|
|
55
|
+
register_signature(lambda { |raw| raw[0, 2] == "MZ" and off = raw[0x3c, 4].to_s.unpack('V')[0] and off < raw.length and raw[off, 4] == "PE\0\0" }) { PE }
|
|
56
|
+
%w[feedface cefaedfe feedfacf cffaedfe].each { |sig| register_signature([sig].pack('H*')) { MachO } }
|
|
57
|
+
register_signature("\xca\xfe\xba\xbe") { UniversalBinary }
|
|
58
|
+
register_signature("dex\n") { DEX }
|
|
59
|
+
register_signature("dey\n") { DEY }
|
|
60
|
+
register_signature("\xfa\x70\x0e\x1f") { FatELF }
|
|
61
|
+
register_signature('Metasm.dasm') { Disassembler }
|
|
62
|
+
|
|
63
|
+
# replacement for AutoExe where #load defaults to a Shellcode of the specified CPU
|
|
64
|
+
def self.orshellcode(cpu=nil, &b)
|
|
65
|
+
# here we create an anonymous subclass of AutoExe whose #unknown_sig is patched to return a Shellcode instead of raise()ing
|
|
66
|
+
c = ::Class.new(self)
|
|
67
|
+
# yeeehaa
|
|
68
|
+
class << c ; self ; end.send(:define_method, :unknown_signature) { |raw|
|
|
69
|
+
Shellcode.withcpu(cpu || b[raw])
|
|
70
|
+
}
|
|
71
|
+
c.init_signatures @signatures
|
|
72
|
+
c
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# special class that decodes a LoadedPE or LoadedELF from its signature (used to read memory-mapped binaries)
|
|
77
|
+
class LoadedAutoExe < AutoExe
|
|
78
|
+
init_signatures
|
|
79
|
+
register_signature("\x7fELF") { LoadedELF }
|
|
80
|
+
register_signature(lambda { |raw| raw[0, 2] == "MZ" and off = raw[0x3c, 4].to_s.unpack('V')[0] and off < raw.length and raw[off, 4] == "PE\0\0" }) { LoadedPE }
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,189 @@
|
|
|
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
|
+
require 'metasm/exe_format/main'
|
|
7
|
+
require 'metasm/encode'
|
|
8
|
+
require 'metasm/decode'
|
|
9
|
+
|
|
10
|
+
module Metasm
|
|
11
|
+
# BFLT is the binary flat format used by the uClinux
|
|
12
|
+
class Bflt < ExeFormat
|
|
13
|
+
MAGIC = 'bFLT'
|
|
14
|
+
FLAGS = { 1 => 'RAM', 2 => 'GOTPIC', 4 => 'GZIP' }
|
|
15
|
+
|
|
16
|
+
attr_accessor :header, :text, :data, :reloc, :got
|
|
17
|
+
|
|
18
|
+
class Header < SerialStruct
|
|
19
|
+
mem :magic, 4
|
|
20
|
+
words :rev, :entry, :data_start, :data_end, :bss_end, :stack_size,
|
|
21
|
+
:reloc_start, :reloc_count, :flags
|
|
22
|
+
mem :pad, 6*4
|
|
23
|
+
fld_bits(:flags, FLAGS)
|
|
24
|
+
|
|
25
|
+
def decode(exe)
|
|
26
|
+
super(exe)
|
|
27
|
+
|
|
28
|
+
case @magic
|
|
29
|
+
when MAGIC
|
|
30
|
+
else raise InvalidExeFormat, "Bad bFLT signature #@magic"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def set_default_values(exe)
|
|
35
|
+
@magic ||= MAGIC
|
|
36
|
+
@rev ||= 4
|
|
37
|
+
@entry ||= 0x40
|
|
38
|
+
@data_start ||= @entry + exe.text.length if exe.text
|
|
39
|
+
@data_end ||= @data_start + exe.data.data.length if exe.data
|
|
40
|
+
@bss_end ||= @data_start + exe.data.length if exe.data
|
|
41
|
+
@stack_size ||= 0x1000
|
|
42
|
+
@reloc_start ||= @data_end
|
|
43
|
+
@reloc_count ||= exe.reloc.length
|
|
44
|
+
@flags ||= []
|
|
45
|
+
|
|
46
|
+
super(exe)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def decode_word(edata = @encoded) edata.decode_imm(:u32, @endianness) end
|
|
51
|
+
def encode_word(w) Expression[w].encode(:u32, @endianness) end
|
|
52
|
+
|
|
53
|
+
def initialize(cpu = nil)
|
|
54
|
+
@endianness = cpu ? cpu.endianness : :little
|
|
55
|
+
@header = Header.new
|
|
56
|
+
@text = EncodedData.new
|
|
57
|
+
@data = EncodedData.new
|
|
58
|
+
super(cpu)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def decode_header
|
|
62
|
+
@encoded.ptr = 0
|
|
63
|
+
@header.decode(self)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def decode
|
|
67
|
+
decode_header
|
|
68
|
+
|
|
69
|
+
@encoded.ptr = @header.entry
|
|
70
|
+
@text = EncodedData.new << @encoded.read(@header.data_start - @header.entry)
|
|
71
|
+
@data = EncodedData.new << @encoded.read(@header.data_end - @header.data_start)
|
|
72
|
+
@data.virtsize += (@header.bss_end - @header.data_end)
|
|
73
|
+
|
|
74
|
+
if @header.flags.include? 'GZIP'
|
|
75
|
+
# TODO gzip
|
|
76
|
+
raise 'bFLT decoder: gzip format not supported'
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
@reloc = []
|
|
80
|
+
@encoded.ptr = @header.reloc_start
|
|
81
|
+
@header.reloc_count.times { @reloc << decode_word }
|
|
82
|
+
if @header.version == 2
|
|
83
|
+
@reloc.map! { |r| r & 0x3fff_ffff }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
decode_interpret_relocs
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def decode_interpret_relocs
|
|
90
|
+
@reloc.each { |r|
|
|
91
|
+
# where the reloc is
|
|
92
|
+
if r >= @header.entry and r < @header.data_start
|
|
93
|
+
section = @text
|
|
94
|
+
base = @header.entry
|
|
95
|
+
elsif r >= @header.data_start and r < @header.data_end
|
|
96
|
+
section = @data
|
|
97
|
+
base = @header.data_start
|
|
98
|
+
else
|
|
99
|
+
puts "out of bounds reloc at #{Expression[r]}" if $VERBOSE
|
|
100
|
+
next
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# what it points to
|
|
104
|
+
section.ptr = r-base
|
|
105
|
+
target = decode_word(section)
|
|
106
|
+
if target >= @header.entry and target < @header.data_start
|
|
107
|
+
target = label_at(@text, target - @header.entry, "xref_#{Expression[target]}")
|
|
108
|
+
elsif target >= @header.data_start and target < @header.bss_end
|
|
109
|
+
target = label_at(@data, target - @header.data_start, "xref_#{Expression[target]}")
|
|
110
|
+
else
|
|
111
|
+
puts "out of bounds reloc target at #{Expression[r]}" if $VERBOSE
|
|
112
|
+
next
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
@text.reloc[r-base] = Relocation.new(Expression[target], :u32, @endianness)
|
|
116
|
+
}
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def encode
|
|
120
|
+
create_relocation_table
|
|
121
|
+
|
|
122
|
+
# TODO got, gzip
|
|
123
|
+
if @header.flags.include? 'GZIP'
|
|
124
|
+
puts "W: bFLT: clearing gzip flag" if $VERBOSE
|
|
125
|
+
@header.flags.delete 'GZIP'
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
@encoded = EncodedData.new
|
|
129
|
+
@encoded << @header.encode(self)
|
|
130
|
+
|
|
131
|
+
binding = @text.binding(@header.entry).merge(@data.binding(@header.data_start))
|
|
132
|
+
@encoded << @text << @data.data
|
|
133
|
+
@encoded.fixup! binding
|
|
134
|
+
@encoded.reloc.clear
|
|
135
|
+
|
|
136
|
+
@relocs.each { |r| @encoded << encode_word(r) }
|
|
137
|
+
|
|
138
|
+
@encoded.data
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def create_relocation_table
|
|
142
|
+
@reloc = []
|
|
143
|
+
mapaddr = new_label('mapaddr')
|
|
144
|
+
binding = @text.binding(mapaddr).merge(@data.binding(mapaddr))
|
|
145
|
+
[@text, @data].each { |section|
|
|
146
|
+
base = @header.entry || 0x40
|
|
147
|
+
base = @header.data_start || base+@text.length if section == @data
|
|
148
|
+
section.reloc.each { |o, r|
|
|
149
|
+
if r.endianness == @endianness and [:u32, :a32, :i32].include? r.type and
|
|
150
|
+
Expression[r.target.bind(binding), :-, mapaddr].reduce.kind_of? ::Integer
|
|
151
|
+
@reloc << (base+o)
|
|
152
|
+
else
|
|
153
|
+
puts "bFLT: ignoring unsupported reloc #{r.inspect} at #{Expression[o]}" if $VERBOSE
|
|
154
|
+
end
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def parse_init
|
|
160
|
+
@textsrc ||= []
|
|
161
|
+
@datasrc ||= []
|
|
162
|
+
@cursource ||= @textsrc
|
|
163
|
+
super()
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def parse_parser_instruction(instr)
|
|
167
|
+
case instr.raw.downcase
|
|
168
|
+
when '.text'; @cursource = @textsrc
|
|
169
|
+
when '.data'; @cursource = @datasrc
|
|
170
|
+
# entrypoint is the 1st byte of .text
|
|
171
|
+
else super(instr)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def assemble(*a)
|
|
176
|
+
parse(*a) if not a.empty?
|
|
177
|
+
@text << assemble_sequence(@textsrc, @cpu)
|
|
178
|
+
@textsrc.clear
|
|
179
|
+
@data << assemble_sequence(@datasrc, @cpu)
|
|
180
|
+
@datasrc.clear
|
|
181
|
+
self
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def each_section
|
|
185
|
+
yield @text, @header.entry
|
|
186
|
+
yield @data, @header.data_start
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
@@ -0,0 +1,455 @@
|
|
|
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
|
+
|
|
9
|
+
module Metasm
|
|
10
|
+
# the COFF object file format
|
|
11
|
+
# mostly used on windows (PE/COFF)
|
|
12
|
+
class COFF < ExeFormat
|
|
13
|
+
CHARACTERISTIC_BITS = {
|
|
14
|
+
0x0001 => 'RELOCS_STRIPPED', 0x0002 => 'EXECUTABLE_IMAGE',
|
|
15
|
+
0x0004 => 'LINE_NUMS_STRIPPED', 0x0008 => 'LOCAL_SYMS_STRIPPED',
|
|
16
|
+
0x0010 => 'AGGRESSIVE_WS_TRIM', 0x0020 => 'LARGE_ADDRESS_AWARE',
|
|
17
|
+
0x0040 => 'x16BIT_MACHINE', 0x0080 => 'BYTES_REVERSED_LO',
|
|
18
|
+
0x0100 => 'x32BIT_MACHINE', 0x0200 => 'DEBUG_STRIPPED',
|
|
19
|
+
0x0400 => 'REMOVABLE_RUN_FROM_SWAP', 0x0800 => 'NET_RUN_FROM_SWAP',
|
|
20
|
+
0x1000 => 'SYSTEM', 0x2000 => 'DLL',
|
|
21
|
+
0x4000 => 'UP_SYSTEM_ONLY', 0x8000 => 'BYTES_REVERSED_HI'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
MACHINE = {
|
|
25
|
+
0x0 => 'UNKNOWN', 0x184 => 'ALPHA', 0x1c0 => 'ARM',
|
|
26
|
+
0x1d3 => 'AM33', 0x8664=> 'AMD64', 0xebc => 'EBC',
|
|
27
|
+
0x9041=> 'M32R', 0x1f1 => 'POWERPCFP',
|
|
28
|
+
0x284 => 'ALPHA64', 0x14c => 'I386', 0x200 => 'IA64',
|
|
29
|
+
0x268 => 'M68K', 0x266 => 'MIPS16', 0x366 => 'MIPSFPU',
|
|
30
|
+
0x466 => 'MIPSFPU16', 0x1f0 => 'POWERPC', 0x162 => 'R3000',
|
|
31
|
+
0x166 => 'R4000', 0x168 => 'R10000', 0x1a2 => 'SH3',
|
|
32
|
+
0x1a3 => 'SH3DSP', 0x1a6 => 'SH4', 0x1a8 => 'SH5',
|
|
33
|
+
0x1c2 => 'THUMB', 0x169 => 'WCEMIPSV2'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# PE+ is for 64bits address spaces
|
|
37
|
+
SIGNATURE = { 0x10b => 'PE', 0x20b => 'PE+', 0x107 => 'ROM' }
|
|
38
|
+
|
|
39
|
+
SUBSYSTEM = {
|
|
40
|
+
0 => 'UNKNOWN', 1 => 'NATIVE', 2 => 'WINDOWS_GUI',
|
|
41
|
+
3 => 'WINDOWS_CUI', 5 => 'OS/2_CUI', 7 => 'POSIX_CUI',
|
|
42
|
+
8 => 'WIN9X_DRIVER', 9 => 'WINDOWS_CE_GUI',
|
|
43
|
+
10 => 'EFI_APPLICATION',
|
|
44
|
+
11 => 'EFI_BOOT_SERVICE_DRIVER', 12 => 'EFI_RUNTIME_DRIVER',
|
|
45
|
+
13 => 'EFI_ROM', 14 => 'XBOX'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
DLL_CHARACTERISTIC_BITS = {
|
|
49
|
+
0x40 => 'DYNAMIC_BASE', 0x80 => 'FORCE_INTEGRITY', 0x100 => 'NX_COMPAT',
|
|
50
|
+
0x200 => 'NO_ISOLATION', 0x400 => 'NO_SEH', 0x800 => 'NO_BIND',
|
|
51
|
+
0x2000 => 'WDM_DRIVER', 0x8000 => 'TERMINAL_SERVER_AWARE'
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
BASE_RELOCATION_TYPE = { 0 => 'ABSOLUTE', 1 => 'HIGH', 2 => 'LOW', 3 => 'HIGHLOW',
|
|
55
|
+
4 => 'HIGHADJ', 5 => 'MIPS_JMPADDR', 9 => 'MIPS_JMPADDR16', 10 => 'DIR64'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
RELOCATION_TYPE = Hash.new({}).merge(
|
|
59
|
+
'AMD64' => { 0 => 'ABSOLUTE', 1 => 'ADDR64', 2 => 'ADDR32', 3 => 'ADDR32NB',
|
|
60
|
+
4 => 'REL32', 5 => 'REL32_1', 6 => 'REL32_2', 7 => 'REL32_3',
|
|
61
|
+
8 => 'REL32_4', 9 => 'REL32_5', 10 => 'SECTION', 11 => 'SECREL',
|
|
62
|
+
12 => 'SECREL7', 13 => 'TOKEN', 14 => 'SREL32', 15 => 'PAIR',
|
|
63
|
+
16 => 'SSPAN32' },
|
|
64
|
+
'ARM' => { 0 => 'ABSOLUTE', 1 => 'ADDR32', 2 => 'ADDR32NB', 3 => 'BRANCH24',
|
|
65
|
+
4 => 'BRANCH11', 14 => 'SECTION', 15 => 'SECREL' },
|
|
66
|
+
'I386' => { 0 => 'ABSOLUTE', 1 => 'DIR16', 2 => 'REL16', 6 => 'DIR32',
|
|
67
|
+
7 => 'DIR32NB', 9 => 'SEG12', 10 => 'SECTION', 11 => 'SECREL',
|
|
68
|
+
12 => 'TOKEN', 13 => 'SECREL7', 20 => 'REL32' }
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# lsb of symbol type, unused
|
|
72
|
+
SYMBOL_BTYPE = { 0 => 'NULL', 1 => 'VOID', 2 => 'CHAR', 3 => 'SHORT',
|
|
73
|
+
4 => 'INT', 5 => 'LONG', 6 => 'FLOAT', 7 => 'DOUBLE', 8 => 'STRUCT',
|
|
74
|
+
9 => 'UNION', 10 => 'ENUM', 11 => 'MOE', 12 => 'BYTE', 13 => 'WORD',
|
|
75
|
+
14 => 'UINT', 15 => 'DWORD'}
|
|
76
|
+
SYMBOL_TYPE = { 0 => 'NULL', 1 => 'POINTER', 2 => 'FUNCTION', 3 => 'ARRAY' }
|
|
77
|
+
SYMBOL_SECTION = { 0 => 'UNDEF', 0xffff => 'ABS', 0xfffe => 'DEBUG' }
|
|
78
|
+
SYMBOL_STORAGE = { 0xff => 'EOF', 0 => 'NULL', 1 => 'AUTO', 2 => 'EXTERNAL',
|
|
79
|
+
3 => 'STATIC', 4 => 'REGISTER', 5 => 'EXT_DEF', 6 => 'LABEL',
|
|
80
|
+
7 => 'UNDEF_LABEL', 8 => 'STRUCT_MEMBER', 9 => 'ARGUMENT', 10 => 'STRUCT_TAG',
|
|
81
|
+
11 => 'UNION_MEMBER', 12 => 'UNION_TAG', 13 => 'TYPEDEF', 14 => 'UNDEF_STATIC',
|
|
82
|
+
15 => 'ENUM_TAG', 16 => 'ENUM_MEMBER', 17 => 'REG_PARAM', 18 => 'BIT_FIELD',
|
|
83
|
+
100 => 'BLOCK', 101 => 'FUNCTION', 102 => 'END_STRUCT',
|
|
84
|
+
103 => 'FILE', 104 => 'SECTION', 105 => 'WEAK_EXT',
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
DEBUG_TYPE = { 0 => 'UNKNOWN', 1 => 'COFF', 2 => 'CODEVIEW', 3 => 'FPO', 4 => 'MISC',
|
|
88
|
+
5 => 'EXCEPTION', 6 => 'FIXUP', 7 => 'OMAP_TO_SRC', 8 => 'OMAP_FROM_SRC',
|
|
89
|
+
9 => 'BORLAND', 10 => 'RESERVED10', 11 => 'CLSID' }
|
|
90
|
+
|
|
91
|
+
DIRECTORIES = %w[export_table import_table resource_table exception_table certificate_table
|
|
92
|
+
base_relocation_table debug architecture global_ptr tls_table load_config
|
|
93
|
+
bound_import iat delay_import com_runtime reserved]
|
|
94
|
+
|
|
95
|
+
SECTION_CHARACTERISTIC_BITS = {
|
|
96
|
+
0x20 => 'CONTAINS_CODE', 0x40 => 'CONTAINS_DATA', 0x80 => 'CONTAINS_UDATA',
|
|
97
|
+
0x100 => 'LNK_OTHER', 0x200 => 'LNK_INFO', 0x800 => 'LNK_REMOVE',
|
|
98
|
+
0x1000 => 'LNK_COMDAT', 0x8000 => 'GPREL',
|
|
99
|
+
0x20000 => 'MEM_PURGEABLE|16BIT', 0x40000 => 'MEM_LOCKED', 0x80000 => 'MEM_PRELOAD',
|
|
100
|
+
0x100000 => 'ALIGN_1BYTES', 0x200000 => 'ALIGN_2BYTES',
|
|
101
|
+
0x300000 => 'ALIGN_4BYTES', 0x400000 => 'ALIGN_8BYTES',
|
|
102
|
+
0x500000 => 'ALIGN_16BYTES', 0x600000 => 'ALIGN_32BYTES',
|
|
103
|
+
0x700000 => 'ALIGN_64BYTES', 0x800000 => 'ALIGN_128BYTES',
|
|
104
|
+
0x900000 => 'ALIGN_256BYTES', 0xA00000 => 'ALIGN_512BYTES',
|
|
105
|
+
0xB00000 => 'ALIGN_1024BYTES', 0xC00000 => 'ALIGN_2048BYTES',
|
|
106
|
+
0xD00000 => 'ALIGN_4096BYTES', 0xE00000 => 'ALIGN_8192BYTES',
|
|
107
|
+
0x01000000 => 'LNK_NRELOC_OVFL', 0x02000000 => 'MEM_DISCARDABLE',
|
|
108
|
+
0x04000000 => 'MEM_NOT_CACHED', 0x08000000 => 'MEM_NOT_PAGED',
|
|
109
|
+
0x10000000 => 'MEM_SHARED', 0x20000000 => 'MEM_EXECUTE',
|
|
110
|
+
0x40000000 => 'MEM_READ', 0x80000000 => 'MEM_WRITE'
|
|
111
|
+
}
|
|
112
|
+
# NRELOC_OVFL means there are more than 0xffff reloc
|
|
113
|
+
# the reloc count must be set to 0xffff, and the real reloc count
|
|
114
|
+
# is the VA of the first relocation
|
|
115
|
+
|
|
116
|
+
ORDINAL_REGEX = /^Ordinal_(\d+)$/
|
|
117
|
+
|
|
118
|
+
COMIMAGE_FLAGS = {
|
|
119
|
+
1 => 'ILONLY', 2 => '32BITREQUIRED', 4 => 'IL_LIBRARY',
|
|
120
|
+
8 => 'STRONGNAMESIGNED', 16 => 'NATIVE_ENTRYPOINT',
|
|
121
|
+
0x10000 => 'TRACKDEBUGDATA'
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
class SerialStruct < Metasm::SerialStruct
|
|
125
|
+
new_int_field :xword
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
class Header < SerialStruct
|
|
129
|
+
half :machine, 'I386', MACHINE
|
|
130
|
+
half :num_sect
|
|
131
|
+
words :time, :ptr_sym, :num_sym
|
|
132
|
+
half :size_opthdr
|
|
133
|
+
half :characteristics
|
|
134
|
+
fld_bits :characteristics, CHARACTERISTIC_BITS
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# present in linked files (exe/dll/kmod)
|
|
138
|
+
class OptionalHeader < SerialStruct
|
|
139
|
+
half :signature, 'PE', SIGNATURE
|
|
140
|
+
bytes :link_ver_maj, :link_ver_min
|
|
141
|
+
words :code_size, :data_size, :udata_size, :entrypoint, :base_of_code
|
|
142
|
+
# base_of_data does not exist in 64-bit
|
|
143
|
+
new_field(:base_of_data, lambda { |exe, hdr| exe.decode_word if exe.bitsize != 64 }, lambda { |exe, hdr, val| exe.encode_word(val) if exe.bitsize != 64 }, 0)
|
|
144
|
+
# NT-specific fields
|
|
145
|
+
xword :image_base
|
|
146
|
+
words :sect_align, :file_align
|
|
147
|
+
halfs :os_ver_maj, :os_ver_min, :img_ver_maj, :img_ver_min, :subsys_maj, :subsys_min
|
|
148
|
+
words :reserved, :image_size, :headers_size, :checksum
|
|
149
|
+
half :subsystem, 0, SUBSYSTEM
|
|
150
|
+
half :dll_characts
|
|
151
|
+
fld_bits :dll_characts, DLL_CHARACTERISTIC_BITS
|
|
152
|
+
xwords :stack_reserve, :stack_commit, :heap_reserve, :heap_commit
|
|
153
|
+
words :ldrflags, :numrva
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# COFF relocatable object symbol (table offset found in the Header.ptr_sym)
|
|
157
|
+
class Symbol < SerialStruct
|
|
158
|
+
str :name, 8 # if the 1st 4 bytes are 0, the word at 4...8 is the name index in the string table
|
|
159
|
+
word :value
|
|
160
|
+
half :sec_nr
|
|
161
|
+
fld_enum :sec_nr, SYMBOL_SECTION
|
|
162
|
+
bitfield :half, 0 => :type_base, 4 => :type
|
|
163
|
+
fld_enum :type_base, SYMBOL_BTYPE
|
|
164
|
+
fld_enum :type, SYMBOL_TYPE
|
|
165
|
+
bytes :storage, :nr_aux
|
|
166
|
+
fld_enum :storage, SYMBOL_STORAGE
|
|
167
|
+
|
|
168
|
+
attr_accessor :aux
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
class Section < SerialStruct
|
|
172
|
+
str :name, 8
|
|
173
|
+
words :virtsize, :virtaddr, :rawsize, :rawaddr, :relocaddr, :linenoaddr
|
|
174
|
+
halfs :relocnr, :linenonr
|
|
175
|
+
word :characteristics
|
|
176
|
+
fld_bits :characteristics, SECTION_CHARACTERISTIC_BITS
|
|
177
|
+
|
|
178
|
+
attr_accessor :encoded, :relocs
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# COFF relocatable object relocation (per section, see relocaddr/relocnr)
|
|
182
|
+
class RelocObj < SerialStruct
|
|
183
|
+
word :va
|
|
184
|
+
word :symidx
|
|
185
|
+
half :type
|
|
186
|
+
fld_enum(:type) { |coff, rel| RELOCATION_TYPE[coff.header.machine] || {} }
|
|
187
|
+
attr_accessor :sym
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# lists the functions/addresses exported to the OS (pendant of ImportDirectory)
|
|
191
|
+
class ExportDirectory < SerialStruct
|
|
192
|
+
words :reserved, :timestamp
|
|
193
|
+
halfs :version_major, :version_minor
|
|
194
|
+
words :libname_p, :ordinal_base, :num_exports, :num_names, :func_p, :names_p, :ord_p
|
|
195
|
+
attr_accessor :libname, :exports
|
|
196
|
+
|
|
197
|
+
class Export
|
|
198
|
+
attr_accessor :forwarder_lib, :forwarder_ordinal, :forwarder_name, :target, :target_rva, :name_p, :name, :ordinal
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# contains the name of dynamic libraries required by the program, and the function to import from them
|
|
203
|
+
class ImportDirectory < SerialStruct
|
|
204
|
+
words :ilt_p, :timestamp, :firstforwarder, :libname_p, :iat_p
|
|
205
|
+
fld_default :firstforwarder, 0xffff_ffff
|
|
206
|
+
attr_accessor :libname, :imports, :iat
|
|
207
|
+
|
|
208
|
+
class Import
|
|
209
|
+
attr_accessor :ordinal, :hint, :hintname_p, :name, :target, :thunk
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# tree-like structure, holds all misc data the program might need (icons, cursors, version information)
|
|
214
|
+
# conventionnally structured in a 3-level depth structure:
|
|
215
|
+
# I resource type (icon/cursor/etc, see +TYPES+)
|
|
216
|
+
# II resource id (icon n1, icon 'toto', ...)
|
|
217
|
+
# III language-specific version (icon n1 en, icon n1 en-dvorak...)
|
|
218
|
+
class ResourceDirectory < SerialStruct
|
|
219
|
+
words :characteristics, :timestamp
|
|
220
|
+
halfs :major_version, :minor_version, :nr_names, :nr_id
|
|
221
|
+
attr_accessor :entries
|
|
222
|
+
attr_accessor :curoff_label # internal use, in encoder
|
|
223
|
+
|
|
224
|
+
class Entry
|
|
225
|
+
attr_accessor :name_p, :name, :name_w,
|
|
226
|
+
:id, :subdir_p, :subdir, :dataentry_p,
|
|
227
|
+
:data_p, :data, :codepage, :reserved
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# array of relocations to apply to an executable file
|
|
232
|
+
# when it is loaded at an address that is not its preferred_base_address
|
|
233
|
+
class RelocationTable < SerialStruct
|
|
234
|
+
word :base_addr
|
|
235
|
+
attr_accessor :relocs
|
|
236
|
+
|
|
237
|
+
class Relocation < SerialStruct
|
|
238
|
+
bitfield :half, 0 => :offset, 12 => :type
|
|
239
|
+
fld_enum :type, BASE_RELOCATION_TYPE
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
class DebugDirectory < SerialStruct
|
|
244
|
+
words :characteristics, :timestamp
|
|
245
|
+
halfs :major_version, :minor_version
|
|
246
|
+
words :type, :size_of_data, :addr, :pointer
|
|
247
|
+
fld_enum :type, DEBUG_TYPE
|
|
248
|
+
|
|
249
|
+
attr_accessor :data
|
|
250
|
+
|
|
251
|
+
class NB10 < SerialStruct
|
|
252
|
+
word :offset
|
|
253
|
+
word :signature
|
|
254
|
+
word :age
|
|
255
|
+
strz :pdbfilename
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
class RSDS < SerialStruct
|
|
259
|
+
mem :guid, 16
|
|
260
|
+
word :age
|
|
261
|
+
strz :pdbfilename
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
class TLSDirectory < SerialStruct
|
|
266
|
+
xwords :start_va, :end_va, :index_addr, :callback_p
|
|
267
|
+
words :zerofill_sz, :characteristics
|
|
268
|
+
|
|
269
|
+
attr_accessor :callbacks
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# the 'load configuration' directory (used for SafeSEH)
|
|
273
|
+
class LoadConfig < SerialStruct
|
|
274
|
+
words :signature, :timestamp
|
|
275
|
+
halfs :major_version, :minor_version
|
|
276
|
+
words :globalflags_clear, :globalflags_set, :critsec_timeout
|
|
277
|
+
# lockpfxtable is an array of VA of LOCK prefixes, to be nopped on singleproc machines (!)
|
|
278
|
+
xwords :decommitblock, :decommittotal, :lockpfxtable, :maxalloc, :maxvirtmem, :process_affinity_mask
|
|
279
|
+
word :process_heap_flags
|
|
280
|
+
halfs :service_pack_id, :reserved
|
|
281
|
+
xwords :editlist, :security_cookie, :sehtable_p, :sehcount
|
|
282
|
+
|
|
283
|
+
attr_accessor :safeseh
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
class DelayImportDirectory < SerialStruct
|
|
287
|
+
words :attributes, :libname_p, :handle_p, :iat_p, :int_p, :biat_p, :uiat_p, :timestamp
|
|
288
|
+
|
|
289
|
+
attr_accessor :libname
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# structure defining entrypoints and stuff for .net binaries
|
|
293
|
+
class Cor20Header < SerialStruct
|
|
294
|
+
word :size
|
|
295
|
+
halfs :major_version, :minor_version # runtime version
|
|
296
|
+
words :metadata_rva, :metadata_sz
|
|
297
|
+
word :flags
|
|
298
|
+
fld_bits :flags, COMIMAGE_FLAGS
|
|
299
|
+
word :entrypoint # RVA to native or managed ep, depending on flags
|
|
300
|
+
words :resources_rva, :resources_sz
|
|
301
|
+
words :strongnamesig_rva, :strongnamesig_sz
|
|
302
|
+
words :codemgr_rva, :codemgr_sz
|
|
303
|
+
words :vtfixup_rva, :vtfixup_sz
|
|
304
|
+
words :eatjumps_rva, :eatjumps_sz
|
|
305
|
+
words :managednativehdr_rva, :managednativehdr_sz
|
|
306
|
+
|
|
307
|
+
attr_accessor :metadata, :resources, :strongnamesig, :codemgr, :vtfixup, :eatjumps, :managednativehdr
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# for the icon, the one that appears in the explorer is
|
|
311
|
+
# (NT) the one with the lowest ID
|
|
312
|
+
# (98) the first to appear in the table
|
|
313
|
+
class ResourceDirectory
|
|
314
|
+
def to_hash(depth=0)
|
|
315
|
+
map = case depth
|
|
316
|
+
when 0; TYPE
|
|
317
|
+
when 1; {} # resource-id
|
|
318
|
+
when 2; {} # lang
|
|
319
|
+
else {}
|
|
320
|
+
end
|
|
321
|
+
@entries.inject({}) { |h, e|
|
|
322
|
+
k = e.id ? map.fetch(e.id, e.id) : e.name ? e.name : e.name_w
|
|
323
|
+
v = e.subdir ? e.subdir.to_hash(depth+1) : e.data
|
|
324
|
+
h.update k => v
|
|
325
|
+
}
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def self.from_hash(h, depth=0)
|
|
329
|
+
map = case depth
|
|
330
|
+
when 0; TYPE
|
|
331
|
+
when 1; {} # resource-id
|
|
332
|
+
when 2; {} # lang
|
|
333
|
+
else {}
|
|
334
|
+
end
|
|
335
|
+
ret = new
|
|
336
|
+
ret.entries = h.map { |k, v|
|
|
337
|
+
e = Entry.new
|
|
338
|
+
k.kind_of?(Integer) ? (e.id = k) : map.index(k) ? (e.id = map.index(k)) : (e.name = k) # name_w ?
|
|
339
|
+
v.kind_of?(Hash) ? (e.subdir = from_hash(v, depth+1)) : (e.data = v)
|
|
340
|
+
e
|
|
341
|
+
}
|
|
342
|
+
ret
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# returns a string with the to_hash key tree
|
|
346
|
+
def to_s
|
|
347
|
+
to_s_a(0).join("\n")
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def to_s_a(depth)
|
|
351
|
+
@entries.map { |e|
|
|
352
|
+
ar = []
|
|
353
|
+
ar << if e.id
|
|
354
|
+
if depth == 0 and TYPE.has_key?(e.id); "#{e.id.to_s} (#{TYPE[e.id]})".ljust(18)
|
|
355
|
+
else e.id.to_s.ljust(5)
|
|
356
|
+
end
|
|
357
|
+
else (e.name || e.name_w).inspect
|
|
358
|
+
end
|
|
359
|
+
if e.subdir
|
|
360
|
+
sa = e.subdir.to_s_a(depth+1)
|
|
361
|
+
if sa.length == 1
|
|
362
|
+
ar.last << " | #{sa.first}"
|
|
363
|
+
else
|
|
364
|
+
ar << sa.map { |s| ' ' + s }
|
|
365
|
+
end
|
|
366
|
+
elsif e.data.length > 16
|
|
367
|
+
ar.last << " #{e.data[0, 8].inspect}... <#{e.data.length} bytes>"
|
|
368
|
+
else
|
|
369
|
+
ar.last << ' ' << e.data.inspect
|
|
370
|
+
end
|
|
371
|
+
ar
|
|
372
|
+
}.flatten
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
TYPE = {
|
|
376
|
+
1 => 'CURSOR', 2 => 'BITMAP', 3 => 'ICON', 4 => 'MENU',
|
|
377
|
+
5 => 'DIALOG', 6 => 'STRING', 7 => 'FONTDIR', 8 => 'FONT',
|
|
378
|
+
9 => 'ACCELERATOR', 10 => 'RCADATA', 11 => 'MESSAGETABLE',
|
|
379
|
+
12 => 'GROUP_CURSOR', 14 => 'GROUP_ICON', 16 => 'VERSION',
|
|
380
|
+
17 => 'DLGINCLUDE', 19 => 'PLUGPLAY', 20 => 'VXD',
|
|
381
|
+
21 => 'ANICURSOR', 22 => 'ANIICON', 23 => 'HTML',
|
|
382
|
+
24 => 'MANIFEST'
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
ACCELERATOR_BITS = {
|
|
386
|
+
1 => 'VIRTKEY', 2 => 'NOINVERT', 4 => 'SHIFT', 8 => 'CTRL',
|
|
387
|
+
16 => 'ALT', 128 => 'LAST'
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
# cursor = raw data, cursor_group = header , pareil pour les icons
|
|
391
|
+
class Cursor
|
|
392
|
+
attr_accessor :xhotspot, :yhotspot, :data
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
attr_accessor :header, :optheader, :directory, :sections, :endianness, :symbols, :bitsize,
|
|
397
|
+
:export, :imports, :resource, :certificates, :relocations, :debug, :tls, :loadconfig, :delayimports, :com_header
|
|
398
|
+
|
|
399
|
+
# boolean, set to true to have #decode() ignore the base_relocs directory
|
|
400
|
+
attr_accessor :nodecode_relocs
|
|
401
|
+
|
|
402
|
+
def initialize(*a)
|
|
403
|
+
cpu = a.grep(CPU).first
|
|
404
|
+
@nodecode_relocs = true if a.include? :nodecode_relocs
|
|
405
|
+
|
|
406
|
+
@directory = {} # DIRECTORIES.key => [rva, size]
|
|
407
|
+
@sections = []
|
|
408
|
+
@endianness = cpu ? cpu.endianness : :little
|
|
409
|
+
@bitsize = cpu ? cpu.size : 32
|
|
410
|
+
@header = Header.new
|
|
411
|
+
@optheader = OptionalHeader.new
|
|
412
|
+
super(cpu)
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def shortname; 'coff'; end
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
# the COFF archive file format
|
|
419
|
+
# maybe used in .lib files (they hold binary import information for libraries)
|
|
420
|
+
# used for unix .a static library files (with no 2nd linker and newline-separated longnames)
|
|
421
|
+
class COFFArchive < ExeFormat
|
|
422
|
+
class Member < SerialStruct
|
|
423
|
+
mem :name, 16
|
|
424
|
+
mem :date, 12
|
|
425
|
+
mem :uid, 6
|
|
426
|
+
mem :gid, 6
|
|
427
|
+
mem :mode, 8
|
|
428
|
+
mem :size, 10
|
|
429
|
+
mem :eoh, 2
|
|
430
|
+
|
|
431
|
+
attr_accessor :offset, :encoded
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
class ImportHeader < SerialStruct
|
|
435
|
+
halfs :sig1, :sig2, :version, :machine
|
|
436
|
+
words :timestamp, :size_of_data
|
|
437
|
+
half :hint
|
|
438
|
+
bitfield :half, 0 => :reserved, 11 => :name_type, 14 => :type
|
|
439
|
+
#fld_enum :type, IMPORT_TYPE
|
|
440
|
+
#fld_enum :name_type, NAME_TYPE
|
|
441
|
+
strz :symname
|
|
442
|
+
strz :libname
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
attr_accessor :members, :signature, :first_linker, :second_linker, :longnames
|
|
446
|
+
|
|
447
|
+
# return the 1st member whose name is name
|
|
448
|
+
def member(name)
|
|
449
|
+
@members.find { |m| m.name == name }
|
|
450
|
+
end
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
require 'metasm/exe_format/coff_encode'
|
|
455
|
+
require 'metasm/exe_format/coff_decode'
|