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,901 @@
|
|
|
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/decode'
|
|
8
|
+
require 'metasm/exe_format/coff' unless defined? Metasm::COFF
|
|
9
|
+
|
|
10
|
+
module Metasm
|
|
11
|
+
class COFF
|
|
12
|
+
class OptionalHeader
|
|
13
|
+
decode_hook(:entrypoint) { |coff, ohdr|
|
|
14
|
+
coff.bitsize = (ohdr.signature == 'PE+' ? 64 : 32)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# decodes a COFF optional header from coff.cursection
|
|
18
|
+
# also decodes directories in coff.directory
|
|
19
|
+
def decode(coff)
|
|
20
|
+
return set_default_values(coff) if coff.header.size_opthdr == 0
|
|
21
|
+
super(coff)
|
|
22
|
+
|
|
23
|
+
nrva = @numrva
|
|
24
|
+
if @numrva > DIRECTORIES.length
|
|
25
|
+
puts "W: COFF: Invalid directories count #{@numrva}" if $VERBOSE
|
|
26
|
+
nrva = DIRECTORIES.length
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
coff.directory = {}
|
|
30
|
+
DIRECTORIES[0, nrva].each { |dir|
|
|
31
|
+
rva = coff.decode_word
|
|
32
|
+
sz = coff.decode_word
|
|
33
|
+
if rva != 0 or sz != 0
|
|
34
|
+
coff.directory[dir] = [rva, sz]
|
|
35
|
+
end
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Symbol
|
|
41
|
+
def decode(coff, strtab='')
|
|
42
|
+
n0, n1 = coff.decode_word, coff.decode_word
|
|
43
|
+
coff.encoded.ptr -= 8
|
|
44
|
+
|
|
45
|
+
super(coff)
|
|
46
|
+
|
|
47
|
+
if n0 == 0 and ne = strtab.index(?\0, n1)
|
|
48
|
+
@name = strtab[n1...ne]
|
|
49
|
+
end
|
|
50
|
+
return if @nr_aux == 0
|
|
51
|
+
|
|
52
|
+
@aux = []
|
|
53
|
+
@nr_aux.times { @aux << coff.encoded.read(18) }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class Section
|
|
58
|
+
def decode(coff)
|
|
59
|
+
super(coff)
|
|
60
|
+
coff.decode_section_body(self)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class RelocObj
|
|
65
|
+
def decode(coff)
|
|
66
|
+
super(coff)
|
|
67
|
+
@sym = coff.symbols[@symidx]
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
class ExportDirectory
|
|
72
|
+
# decodes a COFF export table from coff.cursection
|
|
73
|
+
def decode(coff)
|
|
74
|
+
super(coff)
|
|
75
|
+
|
|
76
|
+
if coff.sect_at_rva(@libname_p)
|
|
77
|
+
@libname = coff.decode_strz
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
if coff.sect_at_rva(@func_p)
|
|
81
|
+
@exports = []
|
|
82
|
+
addrs = []
|
|
83
|
+
@num_exports.times { addrs << coff.decode_word }
|
|
84
|
+
@num_exports.times { |i|
|
|
85
|
+
e = Export.new
|
|
86
|
+
e.ordinal = i + @ordinal_base
|
|
87
|
+
addr = addrs[i]
|
|
88
|
+
if addr >= coff.directory['export_table'][0] and addr < coff.directory['export_table'][0] + coff.directory['export_table'][1] and coff.sect_at_rva(addr)
|
|
89
|
+
name = coff.decode_strz
|
|
90
|
+
e.forwarder_lib, name = name.split('.', 2)
|
|
91
|
+
if name[0] == ?#
|
|
92
|
+
e.forwarder_ordinal = name[1..-1].to_i
|
|
93
|
+
else
|
|
94
|
+
e.forwarder_name = name
|
|
95
|
+
end
|
|
96
|
+
else
|
|
97
|
+
e.target = e.target_rva = addr
|
|
98
|
+
end
|
|
99
|
+
@exports << e
|
|
100
|
+
}
|
|
101
|
+
end
|
|
102
|
+
if coff.sect_at_rva(@names_p)
|
|
103
|
+
namep = []
|
|
104
|
+
num_names.times { namep << coff.decode_word }
|
|
105
|
+
end
|
|
106
|
+
if coff.sect_at_rva(@ord_p)
|
|
107
|
+
ords = []
|
|
108
|
+
num_names.times { ords << coff.decode_half }
|
|
109
|
+
end
|
|
110
|
+
if namep and ords
|
|
111
|
+
namep.zip(ords).each { |np, oi|
|
|
112
|
+
@exports[oi].name_p = np
|
|
113
|
+
if coff.sect_at_rva(np)
|
|
114
|
+
@exports[oi].name = coff.decode_strz
|
|
115
|
+
end
|
|
116
|
+
}
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
class ImportDirectory
|
|
122
|
+
# decodes all COFF import directories from coff.cursection
|
|
123
|
+
def self.decode_all(coff)
|
|
124
|
+
ret = []
|
|
125
|
+
loop do
|
|
126
|
+
idata = decode(coff)
|
|
127
|
+
break if [idata.ilt_p, idata.libname_p].uniq == [0]
|
|
128
|
+
ret << idata
|
|
129
|
+
end
|
|
130
|
+
ret.each { |idata| idata.decode_inner(coff) }
|
|
131
|
+
ret
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# decode the tables referenced
|
|
135
|
+
def decode_inner(coff)
|
|
136
|
+
if coff.sect_at_rva(@libname_p)
|
|
137
|
+
@libname = coff.decode_strz
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
if coff.sect_at_rva(@ilt_p) || coff.sect_at_rva(@iat_p)
|
|
141
|
+
addrs = []
|
|
142
|
+
while (a_ = coff.decode_xword) != 0
|
|
143
|
+
addrs << a_
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
@imports = []
|
|
147
|
+
|
|
148
|
+
ord_mask = 1 << (coff.bitsize-1)
|
|
149
|
+
addrs.each { |a|
|
|
150
|
+
i = Import.new
|
|
151
|
+
if (a & ord_mask) != 0
|
|
152
|
+
i.ordinal = a & (~ord_mask)
|
|
153
|
+
else
|
|
154
|
+
i.hintname_p = a
|
|
155
|
+
if coff.sect_at_rva(a)
|
|
156
|
+
i.hint = coff.decode_half
|
|
157
|
+
i.name = coff.decode_strz
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
@imports << i
|
|
161
|
+
}
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
if coff.sect_at_rva(@iat_p)
|
|
165
|
+
@iat = []
|
|
166
|
+
while (a = coff.decode_xword) != 0
|
|
167
|
+
@iat << a
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
class ResourceDirectory
|
|
174
|
+
def decode(coff, edata = coff.curencoded, startptr = edata.ptr)
|
|
175
|
+
super(coff, edata)
|
|
176
|
+
|
|
177
|
+
@entries = []
|
|
178
|
+
|
|
179
|
+
nrnames = @nr_names if $DEBUG
|
|
180
|
+
(@nr_names+@nr_id).times {
|
|
181
|
+
e = Entry.new
|
|
182
|
+
|
|
183
|
+
e_id = coff.decode_word(edata)
|
|
184
|
+
e_ptr = coff.decode_word(edata)
|
|
185
|
+
|
|
186
|
+
if not e_id.kind_of? Integer or not e_ptr.kind_of? Integer
|
|
187
|
+
puts 'W: COFF: relocs in the rsrc directory?' if $VERBOSE
|
|
188
|
+
next
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
tmp = edata.ptr
|
|
192
|
+
|
|
193
|
+
if (e_id >> 31) == 1
|
|
194
|
+
if $DEBUG
|
|
195
|
+
nrnames -= 1
|
|
196
|
+
puts "W: COFF: rsrc has invalid id #{e_id}" if nrnames < 0
|
|
197
|
+
end
|
|
198
|
+
e.name_p = e_id & 0x7fff_ffff
|
|
199
|
+
edata.ptr = startptr + e.name_p
|
|
200
|
+
namelen = coff.decode_half(edata)
|
|
201
|
+
e.name_w = edata.read(2*namelen)
|
|
202
|
+
if (chrs = e.name_w.unpack('v*')).all? { |c| c >= 0 and c <= 255 }
|
|
203
|
+
e.name = chrs.pack('C*')
|
|
204
|
+
end
|
|
205
|
+
else
|
|
206
|
+
if $DEBUG
|
|
207
|
+
puts "W: COFF: rsrc has invalid id #{e_id}" if nrnames > 0
|
|
208
|
+
end
|
|
209
|
+
e.id = e_id
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
if (e_ptr >> 31) == 1 # subdir
|
|
213
|
+
e.subdir_p = e_ptr & 0x7fff_ffff
|
|
214
|
+
if startptr + e.subdir_p >= edata.length
|
|
215
|
+
puts 'W: COFF: invalid resource structure: directory too far' if $VERBOSE
|
|
216
|
+
else
|
|
217
|
+
edata.ptr = startptr + e.subdir_p
|
|
218
|
+
e.subdir = ResourceDirectory.new
|
|
219
|
+
e.subdir.decode coff, edata, startptr
|
|
220
|
+
end
|
|
221
|
+
else
|
|
222
|
+
e.dataentry_p = e_ptr
|
|
223
|
+
edata.ptr = startptr + e.dataentry_p
|
|
224
|
+
e.data_p = coff.decode_word(edata)
|
|
225
|
+
sz = coff.decode_word(edata)
|
|
226
|
+
e.codepage = coff.decode_word(edata)
|
|
227
|
+
e.reserved = coff.decode_word(edata)
|
|
228
|
+
|
|
229
|
+
if coff.sect_at_rva(e.data_p)
|
|
230
|
+
e.data = coff.curencoded.read(sz)
|
|
231
|
+
else
|
|
232
|
+
puts 'W: COFF: invalid resource body offset' if $VERBOSE
|
|
233
|
+
break
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
edata.ptr = tmp
|
|
238
|
+
@entries << e
|
|
239
|
+
}
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def decode_version(coff, lang=nil)
|
|
243
|
+
vers = {}
|
|
244
|
+
|
|
245
|
+
decode_tllv = lambda { |ed, state|
|
|
246
|
+
sptr = ed.ptr
|
|
247
|
+
len, vlen, type = coff.decode_half(ed), coff.decode_half(ed), coff.decode_half(ed)
|
|
248
|
+
tagname = ''
|
|
249
|
+
while c = coff.decode_half(ed) and c != 0
|
|
250
|
+
tagname << (c&255)
|
|
251
|
+
end
|
|
252
|
+
ed.ptr = (ed.ptr + 3) / 4 * 4
|
|
253
|
+
|
|
254
|
+
case state
|
|
255
|
+
when 0
|
|
256
|
+
raise if tagname != 'VS_VERSION_INFO'
|
|
257
|
+
dat = ed.read(vlen)
|
|
258
|
+
dat.unpack('V*').zip([:signature, :strucversion, :fileversionm, :fileversionl, :prodversionm, :prodversionl, :fileflagsmask, :fileflags, :fileos, :filetype, :filesubtype, :filedatem, :filedatel]) { |v, k| vers[k] = v }
|
|
259
|
+
raise if vers[:signature] != 0xfeef04bd
|
|
260
|
+
vers.delete :signature
|
|
261
|
+
vers[:fileversion] = (vers.delete(:fileversionm) << 32) | vers.delete(:fileversionl)
|
|
262
|
+
vers[:prodversion] = (vers.delete(:prodversionm) << 32) | vers.delete(:prodversionl)
|
|
263
|
+
vers[:filedate] = (vers.delete(:filedatem) << 32) | vers.delete(:filedatel)
|
|
264
|
+
nstate = 1
|
|
265
|
+
when 1
|
|
266
|
+
nstate = case tagname
|
|
267
|
+
when 'StringFileInfo'; :strtable
|
|
268
|
+
when 'VarFileInfo'; :var
|
|
269
|
+
else raise
|
|
270
|
+
end
|
|
271
|
+
when :strtable
|
|
272
|
+
nstate = :str
|
|
273
|
+
when :str
|
|
274
|
+
val = ed.read(vlen*2).unpack('v*')
|
|
275
|
+
val.pop if val[-1] == 0
|
|
276
|
+
val = val.pack('C*') if val.all? { |c_| c_ > 0 and c_ < 256 }
|
|
277
|
+
vers[tagname] = val
|
|
278
|
+
when :var
|
|
279
|
+
val = ed.read(vlen).unpack('V*')
|
|
280
|
+
vers[tagname] = val
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
ed.ptr = (ed.ptr + 3) / 4 * 4
|
|
284
|
+
len = ed.length-sptr if len > ed.length-sptr
|
|
285
|
+
while ed.ptr < sptr+len
|
|
286
|
+
decode_tllv[ed, nstate]
|
|
287
|
+
ed.ptr = (ed.ptr + 3) / 4 * 4
|
|
288
|
+
end
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return if not e = @entries.find { |e_| e_.id == TYPE.index('VERSION') }
|
|
292
|
+
e = e.subdir.entries.first.subdir
|
|
293
|
+
e = e.entries.find { |e_| e_.id == lang } || e.entries.first
|
|
294
|
+
ed = EncodedData.new(e.data)
|
|
295
|
+
decode_tllv[ed, 0]
|
|
296
|
+
|
|
297
|
+
vers
|
|
298
|
+
#rescue
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
class RelocationTable
|
|
303
|
+
# decodes a relocation table from coff.encoded.ptr
|
|
304
|
+
def decode(coff)
|
|
305
|
+
super(coff)
|
|
306
|
+
len = coff.decode_word
|
|
307
|
+
len -= 8
|
|
308
|
+
if len < 0 or len % 2 != 0
|
|
309
|
+
puts "W: COFF: Invalid relocation table length #{len+8}" if $VERBOSE
|
|
310
|
+
coff.curencoded.read(len) if len > 0
|
|
311
|
+
@relocs = []
|
|
312
|
+
return
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
@relocs = coff.curencoded.read(len).unpack(coff.endianness == :big ? 'n*' : 'v*').map { |r| Relocation.new(r&0xfff, r>>12) }
|
|
316
|
+
#(len/2).times { @relocs << Relocation.decode(coff) } # tables may be big, this is too slow
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
class TLSDirectory
|
|
321
|
+
def decode(coff)
|
|
322
|
+
super(coff)
|
|
323
|
+
|
|
324
|
+
if coff.sect_at_va(@callback_p)
|
|
325
|
+
@callbacks = []
|
|
326
|
+
while (ptr = coff.decode_xword) != 0
|
|
327
|
+
# __stdcall void (*ptr)(void* dllhandle, dword reason, void* reserved)
|
|
328
|
+
# (same as dll entrypoint)
|
|
329
|
+
@callbacks << (ptr - coff.optheader.image_base)
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
class LoadConfig
|
|
336
|
+
def decode(coff)
|
|
337
|
+
super(coff)
|
|
338
|
+
|
|
339
|
+
if @sehcount >= 0 and @sehcount < 100 and (@signature == 0x40 or @signature == 0x48) and coff.sect_at_va(@sehtable_p)
|
|
340
|
+
@safeseh = []
|
|
341
|
+
@sehcount.times { @safeseh << coff.decode_xword }
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
class DelayImportDirectory
|
|
347
|
+
def self.decode_all(coff)
|
|
348
|
+
ret = []
|
|
349
|
+
loop do
|
|
350
|
+
didata = decode(coff)
|
|
351
|
+
break if [didata.libname_p, didata.handle_p, didata.iat_p].uniq == [0]
|
|
352
|
+
ret << didata
|
|
353
|
+
end
|
|
354
|
+
ret.each { |didata| didata.decode_inner(coff) }
|
|
355
|
+
ret
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def decode_inner(coff)
|
|
359
|
+
if coff.sect_at_rva(@libname_p)
|
|
360
|
+
@libname = coff.decode_strz
|
|
361
|
+
end
|
|
362
|
+
# TODO
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
class Cor20Header
|
|
367
|
+
def decode_all(coff)
|
|
368
|
+
if coff.sect_at_rva(@metadata_rva)
|
|
369
|
+
@metadata = coff.curencoded.read(@metadata_sz)
|
|
370
|
+
end
|
|
371
|
+
if coff.sect_at_rva(@resources_rva)
|
|
372
|
+
@resources = coff.curencoded.read(@resources_sz)
|
|
373
|
+
end
|
|
374
|
+
if coff.sect_at_rva(@strongnamesig_rva)
|
|
375
|
+
@strongnamesig = coff.curencoded.read(@strongnamesig_sz)
|
|
376
|
+
end
|
|
377
|
+
if coff.sect_at_rva(@codemgr_rva)
|
|
378
|
+
@codemgr = coff.curencoded.read(@codemgr_sz)
|
|
379
|
+
end
|
|
380
|
+
if coff.sect_at_rva(@vtfixup_rva)
|
|
381
|
+
@vtfixup = coff.curencoded.read(@vtfixup_sz)
|
|
382
|
+
end
|
|
383
|
+
if coff.sect_at_rva(@eatjumps_rva)
|
|
384
|
+
@eatjumps = coff.curencoded.read(@eatjumps_sz)
|
|
385
|
+
end
|
|
386
|
+
if coff.sect_at_rva(@managednativehdr_rva)
|
|
387
|
+
@managednativehdr = coff.curencoded.read(@managednativehdr_sz)
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
class DebugDirectory
|
|
393
|
+
def decode_inner(coff)
|
|
394
|
+
case @type
|
|
395
|
+
when 'CODEVIEW'
|
|
396
|
+
# XXX what is @pointer?
|
|
397
|
+
return if not coff.sect_at_rva(@addr)
|
|
398
|
+
sig = coff.curencoded.read(4)
|
|
399
|
+
case sig
|
|
400
|
+
when 'NB09' # CodeView 4.10
|
|
401
|
+
when 'NB10' # external pdb2.0
|
|
402
|
+
@data = NB10.decode(coff)
|
|
403
|
+
when 'NB11' # CodeView 5.0
|
|
404
|
+
when 'RSDS' # external pdb7.0
|
|
405
|
+
@data = RSDS.decode(coff)
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
attr_accessor :cursection
|
|
412
|
+
def curencoded
|
|
413
|
+
@cursection.encoded
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
def decode_byte( edata = curencoded) ; edata.decode_imm(:u8, @endianness) end
|
|
417
|
+
def decode_half( edata = curencoded) ; edata.decode_imm(:u16, @endianness) end
|
|
418
|
+
def decode_word( edata = curencoded) ; edata.decode_imm(:u32, @endianness) end
|
|
419
|
+
def decode_xword(edata = curencoded) ; edata.decode_imm((@bitsize == 32 ? :u32 : :u64), @endianness) end
|
|
420
|
+
def decode_strz( edata = curencoded) ; super(edata) ; end
|
|
421
|
+
|
|
422
|
+
# converts an RVA (offset from base address of file when loaded in memory) to the section containing it using the section table
|
|
423
|
+
# updates @cursection and @cursection.encoded.ptr to point to the specified address
|
|
424
|
+
# may return self when rva points to the coff header
|
|
425
|
+
# returns nil if none match, 0 never matches
|
|
426
|
+
def sect_at_rva(rva)
|
|
427
|
+
return if not rva or rva <= 0
|
|
428
|
+
if sections and not @sections.empty?
|
|
429
|
+
valign = lambda { |l| EncodedData.align_size(l, @optheader.sect_align) }
|
|
430
|
+
if s = @sections.find { |s_| s_.virtaddr <= rva and s_.virtaddr + valign[s_.virtsize] > rva }
|
|
431
|
+
s.encoded.ptr = rva - s.virtaddr
|
|
432
|
+
@cursection = s
|
|
433
|
+
elsif rva < @sections.map { |s_| s_.virtaddr }.min
|
|
434
|
+
@encoded.ptr = rva
|
|
435
|
+
@cursection = self
|
|
436
|
+
end
|
|
437
|
+
elsif rva <= @encoded.length
|
|
438
|
+
@encoded.ptr = rva
|
|
439
|
+
@cursection = self
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
def sect_at_va(va)
|
|
444
|
+
sect_at_rva(va - @optheader.image_base)
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
def label_rva(name)
|
|
448
|
+
if name.kind_of? Integer
|
|
449
|
+
name
|
|
450
|
+
elsif s = @sections.find { |s_| s_.encoded.export[name] }
|
|
451
|
+
s.virtaddr + s.encoded.export[name]
|
|
452
|
+
else
|
|
453
|
+
@encoded.export[name]
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
# address -> file offset
|
|
458
|
+
# handles LoadedPE
|
|
459
|
+
def addr_to_fileoff(addr)
|
|
460
|
+
addr -= @load_address ||= @optheader.image_base
|
|
461
|
+
return 0 if addr == 0 # sect_at_rva specialcases 0
|
|
462
|
+
if s = sect_at_rva(addr)
|
|
463
|
+
if s.respond_to? :virtaddr
|
|
464
|
+
addr - s.virtaddr + s.rawaddr
|
|
465
|
+
else # header
|
|
466
|
+
addr
|
|
467
|
+
end
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# file offset -> memory address
|
|
472
|
+
# handles LoadedPE
|
|
473
|
+
def fileoff_to_addr(foff)
|
|
474
|
+
if s = @sections.find { |s_| s_.rawaddr <= foff and s_.rawaddr + s_.rawsize > foff }
|
|
475
|
+
s.virtaddr + foff - s.rawaddr + (@load_address ||= @optheader.image_base)
|
|
476
|
+
elsif foff >= 0 and foff < @optheader.headers_size
|
|
477
|
+
foff + (@load_address ||= @optheader.image_base)
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
def each_section
|
|
482
|
+
if @header.size_opthdr == 0
|
|
483
|
+
@sections.each { |s|
|
|
484
|
+
next if not s.encoded
|
|
485
|
+
l = new_label(s.name)
|
|
486
|
+
s.encoded.add_export(l, 0)
|
|
487
|
+
yield s.encoded, l
|
|
488
|
+
}
|
|
489
|
+
return
|
|
490
|
+
end
|
|
491
|
+
base = @optheader.image_base
|
|
492
|
+
base = 0 if not base.kind_of? Integer
|
|
493
|
+
yield @encoded[0, @optheader.headers_size], base
|
|
494
|
+
@sections.each { |s| yield s.encoded, base + s.virtaddr }
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
# decodes the COFF header, optional header, section headers
|
|
498
|
+
# marks entrypoint and directories as edata.expord
|
|
499
|
+
def decode_header
|
|
500
|
+
@cursection ||= self
|
|
501
|
+
@encoded.ptr ||= 0
|
|
502
|
+
@sections = []
|
|
503
|
+
@header.decode(self)
|
|
504
|
+
optoff = @encoded.ptr
|
|
505
|
+
@optheader.decode(self)
|
|
506
|
+
decode_symbols if @header.num_sym != 0 and not @header.characteristics.include? 'DEBUG_STRIPPED'
|
|
507
|
+
curencoded.ptr = optoff + @header.size_opthdr
|
|
508
|
+
decode_sections
|
|
509
|
+
if sect_at_rva(@optheader.entrypoint)
|
|
510
|
+
curencoded.add_export new_label('entrypoint')
|
|
511
|
+
end
|
|
512
|
+
(DIRECTORIES - ['certificate_table']).each { |d|
|
|
513
|
+
if @directory[d] and sect_at_rva(@directory[d][0])
|
|
514
|
+
curencoded.add_export new_label(d)
|
|
515
|
+
end
|
|
516
|
+
}
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
# decode the COFF symbol table (obj only)
|
|
520
|
+
def decode_symbols
|
|
521
|
+
endptr = @encoded.ptr = @header.ptr_sym + 18*@header.num_sym
|
|
522
|
+
strlen = decode_word
|
|
523
|
+
@encoded.ptr = endptr
|
|
524
|
+
strtab = @encoded.read(strlen)
|
|
525
|
+
@encoded.ptr = @header.ptr_sym
|
|
526
|
+
@symbols = []
|
|
527
|
+
@header.num_sym.times {
|
|
528
|
+
break if @encoded.ptr >= endptr or @encoded.ptr >= @encoded.length
|
|
529
|
+
@symbols << Symbol.decode(self, strtab)
|
|
530
|
+
# keep the reloc.sym_idx accurate
|
|
531
|
+
@symbols.last.nr_aux.times { @symbols << nil }
|
|
532
|
+
}
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
# decode the COFF sections
|
|
536
|
+
def decode_sections
|
|
537
|
+
@header.num_sect.times {
|
|
538
|
+
@sections << Section.decode(self)
|
|
539
|
+
}
|
|
540
|
+
# now decode COFF object relocations
|
|
541
|
+
@sections.each { |s|
|
|
542
|
+
next if s.relocnr == 0
|
|
543
|
+
curencoded.ptr = s.relocaddr
|
|
544
|
+
s.relocs = []
|
|
545
|
+
s.relocnr.times { s.relocs << RelocObj.decode(self) }
|
|
546
|
+
new_label 'pcrel'
|
|
547
|
+
s.relocs.each { |r|
|
|
548
|
+
case r.type
|
|
549
|
+
when 'DIR32'
|
|
550
|
+
s.encoded.reloc[r.va] = Metasm::Relocation.new(Expression[r.sym.name], :u32, @endianness)
|
|
551
|
+
when 'REL32'
|
|
552
|
+
l = new_label('pcrel')
|
|
553
|
+
s.encoded.add_export(l, r.va+4)
|
|
554
|
+
s.encoded.reloc[r.va] = Metasm::Relocation.new(Expression[r.sym.name, :-, l], :u32, @endianness)
|
|
555
|
+
end
|
|
556
|
+
}
|
|
557
|
+
} if not @header.characteristics.include?('RELOCS_STRIPPED')
|
|
558
|
+
symbols.to_a.compact.each { |sym|
|
|
559
|
+
next if not sym.sec_nr.kind_of? Integer
|
|
560
|
+
next if sym.storage != 'EXTERNAL' and (sym.storage != 'STATIC' or sym.value == 0)
|
|
561
|
+
next if not s = @sections[sym.sec_nr-1]
|
|
562
|
+
s.encoded.add_export new_label(sym.name), sym.value
|
|
563
|
+
}
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
# decodes a section content (allows simpler LoadedPE override)
|
|
567
|
+
def decode_section_body(s)
|
|
568
|
+
raw = EncodedData.align_size(s.rawsize, @optheader.file_align)
|
|
569
|
+
virt = EncodedData.align_size(s.virtsize, @optheader.sect_align)
|
|
570
|
+
virt = raw = s.rawsize if @header.size_opthdr == 0
|
|
571
|
+
s.encoded = @encoded[s.rawaddr, [raw, virt].min] || EncodedData.new
|
|
572
|
+
s.encoded.virtsize = virt
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
# decodes COFF export table from directory
|
|
576
|
+
# mark exported names as encoded.export
|
|
577
|
+
def decode_exports
|
|
578
|
+
if @directory['export_table'] and sect_at_rva(@directory['export_table'][0])
|
|
579
|
+
@export = ExportDirectory.decode(self)
|
|
580
|
+
@export.exports.to_a.each { |e|
|
|
581
|
+
if e.name and sect_at_rva(e.target)
|
|
582
|
+
name = e.name
|
|
583
|
+
elsif e.ordinal and sect_at_rva(e.target)
|
|
584
|
+
name = "ord_#{@export.libname}_#{e.ordinal}"
|
|
585
|
+
end
|
|
586
|
+
e.target = curencoded.add_export new_label(name) if name
|
|
587
|
+
}
|
|
588
|
+
end
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
# decodes COFF import tables from directory
|
|
592
|
+
# mark iat entries as encoded.export
|
|
593
|
+
def decode_imports
|
|
594
|
+
if @directory['import_table'] and sect_at_rva(@directory['import_table'][0])
|
|
595
|
+
@imports = ImportDirectory.decode_all(self)
|
|
596
|
+
iatlen = @bitsize/8
|
|
597
|
+
@imports.each { |id|
|
|
598
|
+
if sect_at_rva(id.iat_p)
|
|
599
|
+
ptr = curencoded.ptr
|
|
600
|
+
id.imports.each { |i|
|
|
601
|
+
if i.name
|
|
602
|
+
name = new_label i.name
|
|
603
|
+
elsif i.ordinal
|
|
604
|
+
name = new_label "ord_#{id.libname}_#{i.ordinal}"
|
|
605
|
+
end
|
|
606
|
+
if name
|
|
607
|
+
i.target ||= name
|
|
608
|
+
r = Metasm::Relocation.new(Expression[name], "u#@bitsize".to_sym, @endianness)
|
|
609
|
+
curencoded.reloc[ptr] = r
|
|
610
|
+
curencoded.add_export new_label('iat_'+name), ptr, true
|
|
611
|
+
end
|
|
612
|
+
ptr += iatlen
|
|
613
|
+
}
|
|
614
|
+
end
|
|
615
|
+
}
|
|
616
|
+
end
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
# decodes resources from directory
|
|
620
|
+
def decode_resources
|
|
621
|
+
if @directory['resource_table'] and sect_at_rva(@directory['resource_table'][0])
|
|
622
|
+
@resource = ResourceDirectory.decode(self)
|
|
623
|
+
end
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
# decode the VERSION information from the resources (file version, os, copyright etc)
|
|
627
|
+
def decode_version(lang=0x409)
|
|
628
|
+
decode_resources if not resource
|
|
629
|
+
resource.decode_version(self, lang)
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
# decodes certificate table
|
|
633
|
+
def decode_certificates
|
|
634
|
+
if ct = @directory['certificate_table']
|
|
635
|
+
@certificates = []
|
|
636
|
+
@cursection = self
|
|
637
|
+
@encoded.ptr = ct[0]
|
|
638
|
+
off_end = ct[0]+ct[1]
|
|
639
|
+
while @encoded.ptr < off_end
|
|
640
|
+
certlen = decode_word
|
|
641
|
+
certrev = decode_half
|
|
642
|
+
certtype = decode_half
|
|
643
|
+
certdat = @encoded.read(certlen)
|
|
644
|
+
@certificates << [certrev, certtype, certdat]
|
|
645
|
+
end
|
|
646
|
+
end
|
|
647
|
+
end
|
|
648
|
+
|
|
649
|
+
# decode the COM Cor20 header
|
|
650
|
+
def decode_com
|
|
651
|
+
if @directory['com_runtime'] and sect_at_rva(@directory['com_runtime'][0])
|
|
652
|
+
@com_header = Cor20Header.decode(self)
|
|
653
|
+
if sect_at_rva(@com_header.entrypoint)
|
|
654
|
+
curencoded.add_export new_label('com_entrypoint')
|
|
655
|
+
end
|
|
656
|
+
@com_header.decode_all(self)
|
|
657
|
+
end
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
# decode COFF relocation tables from directory
|
|
661
|
+
def decode_relocs
|
|
662
|
+
if @directory['base_relocation_table'] and sect_at_rva(@directory['base_relocation_table'][0])
|
|
663
|
+
end_ptr = curencoded.ptr + @directory['base_relocation_table'][1]
|
|
664
|
+
@relocations = []
|
|
665
|
+
while curencoded.ptr < end_ptr
|
|
666
|
+
@relocations << RelocationTable.decode(self)
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
# interpret as EncodedData relocations
|
|
670
|
+
relocfunc = ('decode_reloc_' << @header.machine.downcase).to_sym
|
|
671
|
+
if not respond_to? relocfunc
|
|
672
|
+
puts "W: COFF: unsupported relocs for architecture #{@header.machine}" if $VERBOSE
|
|
673
|
+
return
|
|
674
|
+
end
|
|
675
|
+
@relocations.each { |rt|
|
|
676
|
+
rt.relocs.each { |r|
|
|
677
|
+
if s = sect_at_rva(rt.base_addr + r.offset)
|
|
678
|
+
e, p = s.encoded, s.encoded.ptr
|
|
679
|
+
rel = send(relocfunc, r)
|
|
680
|
+
e.reloc[p] = rel if rel
|
|
681
|
+
end
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
end
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
# decodes an I386 COFF relocation pointing to encoded.ptr
|
|
688
|
+
def decode_reloc_i386(r)
|
|
689
|
+
case r.type
|
|
690
|
+
when 'ABSOLUTE'
|
|
691
|
+
when 'HIGHLOW'
|
|
692
|
+
addr = decode_word
|
|
693
|
+
if s = sect_at_va(addr)
|
|
694
|
+
label = label_at(s.encoded, s.encoded.ptr, "xref_#{Expression[addr]}")
|
|
695
|
+
Metasm::Relocation.new(Expression[label], :u32, @endianness)
|
|
696
|
+
end
|
|
697
|
+
when 'DIR64'
|
|
698
|
+
addr = decode_xword
|
|
699
|
+
if s = sect_at_va(addr)
|
|
700
|
+
label = label_at(s.encoded, s.encoded.ptr, "xref_#{Expression[addr]}")
|
|
701
|
+
Metasm::Relocation.new(Expression[label], :u64, @endianness)
|
|
702
|
+
end
|
|
703
|
+
else puts "W: COFF: Unsupported i386 relocation #{r.inspect}" if $VERBOSE
|
|
704
|
+
end
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
def decode_debug
|
|
708
|
+
if dd = @directory['debug'] and sect_at_rva(dd[0])
|
|
709
|
+
@debug = []
|
|
710
|
+
p0 = curencoded.ptr
|
|
711
|
+
while curencoded.ptr < p0 + dd[1]
|
|
712
|
+
@debug << DebugDirectory.decode(self)
|
|
713
|
+
end
|
|
714
|
+
@debug.each { |dbg| dbg.decode_inner(self) }
|
|
715
|
+
end
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
# decode TLS directory, including tls callback table
|
|
719
|
+
def decode_tls
|
|
720
|
+
if @directory['tls_table'] and sect_at_rva(@directory['tls_table'][0])
|
|
721
|
+
@tls = TLSDirectory.decode(self)
|
|
722
|
+
if s = sect_at_va(@tls.callback_p)
|
|
723
|
+
s.encoded.add_export 'tls_callback_table'
|
|
724
|
+
@tls.callbacks.each_with_index { |cb, i|
|
|
725
|
+
@tls.callbacks[i] = curencoded.add_export "tls_callback_#{i}" if sect_at_rva(cb)
|
|
726
|
+
}
|
|
727
|
+
end
|
|
728
|
+
end
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
def decode_loadconfig
|
|
732
|
+
if lc = @directory['load_config'] and sect_at_rva(lc[0])
|
|
733
|
+
@loadconfig = LoadConfig.decode(self)
|
|
734
|
+
end
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
def decode_delayimports
|
|
738
|
+
if di = @directory['delay_import_table'] and sect_at_rva(di[0])
|
|
739
|
+
@delayimports = DelayImportDirectory.decode_all(self)
|
|
740
|
+
end
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
# decodes a COFF file (headers/exports/imports/relocs/sections)
|
|
745
|
+
# starts at encoded.ptr
|
|
746
|
+
def decode
|
|
747
|
+
decode_header
|
|
748
|
+
decode_exports
|
|
749
|
+
decode_imports
|
|
750
|
+
decode_resources
|
|
751
|
+
decode_certificates
|
|
752
|
+
decode_debug
|
|
753
|
+
decode_tls
|
|
754
|
+
decode_loadconfig
|
|
755
|
+
decode_delayimports
|
|
756
|
+
decode_com
|
|
757
|
+
decode_relocs unless nodecode_relocs or ENV['METASM_NODECODE_RELOCS'] # decode relocs last
|
|
758
|
+
end
|
|
759
|
+
|
|
760
|
+
# returns a metasm CPU object corresponding to +header.machine+
|
|
761
|
+
def cpu_from_headers
|
|
762
|
+
case @header.machine
|
|
763
|
+
when 'I386'; Ia32.new
|
|
764
|
+
when 'AMD64'; X86_64.new
|
|
765
|
+
when 'R4000'; MIPS.new(:little)
|
|
766
|
+
else raise "unknown cpu #{@header.machine}"
|
|
767
|
+
end
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
# returns an array including the PE entrypoint and the exported functions entrypoints
|
|
771
|
+
# TODO filter out exported data, include safeseh ?
|
|
772
|
+
def get_default_entrypoints
|
|
773
|
+
ep = []
|
|
774
|
+
ep.concat @tls.callbacks.to_a if tls
|
|
775
|
+
ep << (@optheader.image_base + label_rva(@optheader.entrypoint))
|
|
776
|
+
@export.exports.to_a.each { |e|
|
|
777
|
+
next if e.forwarder_lib or not e.target
|
|
778
|
+
ep << (@optheader.image_base + label_rva(e.target))
|
|
779
|
+
} if export
|
|
780
|
+
ep
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
def dump_section_header(addr, edata)
|
|
784
|
+
s = @sections.find { |s_| s_.virtaddr == addr-@optheader.image_base }
|
|
785
|
+
s ? "\n.section #{s.name.inspect} base=#{Expression[addr]}" :
|
|
786
|
+
addr == @optheader.image_base ? "// exe header at #{Expression[addr]}" : super(addr, edata)
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
# returns an array of [name, addr, length, info]
|
|
790
|
+
def section_info
|
|
791
|
+
[['header', @optheader.image_base, @optheader.headers_size, nil]] +
|
|
792
|
+
@sections.map { |s|
|
|
793
|
+
[s.name, @optheader.image_base + s.virtaddr, s.virtsize, s.characteristics.join(',')]
|
|
794
|
+
}
|
|
795
|
+
end
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
class COFFArchive
|
|
799
|
+
class Member
|
|
800
|
+
def decode(ar)
|
|
801
|
+
@offset = ar.encoded.ptr
|
|
802
|
+
|
|
803
|
+
super(ar)
|
|
804
|
+
raise 'bad member header' + self.inspect if @eoh != "`\n"
|
|
805
|
+
|
|
806
|
+
@name.strip!
|
|
807
|
+
@date = @date.to_i
|
|
808
|
+
@uid = @uid.to_i
|
|
809
|
+
@gid = @gid.to_i
|
|
810
|
+
@mode = @mode.to_i(8)
|
|
811
|
+
@size = @size.to_i
|
|
812
|
+
|
|
813
|
+
@encoded = ar.encoded[ar.encoded.ptr, @size]
|
|
814
|
+
ar.encoded.ptr += @size
|
|
815
|
+
ar.encoded.ptr += 1 if @size & 1 == 1
|
|
816
|
+
end
|
|
817
|
+
|
|
818
|
+
def decode_half ; @encoded.decode_imm(:u16, :big) end
|
|
819
|
+
def decode_word ; @encoded.decode_imm(:u32, :big) end
|
|
820
|
+
|
|
821
|
+
def exe; AutoExe.decode(@encoded) ; end
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
def decode_half(edata = @encoded) ; edata.decode_imm(:u16, :little) end
|
|
825
|
+
def decode_word(edata = @encoded) ; edata.decode_imm(:u32, :little) end
|
|
826
|
+
def decode_strz(edata = @encoded)
|
|
827
|
+
i = edata.data.index(?\0, edata.ptr) || edata.data.index(?\n, edata.ptr) || (edata.length+1)
|
|
828
|
+
edata.read(i+1-edata.ptr).chop
|
|
829
|
+
end
|
|
830
|
+
|
|
831
|
+
def decode_first_linker(m)
|
|
832
|
+
offsets = []
|
|
833
|
+
names = []
|
|
834
|
+
m.encoded.ptr = 0
|
|
835
|
+
numsym = m.decode_word
|
|
836
|
+
numsym.times { offsets << m.decode_word }
|
|
837
|
+
numsym.times { names << decode_strz(m.encoded) }
|
|
838
|
+
|
|
839
|
+
# names[42] is found in object at file offset offsets[42]
|
|
840
|
+
# offsets are sorted by object index (all syms from 1st object, then 2nd etc)
|
|
841
|
+
|
|
842
|
+
@first_linker = names.zip(offsets) #.inject({}) { |h, (n, o)| h.update n => o }
|
|
843
|
+
end
|
|
844
|
+
|
|
845
|
+
def decode_second_linker(m)
|
|
846
|
+
names = []
|
|
847
|
+
mboffsets = []
|
|
848
|
+
indices = []
|
|
849
|
+
m = @members[1]
|
|
850
|
+
m.encoded.ptr = 0
|
|
851
|
+
nummb = decode_word(m.encoded)
|
|
852
|
+
nummb.times { mboffsets << decode_word(m.encoded) }
|
|
853
|
+
numsym = decode_word(m.encoded)
|
|
854
|
+
numsym.times { indices << decode_half(m.encoded) }
|
|
855
|
+
numsym.times { names << decode_strz(m.encoded) }
|
|
856
|
+
|
|
857
|
+
# names[42] is found in object at file offset mboffsets[indices[42]]
|
|
858
|
+
# symbols sorted by symbol name (supposed to be more efficient, but no index into string table...)
|
|
859
|
+
|
|
860
|
+
#names.zip(indices).inject({}) { |h, (n, i)| h.update n => mboffsets[i] }
|
|
861
|
+
@second_linker = [names, mboffsets, indices]
|
|
862
|
+
end
|
|
863
|
+
|
|
864
|
+
def decode_longnames(m)
|
|
865
|
+
@longnames = m.encoded
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
# set real name to archive members
|
|
869
|
+
# look it up in the name table member if needed, or just remove the trailing /
|
|
870
|
+
def fixup_names
|
|
871
|
+
@members.each { |m|
|
|
872
|
+
case m.name
|
|
873
|
+
when '/'
|
|
874
|
+
when '//'
|
|
875
|
+
when /^\/(\d+)/
|
|
876
|
+
@longnames.ptr = $1.to_i
|
|
877
|
+
m.name = decode_strz(@longnames).chomp("/")
|
|
878
|
+
else m.name.chomp! "/"
|
|
879
|
+
end
|
|
880
|
+
}
|
|
881
|
+
end
|
|
882
|
+
|
|
883
|
+
def decode
|
|
884
|
+
@encoded.ptr = 0
|
|
885
|
+
@signature = @encoded.read(8)
|
|
886
|
+
raise InvalidExeFormat, "Invalid COFF Archive signature #{@signature.inspect}" if @signature != "!<arch>\n"
|
|
887
|
+
@members = []
|
|
888
|
+
while @encoded.ptr < @encoded.virtsize
|
|
889
|
+
@members << Member.decode(self)
|
|
890
|
+
end
|
|
891
|
+
@members.each { |m|
|
|
892
|
+
case m.name
|
|
893
|
+
when '/'; @first_linker ? decode_second_linker(m) : decode_first_linker(m)
|
|
894
|
+
when '//'; decode_longnames(m)
|
|
895
|
+
else break
|
|
896
|
+
end
|
|
897
|
+
}
|
|
898
|
+
fixup_names
|
|
899
|
+
end
|
|
900
|
+
end
|
|
901
|
+
end
|