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,1078 @@
|
|
|
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/encode'
|
|
8
|
+
require 'metasm/exe_format/coff' unless defined? Metasm::COFF
|
|
9
|
+
|
|
10
|
+
module Metasm
|
|
11
|
+
class COFF
|
|
12
|
+
class OptionalHeader
|
|
13
|
+
# encodes an Optional header and the directories
|
|
14
|
+
def encode(coff)
|
|
15
|
+
opth = super(coff)
|
|
16
|
+
|
|
17
|
+
DIRECTORIES[0, @numrva].each { |d|
|
|
18
|
+
if d = coff.directory[d]
|
|
19
|
+
d = d.dup
|
|
20
|
+
d[0] = Expression[d[0], :-, coff.label_at(coff.encoded, 0)] if d[0].kind_of?(::String)
|
|
21
|
+
else
|
|
22
|
+
d = [0, 0]
|
|
23
|
+
end
|
|
24
|
+
opth << coff.encode_word(d[0]) << coff.encode_word(d[1])
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
opth
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# find good default values for optheader members, based on coff.sections
|
|
31
|
+
def set_default_values(coff)
|
|
32
|
+
@signature ||= (coff.bitsize == 64 ? 'PE+' : 'PE')
|
|
33
|
+
@link_ver_maj ||= 1
|
|
34
|
+
@link_ver_min ||= 0
|
|
35
|
+
@sect_align ||= 0x1000
|
|
36
|
+
align = lambda { |sz| EncodedData.align_size(sz, @sect_align) }
|
|
37
|
+
@code_size ||= coff.sections.find_all { |s| s.characteristics.include? 'CONTAINS_CODE' }.inject(0) { |sum, s| sum + align[s.virtsize] }
|
|
38
|
+
@data_size ||= coff.sections.find_all { |s| s.characteristics.include? 'CONTAINS_DATA' }.inject(0) { |sum, s| sum + align[s.virtsize] }
|
|
39
|
+
@udata_size ||= coff.sections.find_all { |s| s.characteristics.include? 'CONTAINS_UDATA' }.inject(0) { |sum, s| sum + align[s.virtsize] }
|
|
40
|
+
@entrypoint = Expression[@entrypoint, :-, coff.label_at(coff.encoded, 0)] if entrypoint and not @entrypoint.kind_of?(::Integer)
|
|
41
|
+
tmp = coff.sections.find { |s| s.characteristics.include? 'CONTAINS_CODE' }
|
|
42
|
+
@base_of_code ||= (tmp ? Expression[coff.label_at(tmp.encoded, 0), :-, coff.label_at(coff.encoded, 0)] : 0)
|
|
43
|
+
tmp = coff.sections.find { |s| s.characteristics.include? 'CONTAINS_DATA' }
|
|
44
|
+
@base_of_data ||= (tmp ? Expression[coff.label_at(tmp.encoded, 0), :-, coff.label_at(coff.encoded, 0)] : 0)
|
|
45
|
+
@file_align ||= 0x200
|
|
46
|
+
@os_ver_maj ||= 4
|
|
47
|
+
@subsys_maj ||= 4
|
|
48
|
+
@stack_reserve||= 0x100000
|
|
49
|
+
@stack_commit ||= 0x1000
|
|
50
|
+
@heap_reserve ||= 0x100000
|
|
51
|
+
@heap_commit ||= 0x1000
|
|
52
|
+
@numrva ||= DIRECTORIES.length
|
|
53
|
+
|
|
54
|
+
super(coff)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class Section
|
|
59
|
+
# find good default values for section header members, defines rawaddr/rawsize as new_label for later fixup
|
|
60
|
+
def set_default_values(coff)
|
|
61
|
+
@name ||= ''
|
|
62
|
+
@virtsize ||= @encoded.virtsize
|
|
63
|
+
@virtaddr ||= Expression[coff.label_at(@encoded, 0, 'sect_start'), :-, coff.label_at(coff.encoded, 0)]
|
|
64
|
+
@rawsize ||= coff.new_label('sect_rawsize')
|
|
65
|
+
@rawaddr ||= coff.new_label('sect_rawaddr')
|
|
66
|
+
|
|
67
|
+
super(coff)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
class ExportDirectory
|
|
72
|
+
# encodes an export directory
|
|
73
|
+
def encode(coff)
|
|
74
|
+
edata = {}
|
|
75
|
+
%w[edata addrtable namptable ord_table libname nametable].each { |name|
|
|
76
|
+
edata[name] = EncodedData.new
|
|
77
|
+
}
|
|
78
|
+
label = lambda { |n| coff.label_at(edata[n], 0, n) }
|
|
79
|
+
rva = lambda { |n| Expression[label[n], :-, coff.label_at(coff.encoded, 0)] }
|
|
80
|
+
rva_end = lambda { |n| Expression[[label[n], :-, coff.label_at(coff.encoded, 0)], :+, edata[n].virtsize] }
|
|
81
|
+
|
|
82
|
+
# ordinal base: smallest number > 1 to honor ordinals, minimize gaps
|
|
83
|
+
olist = @exports.map { |e| e.ordinal }.compact
|
|
84
|
+
# start with lowest ordinal, substract all exports unused to fill ordinal sequence gaps
|
|
85
|
+
omin = olist.min.to_i
|
|
86
|
+
gaps = olist.empty? ? 0 : olist.max+1 - olist.min - olist.length
|
|
87
|
+
noord = @exports.length - olist.length
|
|
88
|
+
@ordinal_base ||= [omin - (noord - gaps), 1].max
|
|
89
|
+
|
|
90
|
+
@libname_p = rva['libname']
|
|
91
|
+
@num_exports = [@exports.length, @exports.map { |e| e.ordinal }.compact.max.to_i - @ordinal_base].max
|
|
92
|
+
@num_names = @exports.find_all { |e| e.name }.length
|
|
93
|
+
@func_p = rva['addrtable']
|
|
94
|
+
@names_p = rva['namptable']
|
|
95
|
+
@ord_p = rva['ord_table']
|
|
96
|
+
|
|
97
|
+
edata['edata'] << super(coff)
|
|
98
|
+
|
|
99
|
+
edata['libname'] << @libname << 0
|
|
100
|
+
|
|
101
|
+
elist = @exports.find_all { |e| e.name and not e.ordinal }.sort_by { |e| e.name }
|
|
102
|
+
@exports.find_all { |e| e.ordinal }.sort_by { |e| e.ordinal }.each { |e| elist.insert(e.ordinal-@ordinal_base, e) }
|
|
103
|
+
elist.each { |e|
|
|
104
|
+
if not e
|
|
105
|
+
# export by ordinal with gaps
|
|
106
|
+
# XXX test this value with the windows loader
|
|
107
|
+
edata['addrtable'] << coff.encode_word(0xffff_ffff)
|
|
108
|
+
next
|
|
109
|
+
end
|
|
110
|
+
if e.forwarder_lib
|
|
111
|
+
edata['addrtable'] << coff.encode_word(rva_end['nametable'])
|
|
112
|
+
edata['nametable'] << e.forwarder_lib << ?. <<
|
|
113
|
+
if not e.forwarder_name
|
|
114
|
+
"##{e.forwarder_ordinal}"
|
|
115
|
+
else
|
|
116
|
+
e.forwarder_name
|
|
117
|
+
end << 0
|
|
118
|
+
else
|
|
119
|
+
edata['addrtable'] << coff.encode_word(Expression[e.target, :-, coff.label_at(coff.encoded, 0)])
|
|
120
|
+
end
|
|
121
|
+
if e.name
|
|
122
|
+
edata['ord_table'] << coff.encode_half(edata['addrtable'].virtsize/4 - 1)
|
|
123
|
+
edata['namptable'] << coff.encode_word(rva_end['nametable'])
|
|
124
|
+
edata['nametable'] << e.name << 0
|
|
125
|
+
end
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# sorted by alignment directives
|
|
129
|
+
%w[edata addrtable namptable ord_table libname nametable].inject(EncodedData.new) { |ed, name| ed << edata[name] }
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def set_default_values(coff)
|
|
133
|
+
@timestamp ||= Time.now.to_i
|
|
134
|
+
@libname ||= 'metalib'
|
|
135
|
+
@ordinal_base ||= 1
|
|
136
|
+
|
|
137
|
+
super(coff)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
class ImportDirectory
|
|
142
|
+
# encodes all import directories + iat
|
|
143
|
+
def self.encode(coff, ary)
|
|
144
|
+
edata = { 'iat' => [] }
|
|
145
|
+
%w[idata ilt nametable].each { |name| edata[name] = EncodedData.new }
|
|
146
|
+
|
|
147
|
+
ary.each { |i| i.encode(coff, edata) }
|
|
148
|
+
|
|
149
|
+
it = edata['idata'] <<
|
|
150
|
+
coff.encode_word(0) <<
|
|
151
|
+
coff.encode_word(0) <<
|
|
152
|
+
coff.encode_word(0) <<
|
|
153
|
+
coff.encode_word(0) <<
|
|
154
|
+
coff.encode_word(0) <<
|
|
155
|
+
edata['ilt'] <<
|
|
156
|
+
edata['nametable']
|
|
157
|
+
|
|
158
|
+
iat = edata['iat'] # why not fragmented ?
|
|
159
|
+
|
|
160
|
+
[it, iat]
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# encodes an import directory + iat + names in the edata hash received as arg
|
|
164
|
+
def encode(coff, edata)
|
|
165
|
+
edata['iat'] << EncodedData.new
|
|
166
|
+
# edata['ilt'] = edata['iat']
|
|
167
|
+
label = lambda { |n| coff.label_at(edata[n], 0, n) }
|
|
168
|
+
rva = lambda { |n| Expression[label[n], :-, coff.label_at(coff.encoded, 0)] }
|
|
169
|
+
rva_end = lambda { |n| Expression[[label[n], :-, coff.label_at(coff.encoded, 0)], :+, edata[n].virtsize] }
|
|
170
|
+
|
|
171
|
+
@libname_p = rva_end['nametable']
|
|
172
|
+
@ilt_p = rva_end['ilt']
|
|
173
|
+
@iat_p ||= Expression[coff.label_at(edata['iat'].last, 0, 'iat'), :-, coff.label_at(coff.encoded, 0)]
|
|
174
|
+
edata['idata'] << super(coff)
|
|
175
|
+
|
|
176
|
+
edata['nametable'] << @libname << 0
|
|
177
|
+
|
|
178
|
+
ord_mask = 1 << (coff.bitsize - 1)
|
|
179
|
+
@imports.each { |i|
|
|
180
|
+
edata['iat'].last.add_export i.target, edata['iat'].last.virtsize if i.target
|
|
181
|
+
if i.ordinal
|
|
182
|
+
ptr = coff.encode_xword(Expression[i.ordinal, :|, ord_mask])
|
|
183
|
+
else
|
|
184
|
+
edata['nametable'].align 2
|
|
185
|
+
ptr = coff.encode_xword(rva_end['nametable'])
|
|
186
|
+
edata['nametable'] << coff.encode_half(i.hint || 0) << i.name << 0
|
|
187
|
+
end
|
|
188
|
+
edata['ilt'] << ptr
|
|
189
|
+
edata['iat'].last << ptr
|
|
190
|
+
}
|
|
191
|
+
edata['ilt'] << coff.encode_xword(0)
|
|
192
|
+
edata['iat'].last << coff.encode_xword(0)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
class TLSDirectory
|
|
197
|
+
def encode(coff)
|
|
198
|
+
cblist = EncodedData.new
|
|
199
|
+
@callback_p = coff.label_at(cblist, 0, 'callback_p')
|
|
200
|
+
@callbacks.to_a.each { |cb|
|
|
201
|
+
cblist << coff.encode_xword(cb)
|
|
202
|
+
}
|
|
203
|
+
cblist << coff.encode_xword(0)
|
|
204
|
+
|
|
205
|
+
dir = super(coff)
|
|
206
|
+
|
|
207
|
+
[dir, cblist]
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def set_default_values(coff)
|
|
211
|
+
@start_va ||= 0
|
|
212
|
+
@end_va ||= @start_va
|
|
213
|
+
|
|
214
|
+
super(coff)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
class RelocationTable
|
|
219
|
+
# encodes a COFF relocation table
|
|
220
|
+
def encode(coff)
|
|
221
|
+
rel = super(coff) << coff.encode_word(8 + 2*@relocs.length)
|
|
222
|
+
@relocs.each { |r| rel << r.encode(coff) }
|
|
223
|
+
rel
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def set_default_values(coff)
|
|
227
|
+
# @base_addr is an rva
|
|
228
|
+
@base_addr = Expression[@base_addr, :-, coff.label_at(coff.encoded, 0)] if @base_addr.kind_of?(::String)
|
|
229
|
+
|
|
230
|
+
# align relocation table size
|
|
231
|
+
if @relocs.length % 2 != 0
|
|
232
|
+
r = Relocation.new
|
|
233
|
+
r.type = 0
|
|
234
|
+
r.offset = 0
|
|
235
|
+
@relocs << r
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
super(coff)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
class ResourceDirectory
|
|
243
|
+
# compiles ressource directories
|
|
244
|
+
def encode(coff, edata = nil)
|
|
245
|
+
if not edata
|
|
246
|
+
# init recursion
|
|
247
|
+
edata = {}
|
|
248
|
+
subtables = %w[table names dataentries data]
|
|
249
|
+
subtables.each { |n| edata[n] = EncodedData.new }
|
|
250
|
+
encode(coff, edata)
|
|
251
|
+
return subtables.inject(EncodedData.new) { |sum, n| sum << edata[n] }
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
label = lambda { |n| coff.label_at(edata[n], 0, n) }
|
|
255
|
+
# data 'rva' are real rvas (from start of COFF)
|
|
256
|
+
rva_end = lambda { |n| Expression[[label[n], :-, coff.label_at(coff.encoded, 0)], :+, edata[n].virtsize] }
|
|
257
|
+
# names and table 'rva' are relative to the beginning of the resource directory
|
|
258
|
+
off_end = lambda { |n| Expression[[label[n], :-, coff.label_at(edata['table'], 0)], :+, edata[n].virtsize] }
|
|
259
|
+
|
|
260
|
+
# build name_w if needed
|
|
261
|
+
@entries.each { |e| e.name_w = e.name.unpack('C*').pack('v*') if e.name and not e.name_w }
|
|
262
|
+
|
|
263
|
+
# fixup forward references to us, as subdir
|
|
264
|
+
edata['table'].fixup @curoff_label => edata['table'].virtsize if defined? @curoff_label
|
|
265
|
+
|
|
266
|
+
@nr_names = @entries.find_all { |e| e.name_w }.length
|
|
267
|
+
@nr_id = @entries.find_all { |e| e.id }.length
|
|
268
|
+
edata['table'] << super(coff)
|
|
269
|
+
|
|
270
|
+
# encode entries, sorted by names nocase, then id
|
|
271
|
+
@entries.sort_by { |e| e.name_w ? [0, e.name_w.downcase] : [1, e.id] }.each { |e|
|
|
272
|
+
if e.name_w
|
|
273
|
+
edata['table'] << coff.encode_word(Expression[off_end['names'], :|, 1 << 31])
|
|
274
|
+
edata['names'] << coff.encode_half(e.name_w.length/2) << e.name_w
|
|
275
|
+
else
|
|
276
|
+
edata['table'] << coff.encode_word(e.id)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
if e.subdir
|
|
280
|
+
e.subdir.curoff_label = coff.new_label('rsrc_curoff')
|
|
281
|
+
edata['table'] << coff.encode_word(Expression[e.subdir.curoff_label, :|, 1 << 31])
|
|
282
|
+
else # data entry
|
|
283
|
+
edata['table'] << coff.encode_word(off_end['dataentries'])
|
|
284
|
+
|
|
285
|
+
edata['dataentries'] <<
|
|
286
|
+
coff.encode_word(rva_end['data']) <<
|
|
287
|
+
coff.encode_word(e.data.length) <<
|
|
288
|
+
coff.encode_word(e.codepage || 0) <<
|
|
289
|
+
coff.encode_word(e.reserved || 0)
|
|
290
|
+
|
|
291
|
+
edata['data'] << e.data
|
|
292
|
+
end
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
# recurse
|
|
296
|
+
@entries.find_all { |e| e.subdir }.each { |e| e.subdir.encode(coff, edata) }
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
# computes the checksum for a given COFF file
|
|
302
|
+
# may not work with overlapping sections
|
|
303
|
+
def self.checksum(str, endianness = :little)
|
|
304
|
+
coff = load str
|
|
305
|
+
coff.endianness = endianness
|
|
306
|
+
coff.decode_header
|
|
307
|
+
coff.encoded.ptr = 0
|
|
308
|
+
|
|
309
|
+
flen = 0
|
|
310
|
+
csum = 0
|
|
311
|
+
# negate old checksum
|
|
312
|
+
oldcs = coff.encode_word(coff.optheader.checksum)
|
|
313
|
+
oldcs.ptr = 0
|
|
314
|
+
csum -= coff.decode_half(oldcs)
|
|
315
|
+
csum -= coff.decode_half(oldcs)
|
|
316
|
+
|
|
317
|
+
# checksum header
|
|
318
|
+
raw = coff.encoded.read(coff.optheader.headers_size)
|
|
319
|
+
flen += coff.optheader.headers_size
|
|
320
|
+
|
|
321
|
+
coff.sections.each { |s|
|
|
322
|
+
coff.encoded.ptr = s.rawaddr
|
|
323
|
+
raw << coff.encoded.read(s.rawsize)
|
|
324
|
+
flen += s.rawsize
|
|
325
|
+
}
|
|
326
|
+
raw.unpack(endianness == :little ? 'v*' : 'n*').each { |s|
|
|
327
|
+
csum += s
|
|
328
|
+
csum = (csum & 0xffff) + (csum >> 16) if (csum >> 16) > 0
|
|
329
|
+
}
|
|
330
|
+
csum + flen
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def encode_byte(w) Expression[w].encode(:u8, @endianness, (caller if $DEBUG)) end
|
|
335
|
+
def encode_half(w) Expression[w].encode(:u16, @endianness, (caller if $DEBUG)) end
|
|
336
|
+
def encode_word(w) Expression[w].encode(:u32, @endianness, (caller if $DEBUG)) end
|
|
337
|
+
def encode_xword(w) Expression[w].encode((@bitsize == 32 ? :u32 : :u64), @endianness, (caller if $DEBUG)) end
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
# adds a new compiler-generated section
|
|
341
|
+
def encode_append_section(s)
|
|
342
|
+
if (s.virtsize || s.encoded.virtsize) < 4096
|
|
343
|
+
# find section to merge with
|
|
344
|
+
# XXX check following sections for hardcoded base address ?
|
|
345
|
+
|
|
346
|
+
char = s.characteristics.dup
|
|
347
|
+
secs = @sections.dup
|
|
348
|
+
# do not merge non-discardable in discardable
|
|
349
|
+
if not char.delete 'MEM_DISCARDABLE'
|
|
350
|
+
secs.delete_if { |ss| ss.characteristics.include? 'MEM_DISCARDABLE' }
|
|
351
|
+
end
|
|
352
|
+
# do not merge shared w/ non-shared
|
|
353
|
+
if char.delete 'MEM_SHARED'
|
|
354
|
+
secs.delete_if { |ss| not ss.characteristics.include? 'MEM_SHARED' }
|
|
355
|
+
else
|
|
356
|
+
secs.delete_if { |ss| ss.characteristics.include? 'MEM_SHARED' }
|
|
357
|
+
end
|
|
358
|
+
secs.delete_if { |ss| ss.virtsize.kind_of?(::Integer) or ss.rawsize.kind_of?(::Integer) or secs[secs.index(ss)+1..-1].find { |ss_| ss_.virtaddr.kind_of?(::Integer) } }
|
|
359
|
+
|
|
360
|
+
# try to find superset of characteristics
|
|
361
|
+
if target = secs.find { |ss| (ss.characteristics & char) == char }
|
|
362
|
+
target.encoded.align 8
|
|
363
|
+
puts "PE: merging #{s.name} in #{target.name} (#{target.encoded.virtsize})" if $DEBUG
|
|
364
|
+
s.encoded = target.encoded << s.encoded
|
|
365
|
+
else
|
|
366
|
+
@sections << s
|
|
367
|
+
end
|
|
368
|
+
else
|
|
369
|
+
@sections << s
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# encodes the export table as a new section, updates directory['export_table']
|
|
374
|
+
def encode_exports
|
|
375
|
+
edata = @export.encode self
|
|
376
|
+
|
|
377
|
+
# must include name tables (for forwarders)
|
|
378
|
+
@directory['export_table'] = [label_at(edata, 0, 'export_table'), edata.virtsize]
|
|
379
|
+
|
|
380
|
+
s = Section.new
|
|
381
|
+
s.name = '.edata'
|
|
382
|
+
s.encoded = edata
|
|
383
|
+
s.characteristics = %w[MEM_READ]
|
|
384
|
+
encode_append_section s
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# encodes the import tables as a new section, updates directory['import_table'] and directory['iat']
|
|
388
|
+
def encode_imports
|
|
389
|
+
idata, iat = ImportDirectory.encode(self, @imports)
|
|
390
|
+
|
|
391
|
+
@directory['import_table'] = [label_at(idata, 0, 'idata'), idata.virtsize]
|
|
392
|
+
|
|
393
|
+
s = Section.new
|
|
394
|
+
s.name = '.idata'
|
|
395
|
+
s.encoded = idata
|
|
396
|
+
s.characteristics = %w[MEM_READ MEM_WRITE MEM_DISCARDABLE]
|
|
397
|
+
encode_append_section s
|
|
398
|
+
|
|
399
|
+
if @imports.first and @imports.first.iat_p.kind_of? Integer
|
|
400
|
+
ordiat = @imports.zip(iat).sort_by { |id, it| id.iat_p.kind_of?(Integer) ? id.iat_p : 1<<65 }.map { |id, it| it }
|
|
401
|
+
else
|
|
402
|
+
ordiat = iat
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
@directory['iat'] = [label_at(ordiat.first, 0, 'iat'),
|
|
406
|
+
Expression[label_at(ordiat.last, ordiat.last.virtsize, 'iat_end'), :-, label_at(ordiat.first, 0)]] if not ordiat.empty?
|
|
407
|
+
|
|
408
|
+
iat_s = nil
|
|
409
|
+
|
|
410
|
+
plt = Section.new
|
|
411
|
+
plt.name = '.plt'
|
|
412
|
+
plt.encoded = EncodedData.new
|
|
413
|
+
plt.characteristics = %w[MEM_READ MEM_EXECUTE]
|
|
414
|
+
|
|
415
|
+
@imports.zip(iat) { |id, it|
|
|
416
|
+
if id.iat_p.kind_of? Integer and s = @sections.find { |s_| s_.virtaddr <= id.iat_p and s_.virtaddr + (s_.virtsize || s_.encoded.virtsize) > id.iat_p }
|
|
417
|
+
id.iat = it # will be fixed up after encode_section
|
|
418
|
+
else
|
|
419
|
+
# XXX should not be mixed (for @directory['iat'][1])
|
|
420
|
+
if not iat_s
|
|
421
|
+
iat_s = Section.new
|
|
422
|
+
iat_s.name = '.iat'
|
|
423
|
+
iat_s.encoded = EncodedData.new
|
|
424
|
+
iat_s.characteristics = %w[MEM_READ MEM_WRITE]
|
|
425
|
+
encode_append_section iat_s
|
|
426
|
+
end
|
|
427
|
+
iat_s.encoded << it
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
id.imports.each { |i|
|
|
431
|
+
if i.thunk
|
|
432
|
+
arch_encode_thunk(plt.encoded, i)
|
|
433
|
+
end
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
encode_append_section plt if not plt.encoded.empty?
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
# encodes a thunk to imported function
|
|
441
|
+
def arch_encode_thunk(edata, import)
|
|
442
|
+
case @cpu.shortname
|
|
443
|
+
when 'ia32', 'x64'
|
|
444
|
+
shellcode = lambda { |c| Shellcode.new(@cpu).share_namespace(self).assemble(c).encoded }
|
|
445
|
+
if @cpu.generate_PIC
|
|
446
|
+
if @cpu.shortname == 'x64'
|
|
447
|
+
edata << shellcode["#{import.thunk}: jmp [rip-$_+#{import.target}]"]
|
|
448
|
+
return
|
|
449
|
+
end
|
|
450
|
+
# sections starts with a helper function that returns the address of metasm_intern_geteip in eax (PIC)
|
|
451
|
+
if not @sections.find { |s| s.encoded and s.encoded.export['metasm_intern_geteip'] } and edata.empty?
|
|
452
|
+
edata << shellcode["metasm_intern_geteip: call 42f\n42:\npop eax\nsub eax, 42b-metasm_intern_geteip\nret"]
|
|
453
|
+
end
|
|
454
|
+
edata << shellcode["#{import.thunk}: call metasm_intern_geteip\njmp [eax+#{import.target}-metasm_intern_geteip]"]
|
|
455
|
+
else
|
|
456
|
+
edata << shellcode["#{import.thunk}: jmp [#{import.target}]"]
|
|
457
|
+
end
|
|
458
|
+
else raise EncodeError, 'E: COFF: encode import thunk: unsupported architecture'
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
def encode_tls
|
|
463
|
+
dir, cbtable = @tls.encode(self)
|
|
464
|
+
@directory['tls_table'] = [label_at(dir, 0, 'tls_table'), dir.virtsize]
|
|
465
|
+
|
|
466
|
+
s = Section.new
|
|
467
|
+
s.name = '.tls'
|
|
468
|
+
s.encoded = EncodedData.new << dir << cbtable
|
|
469
|
+
s.characteristics = %w[MEM_READ MEM_WRITE]
|
|
470
|
+
encode_append_section s
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
# encodes relocation tables in a new section .reloc, updates @directory['base_relocation_table']
|
|
474
|
+
def encode_relocs
|
|
475
|
+
if @relocations.empty?
|
|
476
|
+
rt = RelocationTable.new
|
|
477
|
+
rt.base_addr = 0
|
|
478
|
+
rt.relocs = []
|
|
479
|
+
@relocations << rt
|
|
480
|
+
end
|
|
481
|
+
relocs = @relocations.inject(EncodedData.new) { |edata, rt_| edata << rt_.encode(self) }
|
|
482
|
+
|
|
483
|
+
@directory['base_relocation_table'] = [label_at(relocs, 0, 'reloc_table'), relocs.virtsize]
|
|
484
|
+
|
|
485
|
+
s = Section.new
|
|
486
|
+
s.name = '.reloc'
|
|
487
|
+
s.encoded = relocs
|
|
488
|
+
s.characteristics = %w[MEM_READ MEM_DISCARDABLE]
|
|
489
|
+
encode_append_section s
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
# creates the @relocations from sections.encoded.reloc
|
|
493
|
+
def create_relocation_tables
|
|
494
|
+
@relocations = []
|
|
495
|
+
|
|
496
|
+
# create a fake binding with all exports, to find only-image_base-dependant relocs targets
|
|
497
|
+
# not foolproof, but works in standard cases
|
|
498
|
+
startaddr = curaddr = label_at(@encoded, 0, 'coff_start')
|
|
499
|
+
binding = {}
|
|
500
|
+
@sections.each { |s|
|
|
501
|
+
binding.update s.encoded.binding(curaddr)
|
|
502
|
+
curaddr = Expression[curaddr, :+, s.encoded.virtsize]
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
# for each section.encoded, make as many RelocationTables as needed
|
|
506
|
+
@sections.each { |s|
|
|
507
|
+
|
|
508
|
+
# rt.base_addr temporarily holds the offset from section_start, and is fixed up to rva before '@reloc << rt'
|
|
509
|
+
rt = RelocationTable.new
|
|
510
|
+
|
|
511
|
+
s.encoded.reloc.each { |off, rel|
|
|
512
|
+
# check that the relocation looks like "program_start + integer" when bound using the fake binding
|
|
513
|
+
# XXX allow :i32 etc
|
|
514
|
+
if rel.endianness == @endianness and [:u32, :a32, :u64, :a64].include?(rel.type) and
|
|
515
|
+
rel.target.bind(binding).reduce.kind_of?(Expression) and
|
|
516
|
+
Expression[rel.target, :-, startaddr].bind(binding).reduce.kind_of?(::Integer)
|
|
517
|
+
# winner !
|
|
518
|
+
|
|
519
|
+
# build relocation
|
|
520
|
+
r = RelocationTable::Relocation.new
|
|
521
|
+
r.offset = off & 0xfff
|
|
522
|
+
r.type = { :u32 => 'HIGHLOW', :u64 => 'DIR64', :a32 => 'HIGHLOW', :a64 => 'DIR64' }[rel.type]
|
|
523
|
+
|
|
524
|
+
# check if we need to start a new relocation table
|
|
525
|
+
if rt.base_addr and (rt.base_addr & ~0xfff) != (off & ~0xfff)
|
|
526
|
+
rt.base_addr = Expression[[label_at(s.encoded, 0, 'sect_start'), :-, startaddr], :+, rt.base_addr]
|
|
527
|
+
@relocations << rt
|
|
528
|
+
rt = RelocationTable.new
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
# initialize reloc table base address if needed
|
|
532
|
+
if not rt.base_addr
|
|
533
|
+
rt.base_addr = off & ~0xfff
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
(rt.relocs ||= []) << r
|
|
537
|
+
elsif $DEBUG and not rel.target.bind(binding).reduce.kind_of?(Integer)
|
|
538
|
+
puts "W: COFF: Ignoring weird relocation #{rel.inspect} when building relocation tables"
|
|
539
|
+
end
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if rt and rt.relocs
|
|
543
|
+
rt.base_addr = Expression[[label_at(s.encoded, 0, 'sect_start'), :-, startaddr], :+, rt.base_addr]
|
|
544
|
+
@relocations << rt
|
|
545
|
+
end
|
|
546
|
+
}
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
def encode_resource
|
|
550
|
+
res = @resource.encode self
|
|
551
|
+
|
|
552
|
+
@directory['resource_table'] = [label_at(res, 0, 'resource_table'), res.virtsize]
|
|
553
|
+
|
|
554
|
+
s = Section.new
|
|
555
|
+
s.name = '.rsrc'
|
|
556
|
+
s.encoded = res
|
|
557
|
+
s.characteristics = %w[MEM_READ]
|
|
558
|
+
encode_append_section s
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
# initialize the header from target/cpu/etc, target in ['exe' 'dll' 'kmod' 'obj']
|
|
562
|
+
def pre_encode_header(target = 'exe', want_relocs=true)
|
|
563
|
+
target = {:bin => 'exe', :lib => 'dll', :obj => 'obj', 'sys' => 'kmod', 'drv' => 'kmod'}.fetch(target, target)
|
|
564
|
+
|
|
565
|
+
@header.machine ||= case @cpu.shortname
|
|
566
|
+
when 'x64'; 'AMD64'
|
|
567
|
+
when 'ia32'; 'I386'
|
|
568
|
+
end
|
|
569
|
+
@optheader.signature ||= case @cpu.size
|
|
570
|
+
when 32; 'PE'
|
|
571
|
+
when 64; 'PE+'
|
|
572
|
+
end
|
|
573
|
+
@bitsize = (@optheader.signature == 'PE+' ? 64 : 32)
|
|
574
|
+
|
|
575
|
+
# setup header flags
|
|
576
|
+
tmp = %w[LINE_NUMS_STRIPPED LOCAL_SYMS_STRIPPED DEBUG_STRIPPED] +
|
|
577
|
+
case target
|
|
578
|
+
when 'exe'; %w[EXECUTABLE_IMAGE]
|
|
579
|
+
when 'dll'; %w[EXECUTABLE_IMAGE DLL]
|
|
580
|
+
when 'kmod'; %w[EXECUTABLE_IMAGE]
|
|
581
|
+
when 'obj'; []
|
|
582
|
+
end
|
|
583
|
+
if @cpu.size == 32
|
|
584
|
+
tmp << 'x32BIT_MACHINE'
|
|
585
|
+
else
|
|
586
|
+
tmp << 'LARGE_ADDRESS_AWARE'
|
|
587
|
+
end
|
|
588
|
+
tmp << 'RELOCS_STRIPPED' if not want_relocs
|
|
589
|
+
@header.characteristics ||= tmp
|
|
590
|
+
|
|
591
|
+
@optheader.subsystem ||= case target
|
|
592
|
+
when 'exe', 'dll'; 'WINDOWS_GUI'
|
|
593
|
+
when 'kmod'; 'NATIVE'
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
tmp = []
|
|
597
|
+
tmp << 'NX_COMPAT'
|
|
598
|
+
tmp << 'DYNAMIC_BASE' if want_relocs
|
|
599
|
+
@optheader.dll_characts ||= tmp
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
# resets the values in the header that may have been
|
|
603
|
+
# modified by your script (eg section count, size, imagesize, etc)
|
|
604
|
+
# call this whenever you decode a file, modify it, and want to reencode it later
|
|
605
|
+
def invalidate_header
|
|
606
|
+
# set those values to nil, they will be
|
|
607
|
+
# recomputed during encode_header
|
|
608
|
+
[:code_size, :data_size, :udata_size, :base_of_code, :base_of_data,
|
|
609
|
+
:sect_align, :file_align, :image_size, :headers_size, :checksum].each { |m| @optheader.send("#{m}=", nil) }
|
|
610
|
+
[:num_sect, :ptr_sym, :num_sym, :size_opthdr].each { |m| @header.send("#{m}=", nil) }
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
# appends the header/optheader/directories/section table to @encoded
|
|
614
|
+
def encode_header
|
|
615
|
+
# encode section table, add CONTAINS_* flags from other characteristics flags
|
|
616
|
+
s_table = EncodedData.new
|
|
617
|
+
|
|
618
|
+
@sections.each { |s|
|
|
619
|
+
if s.characteristics.kind_of? Array and s.characteristics.include? 'MEM_READ'
|
|
620
|
+
if s.characteristics.include? 'MEM_EXECUTE'
|
|
621
|
+
s.characteristics |= ['CONTAINS_CODE']
|
|
622
|
+
elsif s.encoded
|
|
623
|
+
if s.encoded.rawsize == 0
|
|
624
|
+
s.characteristics |= ['CONTAINS_UDATA']
|
|
625
|
+
else
|
|
626
|
+
s.characteristics |= ['CONTAINS_DATA']
|
|
627
|
+
end
|
|
628
|
+
end
|
|
629
|
+
end
|
|
630
|
+
s.rawaddr = nil if s.rawaddr.kind_of?(::Integer) # XXX allow to force rawaddr ?
|
|
631
|
+
s_table << s.encode(self)
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
# encode optional header
|
|
635
|
+
@optheader.image_size ||= new_label('image_size')
|
|
636
|
+
@optheader.image_base ||= label_at(@encoded, 0)
|
|
637
|
+
@optheader.headers_size ||= new_label('headers_size')
|
|
638
|
+
@optheader.checksum ||= new_label('checksum')
|
|
639
|
+
@optheader.subsystem ||= 'WINDOWS_GUI'
|
|
640
|
+
@optheader.numrva = nil
|
|
641
|
+
opth = @optheader.encode(self)
|
|
642
|
+
|
|
643
|
+
# encode header
|
|
644
|
+
@header.machine ||= 'UNKNOWN'
|
|
645
|
+
@header.num_sect ||= sections.length
|
|
646
|
+
@header.time ||= Time.now.to_i & -255
|
|
647
|
+
@header.size_opthdr ||= opth.virtsize
|
|
648
|
+
@encoded << @header.encode(self) << opth << s_table
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
# append the section bodies to @encoded, and link the resulting binary
|
|
652
|
+
def encode_sections_fixup
|
|
653
|
+
@encoded.align @optheader.file_align
|
|
654
|
+
if @optheader.headers_size.kind_of?(::String)
|
|
655
|
+
@encoded.fixup! @optheader.headers_size => @encoded.virtsize
|
|
656
|
+
@optheader.headers_size = @encoded.virtsize
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
baseaddr = @optheader.image_base.kind_of?(::Integer) ? @optheader.image_base : 0x400000
|
|
660
|
+
binding = @encoded.binding(baseaddr)
|
|
661
|
+
|
|
662
|
+
curaddr = baseaddr + @optheader.headers_size
|
|
663
|
+
@sections.each { |s|
|
|
664
|
+
# align
|
|
665
|
+
curaddr = EncodedData.align_size(curaddr, @optheader.sect_align)
|
|
666
|
+
if s.rawaddr.kind_of?(::String)
|
|
667
|
+
@encoded.fixup! s.rawaddr => @encoded.virtsize
|
|
668
|
+
s.rawaddr = @encoded.virtsize
|
|
669
|
+
end
|
|
670
|
+
if s.virtaddr.kind_of?(::Integer)
|
|
671
|
+
raise "E: COFF: cannot encode section #{s.name}: hardcoded address too short" if curaddr > baseaddr + s.virtaddr
|
|
672
|
+
curaddr = baseaddr + s.virtaddr
|
|
673
|
+
end
|
|
674
|
+
binding.update s.encoded.binding(curaddr)
|
|
675
|
+
curaddr += s.virtsize
|
|
676
|
+
|
|
677
|
+
pre_sz = @encoded.virtsize
|
|
678
|
+
@encoded << s.encoded[0, s.encoded.rawsize]
|
|
679
|
+
@encoded.align @optheader.file_align
|
|
680
|
+
if s.rawsize.kind_of?(::String)
|
|
681
|
+
@encoded.fixup! s.rawsize => (@encoded.virtsize - pre_sz)
|
|
682
|
+
s.rawsize = @encoded.virtsize - pre_sz
|
|
683
|
+
end
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
# not aligned ? spec says it is, visual studio does not
|
|
687
|
+
binding[@optheader.image_size] = curaddr - baseaddr if @optheader.image_size.kind_of?(::String)
|
|
688
|
+
|
|
689
|
+
# patch the iat where iat_p was defined
|
|
690
|
+
# sort to ensure a 0-terminated will not overwrite an entry
|
|
691
|
+
# (try to dump notepad.exe, which has a forwarder;)
|
|
692
|
+
@imports.find_all { |id| id.iat_p.kind_of? Integer }.sort_by { |id| id.iat_p }.each { |id|
|
|
693
|
+
s = sect_at_rva(id.iat_p)
|
|
694
|
+
@encoded[s.rawaddr + s.encoded.ptr, id.iat.virtsize] = id.iat
|
|
695
|
+
binding.update id.iat.binding(baseaddr + id.iat_p)
|
|
696
|
+
} if imports
|
|
697
|
+
|
|
698
|
+
@encoded.fill
|
|
699
|
+
@encoded.fixup! binding
|
|
700
|
+
|
|
701
|
+
if @optheader.checksum.kind_of?(::String) and @encoded.reloc.length == 1
|
|
702
|
+
# won't work if there are other unresolved relocs
|
|
703
|
+
checksum = self.class.checksum(@encoded.data, @endianness)
|
|
704
|
+
@encoded.fixup @optheader.checksum => checksum
|
|
705
|
+
@optheader.checksum = checksum
|
|
706
|
+
end
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
# encode a COFF file, building export/import/reloc tables if needed
|
|
710
|
+
# creates the base relocation tables (need for references to IAT not known before)
|
|
711
|
+
# defaults to generating relocatable files, eg ALSR-aware
|
|
712
|
+
# pass want_relocs=false to avoid the file overhead induced by this
|
|
713
|
+
def encode(target = 'exe', want_relocs = true)
|
|
714
|
+
@encoded = EncodedData.new
|
|
715
|
+
label_at(@encoded, 0, 'coff_start')
|
|
716
|
+
pre_encode_header(target, want_relocs)
|
|
717
|
+
autoimport
|
|
718
|
+
encode_exports if export
|
|
719
|
+
encode_imports if imports
|
|
720
|
+
encode_resource if resource
|
|
721
|
+
encode_tls if tls
|
|
722
|
+
create_relocation_tables if want_relocs
|
|
723
|
+
encode_relocs if relocations
|
|
724
|
+
encode_header
|
|
725
|
+
encode_sections_fixup
|
|
726
|
+
@encoded.data
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
def parse_init
|
|
730
|
+
# ahem...
|
|
731
|
+
# a fake object, which when appended makes us parse '.text', which creates a real default section
|
|
732
|
+
# forwards to it this first appendage.
|
|
733
|
+
# allows the user to specify its own section if he wishes, and to use .text if he doesn't
|
|
734
|
+
if not defined? @cursource or not @cursource
|
|
735
|
+
@cursource = ::Object.new
|
|
736
|
+
class << @cursource
|
|
737
|
+
attr_accessor :coff
|
|
738
|
+
def <<(*a)
|
|
739
|
+
t = Preprocessor::Token.new(nil)
|
|
740
|
+
t.raw = '.text'
|
|
741
|
+
coff.parse_parser_instruction t
|
|
742
|
+
coff.cursource.send(:<<, *a)
|
|
743
|
+
end
|
|
744
|
+
end
|
|
745
|
+
@cursource.coff = self
|
|
746
|
+
end
|
|
747
|
+
@source ||= {}
|
|
748
|
+
super()
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
# handles compiler meta-instructions
|
|
752
|
+
#
|
|
753
|
+
# syntax:
|
|
754
|
+
# .section "<section name>" <perm list> <base>
|
|
755
|
+
# section name is a string (may be quoted)
|
|
756
|
+
# perms are in 'r' 'w' 'x' 'shared' 'discard', may be concatenated (in this order), may be prefixed by 'no' to remove the attribute for an existing section
|
|
757
|
+
# base is the token 'base', the token '=' and an immediate expression
|
|
758
|
+
# default sections:
|
|
759
|
+
# .text = .section '.text' rx
|
|
760
|
+
# .data = .section '.data' rw
|
|
761
|
+
# .rodata = .section '.rodata' r
|
|
762
|
+
# .bss = .section '.bss' rw
|
|
763
|
+
# .entrypoint | .entrypoint <label>
|
|
764
|
+
# defines the label as the program entrypoint
|
|
765
|
+
# without argument, creates a label used as entrypoint
|
|
766
|
+
# .libname "<name>"
|
|
767
|
+
# defines the string to be used as exported library name (should be the same as the file name, may omit extension)
|
|
768
|
+
# .export ["<exported_name>"] [<ordinal>] [<label_name>]
|
|
769
|
+
# exports the specified label with the specified name (label_name defaults to exported_name)
|
|
770
|
+
# if exported_name is an unquoted integer, the export is by ordinal. XXX if the ordinal starts with '0', the integer is interpreted as octal
|
|
771
|
+
# .import "<libname>" "<import_name|ordinal>" [<thunk_name>] [<label_name>]
|
|
772
|
+
# imports a symbol from a library
|
|
773
|
+
# if the thunk name is specified and not 'nil', the compiler will generate a thunk that can be called (in ia32, 'call thunk' == 'call [import_name]')
|
|
774
|
+
# the thunk is position-independent, and should be used instead of the indirect call form, for imported functions
|
|
775
|
+
# label_name is the label to attribute to the location that will receive the address of the imported symbol, defaults to import_name (iat_<import_name> if thunk == iname)
|
|
776
|
+
# .image_base <base>
|
|
777
|
+
# specifies the COFF prefered load address, base is an immediate expression
|
|
778
|
+
#
|
|
779
|
+
def parse_parser_instruction(instr)
|
|
780
|
+
readstr = lambda {
|
|
781
|
+
@lexer.skip_space
|
|
782
|
+
raise instr, 'string expected' if not t = @lexer.readtok or (t.type != :string and t.type != :quoted)
|
|
783
|
+
t.value || t.raw
|
|
784
|
+
}
|
|
785
|
+
check_eol = lambda {
|
|
786
|
+
@lexer.skip_space
|
|
787
|
+
raise instr, 'eol expected' if t = @lexer.nexttok and t.type != :eol
|
|
788
|
+
}
|
|
789
|
+
case instr.raw.downcase
|
|
790
|
+
when '.text', '.data', '.rodata', '.bss'
|
|
791
|
+
sname = instr.raw.downcase
|
|
792
|
+
if not @sections.find { |s| s.name == sname }
|
|
793
|
+
s = Section.new
|
|
794
|
+
s.name = sname
|
|
795
|
+
s.encoded = EncodedData.new
|
|
796
|
+
s.characteristics = case sname
|
|
797
|
+
when '.text'; %w[MEM_READ MEM_EXECUTE]
|
|
798
|
+
when '.data', '.bss'; %w[MEM_READ MEM_WRITE]
|
|
799
|
+
when '.rodata'; %w[MEM_READ]
|
|
800
|
+
end
|
|
801
|
+
@sections << s
|
|
802
|
+
end
|
|
803
|
+
@cursource = @source[sname] ||= []
|
|
804
|
+
check_eol[] if instr.backtrace # special case for magic @cursource
|
|
805
|
+
|
|
806
|
+
when '.section'
|
|
807
|
+
# .section <section name|"section name"> [(no)r w x shared discard] [base=<expr>]
|
|
808
|
+
sname = readstr[]
|
|
809
|
+
if not s = @sections.find { |s_| s_.name == sname }
|
|
810
|
+
s = Section.new
|
|
811
|
+
s.name = sname
|
|
812
|
+
s.encoded = EncodedData.new
|
|
813
|
+
s.characteristics = []
|
|
814
|
+
@sections << s
|
|
815
|
+
end
|
|
816
|
+
loop do
|
|
817
|
+
@lexer.skip_space
|
|
818
|
+
break if not tok = @lexer.nexttok or tok.type != :string
|
|
819
|
+
case @lexer.readtok.raw.downcase
|
|
820
|
+
when /^(no)?(r)?(w)?(x)?(shared)?(discard)?$/
|
|
821
|
+
ar = []
|
|
822
|
+
ar << 'MEM_READ' if $2
|
|
823
|
+
ar << 'MEM_WRITE' if $3
|
|
824
|
+
ar << 'MEM_EXECUTE' if $4
|
|
825
|
+
ar << 'MEM_SHARED' if $5
|
|
826
|
+
ar << 'MEM_DISCARDABLE' if $6
|
|
827
|
+
if $1; s.characteristics -= ar
|
|
828
|
+
else s.characteristics |= ar
|
|
829
|
+
end
|
|
830
|
+
when 'base'
|
|
831
|
+
@lexer.skip_space
|
|
832
|
+
@lexer.unreadtok tok if not tok = @lexer.readtok or tok.type != :punct or tok.raw != '='
|
|
833
|
+
raise instr, 'invalid base' if not s.virtaddr = Expression.parse(@lexer).reduce or not s.virtaddr.kind_of?(::Integer)
|
|
834
|
+
if not @optheader.image_base
|
|
835
|
+
@optheader.image_base = (s.virtaddr-0x80) & 0xfff00000
|
|
836
|
+
puts "Warning: no image_base specified, using #{Expression[@optheader.image_base]}" if $VERBOSE
|
|
837
|
+
end
|
|
838
|
+
s.virtaddr -= @optheader.image_base
|
|
839
|
+
else raise instr, 'unknown parameter'
|
|
840
|
+
end
|
|
841
|
+
end
|
|
842
|
+
@cursource = @source[sname] ||= []
|
|
843
|
+
check_eol[]
|
|
844
|
+
|
|
845
|
+
when '.libname'
|
|
846
|
+
# export directory library name
|
|
847
|
+
# .libname <libname|"libname">
|
|
848
|
+
@export ||= ExportDirectory.new
|
|
849
|
+
@export.libname = readstr[]
|
|
850
|
+
check_eol[]
|
|
851
|
+
|
|
852
|
+
when '.export'
|
|
853
|
+
# .export <export name|ordinal|"export name"> [ordinal] [label to export if different]
|
|
854
|
+
@lexer.skip_space
|
|
855
|
+
raise instr, 'string expected' if not tok = @lexer.readtok or (tok.type != :string and tok.type != :quoted)
|
|
856
|
+
exportname = tok.value || tok.raw
|
|
857
|
+
if tok.type == :string and (?0..?9).include? tok.raw[0]
|
|
858
|
+
exportname = Integer(exportname) rescue raise(tok, "bad ordinal value, try quotes #{' or rm leading 0' if exportname[0] == ?0}")
|
|
859
|
+
end
|
|
860
|
+
|
|
861
|
+
@lexer.skip_space
|
|
862
|
+
tok = @lexer.readtok
|
|
863
|
+
if tok and tok.type == :string and (?0..?9).include? tok.raw[0]
|
|
864
|
+
(eord = Integer(tok.raw)) rescue @lexer.unreadtok(tok)
|
|
865
|
+
else @lexer.unreadtok(tok)
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
@lexer.skip_space
|
|
869
|
+
tok = @lexer.readtok
|
|
870
|
+
if tok and tok.type == :string
|
|
871
|
+
exportlabel = tok.raw
|
|
872
|
+
else
|
|
873
|
+
@lexer.unreadtok tok
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
@export ||= ExportDirectory.new
|
|
877
|
+
@export.exports ||= []
|
|
878
|
+
e = ExportDirectory::Export.new
|
|
879
|
+
if exportname.kind_of? Integer
|
|
880
|
+
e.ordinal = exportname
|
|
881
|
+
else
|
|
882
|
+
e.name = exportname
|
|
883
|
+
e.ordinal = eord if eord
|
|
884
|
+
end
|
|
885
|
+
e.target = exportlabel || exportname
|
|
886
|
+
@export.exports << e
|
|
887
|
+
check_eol[]
|
|
888
|
+
|
|
889
|
+
when '.import'
|
|
890
|
+
# .import <libname|"libname"> <imported sym|"imported sym"> [label of plt thunk|nil] [label of iat element if != symname]
|
|
891
|
+
libname = readstr[]
|
|
892
|
+
i = ImportDirectory::Import.new
|
|
893
|
+
|
|
894
|
+
@lexer.skip_space
|
|
895
|
+
raise instr, 'string expected' if not tok = @lexer.readtok or (tok.type != :string and tok.type != :quoted)
|
|
896
|
+
if tok.type == :string and (?0..?9).include? tok.raw[0]
|
|
897
|
+
i.ordinal = Integer(tok.raw)
|
|
898
|
+
else
|
|
899
|
+
i.name = tok.value || tok.raw
|
|
900
|
+
end
|
|
901
|
+
|
|
902
|
+
@lexer.skip_space
|
|
903
|
+
if tok = @lexer.readtok and tok.type == :string
|
|
904
|
+
i.thunk = tok.raw if tok.raw != 'nil'
|
|
905
|
+
@lexer.skip_space
|
|
906
|
+
tok = @lexer.readtok
|
|
907
|
+
end
|
|
908
|
+
if tok and tok.type == :string
|
|
909
|
+
i.target = tok.raw
|
|
910
|
+
else
|
|
911
|
+
i.target = ((i.thunk == i.name) ? ('iat_' + i.name) : (i.name ? i.name : (i.thunk ? 'iat_' + i.thunk : raise(instr, 'need iat label'))))
|
|
912
|
+
@lexer.unreadtok tok
|
|
913
|
+
end
|
|
914
|
+
raise tok, 'import target exists' if i.target != new_label(i.target)
|
|
915
|
+
|
|
916
|
+
@imports ||= []
|
|
917
|
+
if not id = @imports.find { |id_| id_.libname == libname }
|
|
918
|
+
id = ImportDirectory.new
|
|
919
|
+
id.libname = libname
|
|
920
|
+
id.imports = []
|
|
921
|
+
@imports << id
|
|
922
|
+
end
|
|
923
|
+
id.imports << i
|
|
924
|
+
|
|
925
|
+
check_eol[]
|
|
926
|
+
|
|
927
|
+
when '.entrypoint'
|
|
928
|
+
# ".entrypoint <somelabel/expression>" or ".entrypoint" (here)
|
|
929
|
+
@lexer.skip_space
|
|
930
|
+
if tok = @lexer.nexttok and tok.type == :string
|
|
931
|
+
raise instr, 'syntax error' if not entrypoint = Expression.parse(@lexer)
|
|
932
|
+
else
|
|
933
|
+
entrypoint = new_label('entrypoint')
|
|
934
|
+
@cursource << Label.new(entrypoint, instr.backtrace.dup)
|
|
935
|
+
end
|
|
936
|
+
@optheader.entrypoint = entrypoint
|
|
937
|
+
check_eol[]
|
|
938
|
+
|
|
939
|
+
when '.image_base'
|
|
940
|
+
raise instr if not base = Expression.parse(@lexer) or !(base = base.reduce).kind_of?(::Integer)
|
|
941
|
+
@optheader.image_base = base
|
|
942
|
+
check_eol[]
|
|
943
|
+
|
|
944
|
+
when '.subsystem'
|
|
945
|
+
@lexer.skip_space
|
|
946
|
+
raise instr if not tok = @lexer.readtok
|
|
947
|
+
@optheader.subsystem = tok.raw
|
|
948
|
+
check_eol[]
|
|
949
|
+
|
|
950
|
+
else super(instr)
|
|
951
|
+
end
|
|
952
|
+
end
|
|
953
|
+
|
|
954
|
+
def assemble(*a)
|
|
955
|
+
parse(*a) if not a.empty?
|
|
956
|
+
@source.each { |k, v|
|
|
957
|
+
raise "no section named #{k} ?" if not s = @sections.find { |s_| s_.name == k }
|
|
958
|
+
s.encoded << assemble_sequence(v, @cpu)
|
|
959
|
+
v.clear
|
|
960
|
+
}
|
|
961
|
+
end
|
|
962
|
+
|
|
963
|
+
# defines __PE__
|
|
964
|
+
def tune_prepro(l)
|
|
965
|
+
l.define_weak('__PE__', 1)
|
|
966
|
+
l.define_weak('__MS_X86_64_ABI__') if @cpu and @cpu.shortname == 'x64'
|
|
967
|
+
end
|
|
968
|
+
|
|
969
|
+
def tune_cparser(cp)
|
|
970
|
+
super(cp)
|
|
971
|
+
cp.llp64 if @cpu.size == 64
|
|
972
|
+
end
|
|
973
|
+
|
|
974
|
+
# honors C attributes: export, export_as(foo), import_from(kernel32), entrypoint
|
|
975
|
+
# import by ordinal: extern __stdcall int anyname(int) __attribute__((import_from(ws2_32:28)));
|
|
976
|
+
# can alias imports with int mygpaddr_alias() attr(import_from(kernel32:GetProcAddr))
|
|
977
|
+
def read_c_attrs(cp)
|
|
978
|
+
cp.toplevel.symbol.each_value { |v|
|
|
979
|
+
next if not v.kind_of? C::Variable
|
|
980
|
+
if v.has_attribute 'export' or ea = v.has_attribute_var('export_as')
|
|
981
|
+
@export ||= ExportDirectory.new
|
|
982
|
+
@export.exports ||= []
|
|
983
|
+
e = ExportDirectory::Export.new
|
|
984
|
+
begin
|
|
985
|
+
e.ordinal = Integer(ea || v.name)
|
|
986
|
+
rescue ArgumentError
|
|
987
|
+
e.name = ea || v.name
|
|
988
|
+
end
|
|
989
|
+
e.target = v.name
|
|
990
|
+
@export.exports << e
|
|
991
|
+
end
|
|
992
|
+
if v.has_attribute('import') or ln = v.has_attribute_var('import_from')
|
|
993
|
+
ln ||= WindowsExports::EXPORT[v.name]
|
|
994
|
+
raise "unknown library for #{v.name}" if not ln
|
|
995
|
+
i = ImportDirectory::Import.new
|
|
996
|
+
if ln.include? ':'
|
|
997
|
+
ln, name = ln.split(':')
|
|
998
|
+
begin
|
|
999
|
+
i.ordinal = Integer(name)
|
|
1000
|
+
rescue ArgumentError
|
|
1001
|
+
i.name = name
|
|
1002
|
+
end
|
|
1003
|
+
else
|
|
1004
|
+
i.name = v.name
|
|
1005
|
+
end
|
|
1006
|
+
if v.type.kind_of? C::Function
|
|
1007
|
+
i.thunk = v.name
|
|
1008
|
+
i.target = 'iat_'+i.thunk
|
|
1009
|
+
else
|
|
1010
|
+
i.target = v.name
|
|
1011
|
+
end
|
|
1012
|
+
|
|
1013
|
+
@imports ||= []
|
|
1014
|
+
if not id = @imports.find { |id_| id_.libname == ln }
|
|
1015
|
+
id = ImportDirectory.new
|
|
1016
|
+
id.libname = ln
|
|
1017
|
+
id.imports = []
|
|
1018
|
+
@imports << id
|
|
1019
|
+
end
|
|
1020
|
+
id.imports << i
|
|
1021
|
+
end
|
|
1022
|
+
if v.has_attribute 'entrypoint'
|
|
1023
|
+
@optheader.entrypoint = v.name
|
|
1024
|
+
end
|
|
1025
|
+
}
|
|
1026
|
+
end
|
|
1027
|
+
|
|
1028
|
+
# try to resolve automatically COFF import tables from self.sections.encoded.relocations
|
|
1029
|
+
# and WindowsExports::EXPORT
|
|
1030
|
+
# if the relocation target is '<symbolname>' or 'iat_<symbolname>, link to the IAT address, if it is '<symbolname> + <expr>',
|
|
1031
|
+
# link to a thunk (plt-like)
|
|
1032
|
+
# if the relocation is not found, try again after appending 'fallback_append' to the symbol (eg wsprintf => wsprintfA)
|
|
1033
|
+
def autoimport(fallback_append='A')
|
|
1034
|
+
WindowsExports rescue return # autorequire
|
|
1035
|
+
autoexports = WindowsExports::EXPORT.dup
|
|
1036
|
+
@sections.each { |s|
|
|
1037
|
+
next if not s.encoded
|
|
1038
|
+
s.encoded.export.keys.each { |e| autoexports.delete e }
|
|
1039
|
+
}
|
|
1040
|
+
@sections.each { |s|
|
|
1041
|
+
next if not s.encoded
|
|
1042
|
+
s.encoded.reloc.each_value { |r|
|
|
1043
|
+
if r.target.op == :+ and not r.target.lexpr and r.target.rexpr.kind_of?(::String)
|
|
1044
|
+
sym = target = r.target.rexpr
|
|
1045
|
+
sym = sym[4..-1] if sym[0, 4] == 'iat_'
|
|
1046
|
+
elsif r.target.op == :- and r.target.rexpr.kind_of?(::String) and r.target.lexpr.kind_of?(::String)
|
|
1047
|
+
sym = thunk = r.target.lexpr
|
|
1048
|
+
end
|
|
1049
|
+
if not dll = autoexports[sym]
|
|
1050
|
+
sym += fallback_append if sym.kind_of?(::String) and fallback_append.kind_of?(::String)
|
|
1051
|
+
next if not dll = autoexports[sym]
|
|
1052
|
+
end
|
|
1053
|
+
|
|
1054
|
+
@imports ||= []
|
|
1055
|
+
next if @imports.find { |id| id.imports.find { |ii| ii.name == sym } }
|
|
1056
|
+
if not id = @imports.find { |id_| id_.libname =~ /^#{dll}(\.dll)?$/i }
|
|
1057
|
+
id = ImportDirectory.new
|
|
1058
|
+
id.libname = dll
|
|
1059
|
+
id.imports = []
|
|
1060
|
+
@imports << id
|
|
1061
|
+
end
|
|
1062
|
+
if not i = id.imports.find { |i_| i_.name == sym }
|
|
1063
|
+
i = ImportDirectory::Import.new
|
|
1064
|
+
i.name = sym
|
|
1065
|
+
id.imports << i
|
|
1066
|
+
end
|
|
1067
|
+
if (target and i.target and (i.target != target or i.thunk == target)) or
|
|
1068
|
+
(thunk and i.thunk and (i.thunk != thunk or i.target == thunk))
|
|
1069
|
+
puts "autoimport: conflict for #{target} #{thunk} #{i.inspect}" if $VERBOSE
|
|
1070
|
+
else
|
|
1071
|
+
i.target ||= new_label(target || 'iat_' + thunk)
|
|
1072
|
+
i.thunk ||= thunk if thunk
|
|
1073
|
+
end
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
end
|
|
1077
|
+
end
|
|
1078
|
+
end
|