metasm 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|