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,1375 @@
|
|
|
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/elf' unless defined? Metasm::ELF
|
|
9
|
+
|
|
10
|
+
module Metasm
|
|
11
|
+
class ELF
|
|
12
|
+
class Header
|
|
13
|
+
def set_default_values elf
|
|
14
|
+
@magic ||= "\x7fELF"
|
|
15
|
+
@e_class ||= elf.bitsize.to_s
|
|
16
|
+
@data ||= (elf.endianness == :big ? 'MSB' : 'LSB')
|
|
17
|
+
@version ||= 'CURRENT'
|
|
18
|
+
@i_version ||= @version
|
|
19
|
+
@entry ||= 0
|
|
20
|
+
@phoff ||= elf.segments.empty? ? 0 : elf.new_label('phdr')
|
|
21
|
+
@shoff ||= elf.sections.length <= 1 ? 0 : elf.new_label('shdr')
|
|
22
|
+
@flags ||= []
|
|
23
|
+
@ehsize ||= Header.size(elf)
|
|
24
|
+
@phentsize ||= Segment.size(elf)
|
|
25
|
+
@phnum ||= elf.segments.length
|
|
26
|
+
@shentsize ||= Section.size(elf)
|
|
27
|
+
@shnum ||= elf.sections.length
|
|
28
|
+
|
|
29
|
+
super(elf)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class Section
|
|
34
|
+
def set_default_values elf
|
|
35
|
+
make_name_p elf if name and @name != ''
|
|
36
|
+
@flags ||= []
|
|
37
|
+
@addr ||= (encoded and @flags.include?('ALLOC')) ? elf.label_at(@encoded, 0) : 0
|
|
38
|
+
@offset ||= encoded ? elf.new_label('section_offset') : 0
|
|
39
|
+
@size ||= encoded ? @encoded.length : 0
|
|
40
|
+
@addralign ||= entsize || 0
|
|
41
|
+
@entsize ||= @addralign
|
|
42
|
+
@link = elf.sections.index(@link) if link.kind_of? Section
|
|
43
|
+
@info = elf.sections.index(@info) if info.kind_of? Section
|
|
44
|
+
super(elf)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# defines the @name_p field from @name and elf.section[elf.header.shstrndx]
|
|
48
|
+
# creates .shstrtab if needed
|
|
49
|
+
def make_name_p elf
|
|
50
|
+
return 0 if not name or @name == ''
|
|
51
|
+
if elf.header.shstrndx.to_i == 0
|
|
52
|
+
sn = Section.new
|
|
53
|
+
sn.name = '.shstrtab'
|
|
54
|
+
sn.type = 'STRTAB'
|
|
55
|
+
sn.flags = []
|
|
56
|
+
sn.addralign = 1
|
|
57
|
+
sn.encoded = EncodedData.new << 0
|
|
58
|
+
elf.header.shstrndx = elf.sections.length
|
|
59
|
+
elf.sections << sn
|
|
60
|
+
end
|
|
61
|
+
sne = elf.sections[elf.header.shstrndx].encoded
|
|
62
|
+
return if name_p and sne.data[@name_p, @name.length+1] == @name+0.chr
|
|
63
|
+
return if @name_p = sne.data.index(@name+0.chr)
|
|
64
|
+
@name_p = sne.virtsize
|
|
65
|
+
sne << @name << 0
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class Segment
|
|
70
|
+
def set_default_values elf
|
|
71
|
+
if encoded
|
|
72
|
+
@offset ||= elf.new_label('segment_offset')
|
|
73
|
+
@vaddr ||= elf.label_at(@encoded, 0)
|
|
74
|
+
@filesz ||= @encoded.rawsize
|
|
75
|
+
@memsz ||= @encoded.virtsize
|
|
76
|
+
end
|
|
77
|
+
@paddr ||= @vaddr if vaddr
|
|
78
|
+
|
|
79
|
+
super(elf)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
class Symbol
|
|
84
|
+
def set_default_values(elf, strtab)
|
|
85
|
+
make_name_p elf, strtab if strtab and name and @name != ''
|
|
86
|
+
super(elf)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# sets the value of @name_p, appends @name to strtab if needed
|
|
90
|
+
def make_name_p(elf, strtab)
|
|
91
|
+
s = strtab.kind_of?(EncodedData) ? strtab.data : strtab
|
|
92
|
+
return if name_p and s[@name_p, @name.length+1] == @name+0.chr
|
|
93
|
+
return if @name_p = s.index(@name+0.chr)
|
|
94
|
+
@name_p = strtab.length
|
|
95
|
+
strtab << @name << 0
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def encode_byte(w) Expression[w].encode(:u8, @endianness, (caller if $DEBUG)) end
|
|
101
|
+
def encode_half(w) Expression[w].encode(:u16, @endianness, (caller if $DEBUG)) end
|
|
102
|
+
def encode_word(w) Expression[w].encode(:u32, @endianness, (caller if $DEBUG)) end
|
|
103
|
+
def encode_sword(w) Expression[w].encode(:i32, @endianness, (caller if $DEBUG)) end
|
|
104
|
+
def encode_xword(w) Expression[w].encode((@bitsize == 32 ? :u32 : :u64), @endianness, (caller if $DEBUG)) end
|
|
105
|
+
def encode_sxword(w) Expression[w].encode((@bitsize == 32 ? :i32 : :i64), @endianness, (caller if $DEBUG)) end
|
|
106
|
+
alias encode_addr encode_xword
|
|
107
|
+
alias encode_off encode_xword
|
|
108
|
+
|
|
109
|
+
# checks a section's data has not grown beyond s.size, if so undefs addr/offset
|
|
110
|
+
def encode_check_section_size(s)
|
|
111
|
+
if s.size and s.encoded.virtsize < s.size
|
|
112
|
+
puts "W: Elf: preexisting section #{s} has grown, relocating" if $VERBOSE
|
|
113
|
+
s.addr = s.offset = nil
|
|
114
|
+
s.size = s.encoded.virtsize
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# reorders self.symbols according to their gnu_hash
|
|
119
|
+
def encode_reorder_symbols
|
|
120
|
+
gnu_hash_bucket_length = 42 # TODO
|
|
121
|
+
@symbols[1..-1] = @symbols[1..-1].sort_by { |s|
|
|
122
|
+
if s.bind != 'GLOBAL'
|
|
123
|
+
-2
|
|
124
|
+
elsif s.shndx == 'UNDEF' or not s.name
|
|
125
|
+
-1
|
|
126
|
+
else
|
|
127
|
+
ELF.gnu_hash_symbol_name(s.name) % gnu_hash_bucket_length
|
|
128
|
+
end
|
|
129
|
+
}
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# sorted insert of a new section to self.sections according to its permission (for segment merging)
|
|
133
|
+
def encode_add_section s
|
|
134
|
+
# order: r rx rw noalloc
|
|
135
|
+
rank = lambda { |sec|
|
|
136
|
+
f = sec.flags
|
|
137
|
+
sec.type == 'NULL' ? -2 : sec.addr ? -1 :
|
|
138
|
+
f.include?('ALLOC') ? !f.include?('WRITE') ? !f.include?('EXECINSTR') ? 0 : 1 : 2 : 3
|
|
139
|
+
}
|
|
140
|
+
srank = rank[s]
|
|
141
|
+
nexts = @sections.find { |sec| rank[sec] > srank } # find section with rank superior
|
|
142
|
+
nexts = nexts ? @sections.index(nexts) : -1 # if none, last
|
|
143
|
+
@sections.insert(nexts, s) # insert section
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# encodes the GNU_HASH table
|
|
147
|
+
# TODO
|
|
148
|
+
def encode_gnu_hash
|
|
149
|
+
return if true
|
|
150
|
+
|
|
151
|
+
sortedsyms = @symbols.find_all { |s| s.bind == 'GLOBAL' and s.shndx != 'UNDEF' and s.name }
|
|
152
|
+
bucket = Array.new(42)
|
|
153
|
+
|
|
154
|
+
if not gnu_hash = @sections.find { |s| s.type == 'GNU_HASH' }
|
|
155
|
+
gnu_hash = Section.new
|
|
156
|
+
gnu_hash.name = '.gnu.hash'
|
|
157
|
+
gnu_hash.type = 'GNU_HASH'
|
|
158
|
+
gnu_hash.flags = ['ALLOC']
|
|
159
|
+
gnu_hash.entsize = gnu_hash.addralign = 4
|
|
160
|
+
encode_add_section gnu_hash
|
|
161
|
+
end
|
|
162
|
+
gnu_hash.encoded = EncodedData.new
|
|
163
|
+
|
|
164
|
+
# "bloomfilter[N] has bit B cleared if there is no M (M > symndx) which satisfies (C = @header.class)
|
|
165
|
+
# ((gnu_hash(sym[M].name) / C) % maskwords) == N &&
|
|
166
|
+
# ((gnu_hash(sym[M].name) % C) == B ||
|
|
167
|
+
# ((gnu_hash(sym[M].name) >> shift2) % C) == B"
|
|
168
|
+
# bloomfilter may be [~0]
|
|
169
|
+
bloomfilter = []
|
|
170
|
+
|
|
171
|
+
# bucket[N] contains the lowest M for which
|
|
172
|
+
# gnu_hash(sym[M]) % nbuckets == N
|
|
173
|
+
# or 0 if none
|
|
174
|
+
bucket = []
|
|
175
|
+
|
|
176
|
+
gnu_hash.encoded <<
|
|
177
|
+
encode_word(bucket.length) <<
|
|
178
|
+
encode_word(@symbols.length - sortedsyms.length) <<
|
|
179
|
+
encode_word(bloomfilter.length) <<
|
|
180
|
+
encode_word(shift2)
|
|
181
|
+
bloomfilter.each { |bf| gnu_hash.encoded << encode_xword(bf) }
|
|
182
|
+
bucket.each { |bk| gnu_hash.encoded << encode_word(bk) }
|
|
183
|
+
sortedsyms.each { |s|
|
|
184
|
+
# (gnu_hash(sym[N].name) & ~1) | (N == dynsymcount-1 || (gnu_hash(sym[N].name) % nbucket) != (gnu_hash(sym[N+1].name) % nbucket))
|
|
185
|
+
# that's the hash, with its lower bit replaced by the bool [1 if i am the last sym having my hash as hash]
|
|
186
|
+
val = 28
|
|
187
|
+
gnu_hash.encoded << encode_word(val)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
@tag['GNU_HASH'] = label_at(gnu_hash.encoded, 0)
|
|
191
|
+
|
|
192
|
+
encode_check_section_size gnu_hash
|
|
193
|
+
|
|
194
|
+
gnu_hash
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# encodes the symbol dynamic hash table in the .hash section, updates the HASH tag
|
|
198
|
+
def encode_hash
|
|
199
|
+
if not hash = @sections.find { |s| s.type == 'HASH' }
|
|
200
|
+
hash = Section.new
|
|
201
|
+
hash.name = '.hash'
|
|
202
|
+
hash.type = 'HASH'
|
|
203
|
+
hash.flags = ['ALLOC']
|
|
204
|
+
hash.entsize = hash.addralign = 4
|
|
205
|
+
encode_add_section hash
|
|
206
|
+
end
|
|
207
|
+
hash.encoded = EncodedData.new
|
|
208
|
+
|
|
209
|
+
# to find a symbol from its name :
|
|
210
|
+
# 1: idx = hash(name)
|
|
211
|
+
# 2: idx = bucket[idx % bucket.size]
|
|
212
|
+
# 3: if idx == 0: return notfound
|
|
213
|
+
# 4: if dynsym[idx].name == name: return found
|
|
214
|
+
# 5: idx = chain[idx] ; goto 3
|
|
215
|
+
bucket = Array.new(@symbols.length/4+1, 0)
|
|
216
|
+
chain = Array.new(@symbols.length, 0)
|
|
217
|
+
@symbols.each_with_index { |s, i|
|
|
218
|
+
next if s.bind == 'LOCAL' or not s.name or s.shndx == 'UNDEF'
|
|
219
|
+
hash_mod = ELF.hash_symbol_name(s.name) % bucket.length
|
|
220
|
+
chain[i] = bucket[hash_mod]
|
|
221
|
+
bucket[hash_mod] = i
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
hash.encoded << encode_word(bucket.length) << encode_word(chain.length)
|
|
225
|
+
|
|
226
|
+
bucket.each { |b| hash.encoded << encode_word(b) }
|
|
227
|
+
chain.each { |c| hash.encoded << encode_word(c) }
|
|
228
|
+
|
|
229
|
+
@tag['HASH'] = label_at(hash.encoded, 0)
|
|
230
|
+
|
|
231
|
+
encode_check_section_size hash
|
|
232
|
+
|
|
233
|
+
hash
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# encodes the symbol table
|
|
237
|
+
# should have a stable self.sections array (only append allowed after this step)
|
|
238
|
+
def encode_segments_symbols(strtab)
|
|
239
|
+
if not dynsym = @sections.find { |s| s.type == 'DYNSYM' }
|
|
240
|
+
dynsym = Section.new
|
|
241
|
+
dynsym.name = '.dynsym'
|
|
242
|
+
dynsym.type = 'DYNSYM'
|
|
243
|
+
dynsym.entsize = Symbol.size(self)
|
|
244
|
+
dynsym.addralign = 4
|
|
245
|
+
dynsym.flags = ['ALLOC']
|
|
246
|
+
dynsym.info = @symbols[1..-1].find_all { |s| s.bind == 'LOCAL' }.length + 1
|
|
247
|
+
dynsym.link = strtab
|
|
248
|
+
encode_add_section dynsym
|
|
249
|
+
end
|
|
250
|
+
dynsym.encoded = EncodedData.new
|
|
251
|
+
@symbols.each { |s| dynsym.encoded << s.encode(self, strtab.encoded) } # needs all section indexes, as will be in the final section header
|
|
252
|
+
|
|
253
|
+
@tag['SYMTAB'] = label_at(dynsym.encoded, 0)
|
|
254
|
+
@tag['SYMENT'] = Symbol.size(self)
|
|
255
|
+
|
|
256
|
+
encode_check_section_size dynsym
|
|
257
|
+
|
|
258
|
+
dynsym
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# encodes the relocation tables
|
|
262
|
+
# needs a complete self.symbols array
|
|
263
|
+
def encode_segments_relocs
|
|
264
|
+
return if not @relocations
|
|
265
|
+
|
|
266
|
+
arch_preencode_reloc_func = "arch_#{@header.machine.downcase}_preencode_reloc"
|
|
267
|
+
send arch_preencode_reloc_func if respond_to? arch_preencode_reloc_func
|
|
268
|
+
|
|
269
|
+
list = @relocations.find_all { |r| r.type == 'JMP_SLOT' }
|
|
270
|
+
if not list.empty? or @relocations.empty?
|
|
271
|
+
if list.find { |r| r.addend }
|
|
272
|
+
stype = 'RELA'
|
|
273
|
+
sname = '.rela.plt'
|
|
274
|
+
else
|
|
275
|
+
stype = 'REL'
|
|
276
|
+
sname = '.rel.plt'
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
if not relplt = @sections.find { |s| s.type == stype and s.name == sname }
|
|
280
|
+
relplt = Section.new
|
|
281
|
+
relplt.name = sname
|
|
282
|
+
relplt.flags = ['ALLOC']
|
|
283
|
+
encode_add_section relplt
|
|
284
|
+
end
|
|
285
|
+
relplt.encoded = EncodedData.new('', :export => {'_REL_PLT' => 0})
|
|
286
|
+
list.each { |r| relplt.encoded << r.encode(self) }
|
|
287
|
+
@tag['JMPREL'] = label_at(relplt.encoded, 0)
|
|
288
|
+
@tag['PLTRELSZ'] = relplt.encoded.virtsize
|
|
289
|
+
@tag['PLTREL'] = relplt.type = stype
|
|
290
|
+
@tag[stype + 'ENT'] = relplt.entsize = relplt.addralign = (stype == 'REL' ? Relocation.size(self) : RelocationAddend.size(self))
|
|
291
|
+
encode_check_section_size relplt
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
list = @relocations.find_all { |r| r.type != 'JMP_SLOT' and not r.addend }
|
|
295
|
+
if not list.empty?
|
|
296
|
+
if not @tag['TEXTREL'] and @sections.find { |s_|
|
|
297
|
+
s_.encoded and e = s_.encoded.inv_export[0] and not s_.flags.include? 'WRITE' and
|
|
298
|
+
list.find { |r| Expression[r.offset, :-, e].reduce.kind_of? ::Integer }
|
|
299
|
+
# TODO need to check with r.offset.bind(elf_binding)
|
|
300
|
+
}
|
|
301
|
+
@tag['TEXTREL'] = 0
|
|
302
|
+
end
|
|
303
|
+
if not rel = @sections.find { |s_| s_.type == 'REL' and s_.name == '.rel.dyn' }
|
|
304
|
+
rel = Section.new
|
|
305
|
+
rel.name = '.rel.dyn'
|
|
306
|
+
rel.type = 'REL'
|
|
307
|
+
rel.flags = ['ALLOC']
|
|
308
|
+
rel.entsize = rel.addralign = Relocation.size(self)
|
|
309
|
+
encode_add_section rel
|
|
310
|
+
end
|
|
311
|
+
rel.encoded = EncodedData.new
|
|
312
|
+
list.each { |r| rel.encoded << r.encode(self) }
|
|
313
|
+
@tag['REL'] = label_at(rel.encoded, 0)
|
|
314
|
+
@tag['RELENT'] = Relocation.size(self)
|
|
315
|
+
@tag['RELSZ'] = rel.encoded.virtsize
|
|
316
|
+
encode_check_section_size rel
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
list = @relocations.find_all { |r| r.type != 'JMP_SLOT' and r.addend }
|
|
320
|
+
if not list.empty?
|
|
321
|
+
if not rela = @sections.find { |s_| s_.type == 'RELA' and s_.name == '.rela.dyn' }
|
|
322
|
+
rela = Section.new
|
|
323
|
+
rela.name = '.rela.dyn'
|
|
324
|
+
rela.type = 'RELA'
|
|
325
|
+
rela.flags = ['ALLOC']
|
|
326
|
+
rela.entsize = rela.addralign = RelocationAddend.size(self)
|
|
327
|
+
encode_add_section rela
|
|
328
|
+
end
|
|
329
|
+
rela.encoded = EncodedData.new
|
|
330
|
+
list.each { |r| rela.encoded << r.encode(self) }
|
|
331
|
+
@tag['RELA'] = label_at(rela.encoded, 0)
|
|
332
|
+
@tag['RELAENT'] = RelocationAddend.size(self)
|
|
333
|
+
@tag['RELASZ'] = rela.encoded.virtsize
|
|
334
|
+
encode_check_section_size rela
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# creates the .plt/.got from the @relocations
|
|
339
|
+
def arch_386_preencode_reloc
|
|
340
|
+
# if .got.plt does not exist, the dynamic loader segfaults
|
|
341
|
+
if not gotplt = @sections.find { |s| s.type == 'PROGBITS' and s.name == '.got.plt' }
|
|
342
|
+
gotplt = Section.new
|
|
343
|
+
gotplt.name = '.got.plt'
|
|
344
|
+
gotplt.type = 'PROGBITS'
|
|
345
|
+
gotplt.flags = %w[ALLOC WRITE]
|
|
346
|
+
gotplt.addralign = @bitsize/8
|
|
347
|
+
# _DYNAMIC is not base-relocated at runtime
|
|
348
|
+
encode_add_section gotplt
|
|
349
|
+
end
|
|
350
|
+
gotplt.encoded ||= (EncodedData.new('', :export => {'_PLT_GOT' => 0}) << encode_xword('_DYNAMIC') << encode_xword(0) << encode_xword(0))
|
|
351
|
+
@tag['PLTGOT'] = label_at(gotplt.encoded, 0)
|
|
352
|
+
plt = nil
|
|
353
|
+
|
|
354
|
+
shellcode = lambda { |c| Shellcode.new(@cpu).share_namespace(self).assemble(c).encoded }
|
|
355
|
+
|
|
356
|
+
@relocations.dup.each { |r|
|
|
357
|
+
case r.type
|
|
358
|
+
when 'PC32'
|
|
359
|
+
next if not r.symbol
|
|
360
|
+
|
|
361
|
+
if r.symbol.type != 'FUNC'
|
|
362
|
+
# external data xref: generate a GOT entry
|
|
363
|
+
# XXX reuse .got.plt ?
|
|
364
|
+
if not got ||= @sections.find { |s| s.type == 'PROGBITS' and s.name == '.got' }
|
|
365
|
+
got = Section.new
|
|
366
|
+
got.name = '.got'
|
|
367
|
+
got.type = 'PROGBITS'
|
|
368
|
+
got.flags = %w[ALLOC WRITE]
|
|
369
|
+
got.addralign = @bitsize/8
|
|
370
|
+
got.encoded = EncodedData.new
|
|
371
|
+
encode_add_section got
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
prevoffset = r.offset
|
|
375
|
+
gotlabel = r.symbol.name + '_got_entry'
|
|
376
|
+
if not got.encoded.export[gotlabel]
|
|
377
|
+
# create the got thunk
|
|
378
|
+
got.encoded.add_export(gotlabel, got.encoded.length)
|
|
379
|
+
got.encoded << encode_xword(0)
|
|
380
|
+
|
|
381
|
+
# transform the reloc PC32 => GLOB_DAT
|
|
382
|
+
r.type = 'GLOB_DAT'
|
|
383
|
+
r.offset = Expression[gotlabel]
|
|
384
|
+
r.addend = 0 if @bitsize == 64
|
|
385
|
+
else
|
|
386
|
+
@relocations.delete r
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
# prevoffset is label_section_start + int_section_offset
|
|
390
|
+
target_s = @sections.find { |s| s.encoded and s.encoded.export[prevoffset.lexpr] == 0 }
|
|
391
|
+
rel = target_s.encoded.reloc[prevoffset.rexpr]
|
|
392
|
+
# [foo] => [foo - reloc_addr + gotlabel]
|
|
393
|
+
|
|
394
|
+
rel.target = Expression[[rel.target, :-, prevoffset], :+, gotlabel]
|
|
395
|
+
next
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
# convert to .plt entry
|
|
399
|
+
#
|
|
400
|
+
# [.plt header]
|
|
401
|
+
# plt_start: # caller set ebx = gotplt if generate_PIC
|
|
402
|
+
# push [gotplt+4]
|
|
403
|
+
# jmp [gotplt+8]
|
|
404
|
+
#
|
|
405
|
+
# [.plt thunk]
|
|
406
|
+
# some_func_thunk:
|
|
407
|
+
# jmp [gotplt+func_got_offset]
|
|
408
|
+
# some_func_got_default:
|
|
409
|
+
# push some_func_jmpslot_offset_in_.rel.plt
|
|
410
|
+
# jmp plt_start
|
|
411
|
+
#
|
|
412
|
+
# [.got.plt header]
|
|
413
|
+
# dd _DYNAMIC
|
|
414
|
+
# dd 0 # rewritten to GOTPLT? by ld-linux
|
|
415
|
+
# dd 0 # rewritten to dlresolve_inplace by ld-linux
|
|
416
|
+
#
|
|
417
|
+
# [.got.plt + func_got_offset]
|
|
418
|
+
# dd some_func_got_default # lazily rewritten to the real addr of some_func by jmp dlresolve_inplace
|
|
419
|
+
# # base_relocated ?
|
|
420
|
+
|
|
421
|
+
# in the PIC case, _dlresolve imposes us to use the ebx register (which may not be saved by the calling function..)
|
|
422
|
+
# also geteip trashes eax, which may interfere with regparm(3)
|
|
423
|
+
base = @cpu.generate_PIC ? @bitsize == 32 ? 'ebx' : 'rip-$_+_PLT_GOT' : '_PLT_GOT'
|
|
424
|
+
if not plt ||= @sections.find { |s| s.type == 'PROGBITS' and s.name == '.plt' }
|
|
425
|
+
plt = Section.new
|
|
426
|
+
plt.name = '.plt'
|
|
427
|
+
plt.type = 'PROGBITS'
|
|
428
|
+
plt.flags = %w[ALLOC EXECINSTR]
|
|
429
|
+
plt.addralign = @bitsize/8
|
|
430
|
+
plt.encoded = EncodedData.new
|
|
431
|
+
sz = @bitsize/8
|
|
432
|
+
ptqual = @bitsize == 32 ? 'dword' : 'qword'
|
|
433
|
+
plt.encoded << shellcode["metasm_plt_start:\npush #{ptqual} ptr [#{base}+#{sz}]\njmp #{ptqual} ptr [#{base}+#{2*sz}]"]
|
|
434
|
+
if @cpu.generate_PIC and @bitsize == 32 and not @sections.find { |s| s.encoded and s.encoded.export['metasm_intern_geteip'] }
|
|
435
|
+
plt.encoded << shellcode["metasm_intern_geteip:\ncall 42f\n42: pop eax\nsub eax, 42b-metasm_intern_geteip\nret"]
|
|
436
|
+
end
|
|
437
|
+
encode_add_section plt
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
prevoffset = r.offset
|
|
441
|
+
pltlabel = r.symbol.name + '_plt_thunk'
|
|
442
|
+
if not plt.encoded.export[pltlabel]
|
|
443
|
+
# create the plt thunk
|
|
444
|
+
plt.encoded.add_export pltlabel, plt.encoded.length
|
|
445
|
+
if @cpu.generate_PIC and @bitsize == 32
|
|
446
|
+
plt.encoded << shellcode["call metasm_intern_geteip\nlea #{base}, [eax+_PLT_GOT-metasm_intern_geteip]"]
|
|
447
|
+
end
|
|
448
|
+
plt.encoded << shellcode["jmp [#{base} + #{gotplt.encoded.length}]"]
|
|
449
|
+
plt.encoded.add_export r.symbol.name+'_plt_default', plt.encoded.length
|
|
450
|
+
reloffset = @relocations.find_all { |rr| rr.type == 'JMP_SLOT' }.length
|
|
451
|
+
reloffset *= Relocation.size(self) if @bitsize == 32
|
|
452
|
+
plt.encoded << shellcode["push #{reloffset}\njmp metasm_plt_start"]
|
|
453
|
+
|
|
454
|
+
# transform the reloc PC32 => JMP_SLOT
|
|
455
|
+
r.type = 'JMP_SLOT'
|
|
456
|
+
r.offset = Expression['_PLT_GOT', :+, gotplt.encoded.length]
|
|
457
|
+
r.addend = 0 if @bitsize == 64
|
|
458
|
+
|
|
459
|
+
gotplt.encoded << encode_xword(r.symbol.name + '_plt_default')
|
|
460
|
+
else
|
|
461
|
+
@relocations.delete r
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
# mutate the original relocation
|
|
465
|
+
# XXX relies on the exact form of r.target from arch_create_reloc
|
|
466
|
+
target_s = @sections.find { |s| s.encoded and s.encoded.export[prevoffset.lexpr] == 0 }
|
|
467
|
+
rel = target_s.encoded.reloc[prevoffset.rexpr]
|
|
468
|
+
rel.target = Expression[[[rel.target, :-, prevoffset.rexpr], :-, label_at(target_s.encoded, 0)], :+, pltlabel]
|
|
469
|
+
|
|
470
|
+
# when 'GOTOFF', 'GOTPC'
|
|
471
|
+
end
|
|
472
|
+
}
|
|
473
|
+
encode_check_section_size gotplt
|
|
474
|
+
encode_check_section_size plt if plt
|
|
475
|
+
#encode_check_section_size got if got
|
|
476
|
+
end
|
|
477
|
+
alias arch_x86_64_preencode_reloc arch_386_preencode_reloc
|
|
478
|
+
|
|
479
|
+
# encodes the .dynamic section, creates .hash/.gnu.hash/.rel/.rela/.dynsym/.strtab/.init,*_array as needed
|
|
480
|
+
def encode_segments_dynamic
|
|
481
|
+
if not strtab = @sections.find { |s| s.type == 'STRTAB' and s.flags.include? 'ALLOC' }
|
|
482
|
+
strtab = Section.new
|
|
483
|
+
strtab.name = '.dynstr'
|
|
484
|
+
strtab.addralign = 1
|
|
485
|
+
strtab.type = 'STRTAB'
|
|
486
|
+
strtab.flags = ['ALLOC']
|
|
487
|
+
encode_add_section strtab
|
|
488
|
+
end
|
|
489
|
+
strtab.encoded = EncodedData.new << 0
|
|
490
|
+
@tag['STRTAB'] = label_at(strtab.encoded, 0)
|
|
491
|
+
|
|
492
|
+
if not dynamic = @sections.find { |s| s.type == 'DYNAMIC' }
|
|
493
|
+
dynamic = Section.new
|
|
494
|
+
dynamic.name = '.dynamic'
|
|
495
|
+
dynamic.type = 'DYNAMIC'
|
|
496
|
+
dynamic.flags = %w[WRITE ALLOC] # XXX why write ?
|
|
497
|
+
dynamic.addralign = dynamic.entsize = @bitsize / 8 * 2
|
|
498
|
+
dynamic.link = strtab
|
|
499
|
+
encode_add_section dynamic
|
|
500
|
+
end
|
|
501
|
+
dynamic.encoded = EncodedData.new('', :export => {'_DYNAMIC' => 0})
|
|
502
|
+
|
|
503
|
+
encode_tag = lambda { |k, v|
|
|
504
|
+
dynamic.encoded <<
|
|
505
|
+
encode_sxword(int_from_hash(k, DYNAMIC_TAG)) <<
|
|
506
|
+
encode_xword(v)
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
# find or create string in strtab
|
|
510
|
+
add_str = lambda { |n|
|
|
511
|
+
if n and n != '' and not ret = strtab.encoded.data.index(n + 0.chr)
|
|
512
|
+
ret = strtab.encoded.virtsize
|
|
513
|
+
strtab.encoded << n << 0
|
|
514
|
+
end
|
|
515
|
+
ret || 0
|
|
516
|
+
}
|
|
517
|
+
@tag.keys.each { |k|
|
|
518
|
+
case k
|
|
519
|
+
when 'NEEDED'; @tag[k].each { |n| encode_tag[k, add_str[n]] }
|
|
520
|
+
when 'SONAME', 'RPATH', 'RUNPATH'; encode_tag[k, add_str[@tag[k]]]
|
|
521
|
+
when 'INIT_ARRAY', 'FINI_ARRAY', 'PREINIT_ARRAY' # build section containing the array
|
|
522
|
+
if not ar = @sections.find { |s| s.name == '.' + k.downcase }
|
|
523
|
+
ar = Section.new
|
|
524
|
+
ar.name = '.' + k.downcase
|
|
525
|
+
ar.type = k
|
|
526
|
+
ar.addralign = ar.entsize = @bitsize/8
|
|
527
|
+
ar.flags = %w[WRITE ALLOC]
|
|
528
|
+
ar.encoded = EncodedData.new
|
|
529
|
+
encode_add_section ar # insert before encoding syms/relocs (which need section indexes)
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
# fill these later, but create the base relocs now
|
|
533
|
+
arch_create_reloc_func = "arch_#{@header.machine.downcase}_create_reloc"
|
|
534
|
+
next if not respond_to?(arch_create_reloc_func)
|
|
535
|
+
curaddr = label_at(@encoded, 0, 'elf_start')
|
|
536
|
+
fkbind = {}
|
|
537
|
+
@sections.each { |s|
|
|
538
|
+
next if not s.encoded
|
|
539
|
+
fkbind.update s.encoded.binding(Expression[curaddr, :+, 1])
|
|
540
|
+
}
|
|
541
|
+
@relocations ||= []
|
|
542
|
+
off = ar.encoded.length
|
|
543
|
+
@tag[k].each { |a|
|
|
544
|
+
rel = Metasm::Relocation.new(Expression[a], "u#@bitsize".to_sym, @endianness)
|
|
545
|
+
send(arch_create_reloc_func, ar, off, fkbind, rel)
|
|
546
|
+
off += @bitsize/8
|
|
547
|
+
}
|
|
548
|
+
end
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
encode_reorder_symbols
|
|
552
|
+
encode_gnu_hash
|
|
553
|
+
encode_hash
|
|
554
|
+
encode_segments_relocs
|
|
555
|
+
dynsym = encode_segments_symbols(strtab)
|
|
556
|
+
@sections.find_all { |s| %w[HASH GNU_HASH REL RELA].include? s.type }.each { |s| s.link = dynsym }
|
|
557
|
+
|
|
558
|
+
encode_check_section_size strtab
|
|
559
|
+
|
|
560
|
+
# XXX any order needed ?
|
|
561
|
+
@tag.keys.each { |k|
|
|
562
|
+
case k
|
|
563
|
+
when Integer # unknown tags = array of values
|
|
564
|
+
@tag[k].each { |n| encode_tag[k, n] }
|
|
565
|
+
when 'PLTREL'; encode_tag[k, int_from_hash(@tag[k], DYNAMIC_TAG)]
|
|
566
|
+
when 'FLAGS'; encode_tag[k, bits_from_hash(@tag[k], DYNAMIC_FLAGS)]
|
|
567
|
+
when 'FLAGS_1'; encode_tag[k, bits_from_hash(@tag[k], DYNAMIC_FLAGS_1)]
|
|
568
|
+
when 'FEATURES_1'; encode_tag[k, bits_from_hash(@tag[k], DYNAMIC_FEATURES_1)]
|
|
569
|
+
when 'NULL' # keep last
|
|
570
|
+
when 'STRTAB'
|
|
571
|
+
encode_tag[k, @tag[k]]
|
|
572
|
+
encode_tag['STRSZ', strtab.encoded.size]
|
|
573
|
+
when 'INIT_ARRAY', 'FINI_ARRAY', 'PREINIT_ARRAY' # build section containing the array
|
|
574
|
+
ar = @sections.find { |s| s.name == '.' + k.downcase }
|
|
575
|
+
@tag[k].each { |p| ar.encoded << encode_addr(p) }
|
|
576
|
+
encode_check_section_size ar
|
|
577
|
+
encode_tag[k, label_at(ar.encoded, 0)]
|
|
578
|
+
encode_tag[k + 'SZ', ar.encoded.virtsize]
|
|
579
|
+
when 'NEEDED', 'SONAME', 'RPATH', 'RUNPATH' # already handled
|
|
580
|
+
else
|
|
581
|
+
encode_tag[k, @tag[k]]
|
|
582
|
+
end
|
|
583
|
+
}
|
|
584
|
+
encode_tag['NULL', @tag['NULL'] || 0]
|
|
585
|
+
|
|
586
|
+
encode_check_section_size dynamic
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
# creates the undef symbol list from the section.encoded.reloc and a list of known exported symbols (e.g. from libc)
|
|
590
|
+
# also populates @tag['NEEDED']
|
|
591
|
+
def automagic_symbols
|
|
592
|
+
GNUExports rescue return # autorequire
|
|
593
|
+
autoexports = GNUExports::EXPORT.dup
|
|
594
|
+
@sections.each { |s|
|
|
595
|
+
next if not s.encoded
|
|
596
|
+
s.encoded.export.keys.each { |e| autoexports.delete e }
|
|
597
|
+
}
|
|
598
|
+
@sections.each { |s|
|
|
599
|
+
next if not s.encoded
|
|
600
|
+
s.encoded.reloc.each_value { |r|
|
|
601
|
+
t = Expression[r.target.reduce]
|
|
602
|
+
if t.op == :+ and t.rexpr.kind_of? Expression and t.rexpr.op == :- and not t.rexpr.lexpr and
|
|
603
|
+
t.rexpr.rexpr.kind_of?(::String) and t.lexpr.kind_of?(::String)
|
|
604
|
+
symname = t.lexpr
|
|
605
|
+
else
|
|
606
|
+
symname = t.reduce_rec
|
|
607
|
+
end
|
|
608
|
+
next if not dll = autoexports[symname]
|
|
609
|
+
if not @symbols.find { |sym| sym.name == symname }
|
|
610
|
+
@tag['NEEDED'] ||= []
|
|
611
|
+
@tag['NEEDED'] |= [dll]
|
|
612
|
+
sym = Symbol.new
|
|
613
|
+
sym.shndx = 'UNDEF'
|
|
614
|
+
sym.type = 'FUNC'
|
|
615
|
+
sym.name = symname
|
|
616
|
+
sym.bind = 'GLOBAL'
|
|
617
|
+
@symbols << sym
|
|
618
|
+
end
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
# reads the existing segment/sections.encoded and populate @relocations from the encoded.reloc hash
|
|
624
|
+
def create_relocations
|
|
625
|
+
@relocations = []
|
|
626
|
+
|
|
627
|
+
arch_create_reloc_func = "arch_#{@header.machine.downcase}_create_reloc"
|
|
628
|
+
if not respond_to? arch_create_reloc_func
|
|
629
|
+
puts "Elf: create_reloc: unhandled architecture #{@header.machine}" if $VERBOSE
|
|
630
|
+
return
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
# create a fake binding with all our own symbols
|
|
634
|
+
# not foolproof, should work in most cases
|
|
635
|
+
curaddr = label_at(@encoded, 0, 'elf_start')
|
|
636
|
+
binding = {'_DYNAMIC' => 0, '_GOT' => 0} # XXX
|
|
637
|
+
@sections.each { |s|
|
|
638
|
+
next if not s.encoded
|
|
639
|
+
binding.update s.encoded.binding(curaddr)
|
|
640
|
+
curaddr = Expression[curaddr, :+, s.encoded.virtsize]
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
@sections.each { |s|
|
|
644
|
+
next if not s.encoded
|
|
645
|
+
s.encoded.reloc.each { |off, rel|
|
|
646
|
+
t = rel.target.bind(binding).reduce
|
|
647
|
+
next if not t.kind_of? Expression # XXX segment_encode only
|
|
648
|
+
send(arch_create_reloc_func, s, off, binding)
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
# references to FUNC symbols are transformed to JMPSLOT relocations (aka call to .plt)
|
|
654
|
+
# TODO ET_REL support
|
|
655
|
+
def arch_386_create_reloc(section, off, binding, rel=nil)
|
|
656
|
+
rel ||= section.encoded.reloc[off]
|
|
657
|
+
if rel.endianness != @endianness or not [:u32, :i32, :a32].include? rel.type
|
|
658
|
+
puts "ELF: 386_create_reloc: ignoring reloc #{rel.target} in #{section.name}: bad reloc type" if $VERBOSE
|
|
659
|
+
return
|
|
660
|
+
end
|
|
661
|
+
startaddr = label_at(@encoded, 0)
|
|
662
|
+
r = Relocation.new
|
|
663
|
+
r.offset = Expression[label_at(section.encoded, 0, 'sect_start'), :+, off]
|
|
664
|
+
if Expression[rel.target, :-, startaddr].bind(binding).reduce.kind_of?(::Integer)
|
|
665
|
+
# this location is relative to the base load address of the ELF
|
|
666
|
+
r.type = 'RELATIVE'
|
|
667
|
+
else
|
|
668
|
+
et = rel.target.externals
|
|
669
|
+
extern = et.find_all { |name| not binding[name] }
|
|
670
|
+
if extern.length != 1
|
|
671
|
+
puts "ELF: 386_create_reloc: ignoring reloc #{rel.target} in #{section.name}: #{extern.inspect} unknown" if $VERBOSE
|
|
672
|
+
return
|
|
673
|
+
end
|
|
674
|
+
if not sym = @symbols.find { |s| s.name == extern.first }
|
|
675
|
+
puts "ELF: 386_create_reloc: ignoring reloc #{rel.target} in #{section.name}: undefined symbol #{extern.first}" if $VERBOSE
|
|
676
|
+
return
|
|
677
|
+
end
|
|
678
|
+
r.symbol = sym
|
|
679
|
+
rel.target = Expression[rel.target, :-, sym.name]
|
|
680
|
+
if rel.target.bind(binding).reduce.kind_of? ::Integer
|
|
681
|
+
r.type = '32'
|
|
682
|
+
elsif Expression[rel.target, :+, label_at(section.encoded, 0)].bind(section.encoded.binding).reduce.kind_of? ::Integer
|
|
683
|
+
rel.target = Expression[[rel.target, :+, label_at(section.encoded, 0)], :+, off]
|
|
684
|
+
r.type = 'PC32'
|
|
685
|
+
# TODO tls ?
|
|
686
|
+
else
|
|
687
|
+
puts "ELF: 386_create_reloc: ignoring reloc #{sym.name} + #{rel.target}: cannot find matching standard reloc type" if $VERBOSE
|
|
688
|
+
return
|
|
689
|
+
end
|
|
690
|
+
end
|
|
691
|
+
@relocations << r
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
def arch_x86_64_create_reloc(section, off, binding, rel=nil)
|
|
695
|
+
rel ||= section.encoded.reloc[off]
|
|
696
|
+
if rel.endianness != @endianness or not rel.type.to_s =~ /^[aiu](32|64)$/
|
|
697
|
+
puts "ELF: x86_64_create_reloc: ignoring reloc #{rel.target} in #{section.name}: bad reloc type" if $VERBOSE
|
|
698
|
+
return
|
|
699
|
+
end
|
|
700
|
+
startaddr = label_at(@encoded, 0)
|
|
701
|
+
r = RelocationAddend.new
|
|
702
|
+
r.addend = 0
|
|
703
|
+
r.offset = Expression[label_at(section.encoded, 0, 'sect_start'), :+, off]
|
|
704
|
+
if Expression[rel.target, :-, startaddr].bind(binding).reduce.kind_of?(::Integer)
|
|
705
|
+
# this location is relative to the base load address of the ELF
|
|
706
|
+
if rel.length != 8
|
|
707
|
+
puts "ELF: x86_64_create_reloc: ignoring reloc #{rel.target} in #{section.name}: relative non-x64" if $VERBOSE
|
|
708
|
+
return
|
|
709
|
+
end
|
|
710
|
+
r.type = 'RELATIVE'
|
|
711
|
+
else
|
|
712
|
+
et = rel.target.externals
|
|
713
|
+
extern = et.find_all { |name| not binding[name] }
|
|
714
|
+
if extern.length != 1
|
|
715
|
+
puts "ELF: x86_64_create_reloc: ignoring reloc #{rel.target} in #{section.name}: #{extern.inspect} unknown" if $VERBOSE
|
|
716
|
+
return
|
|
717
|
+
end
|
|
718
|
+
if not sym = @symbols.find { |s| s.name == extern.first }
|
|
719
|
+
puts "ELF: x86_64_create_reloc: ignoring reloc #{rel.target} in #{section.name}: undefined symbol #{extern.first}" if $VERBOSE
|
|
720
|
+
return
|
|
721
|
+
end
|
|
722
|
+
r.symbol = sym
|
|
723
|
+
rel.target = Expression[rel.target, :-, sym.name]
|
|
724
|
+
if rel.target.bind(binding).reduce.kind_of? ::Integer
|
|
725
|
+
r.type = '64' # XXX check that
|
|
726
|
+
elsif Expression[rel.target, :+, label_at(section.encoded, 0)].bind(section.encoded.binding).reduce.kind_of? ::Integer
|
|
727
|
+
rel.target = Expression[[rel.target, :+, label_at(section.encoded, 0)], :+, off]
|
|
728
|
+
r.type = 'PC32' # XXX
|
|
729
|
+
# TODO tls ?
|
|
730
|
+
else
|
|
731
|
+
puts "ELF: x86_64_create_reloc: ignoring reloc #{sym.name} + #{rel.target}: cannot find matching standard reloc type" if $VERBOSE
|
|
732
|
+
return
|
|
733
|
+
end
|
|
734
|
+
end
|
|
735
|
+
r.addend = Expression[rel.target]
|
|
736
|
+
#section.encoded.reloc.delete off
|
|
737
|
+
@relocations << r
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
# resets the fields of the elf headers that should be recalculated, eg phdr offset
|
|
741
|
+
def invalidate_header
|
|
742
|
+
@header.shoff = @header.shnum = nil
|
|
743
|
+
@header.phoff = @header.phnum = nil
|
|
744
|
+
@header.shstrndx = nil
|
|
745
|
+
@sections.to_a.each { |s|
|
|
746
|
+
s.name_p = nil
|
|
747
|
+
s.offset = nil
|
|
748
|
+
}
|
|
749
|
+
@segments.to_a.each { |s|
|
|
750
|
+
s.offset = nil
|
|
751
|
+
}
|
|
752
|
+
self
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
# put every ALLOC section in a segment, create segments if needed
|
|
756
|
+
# sections with a good offset within a segment are ignored
|
|
757
|
+
def encode_make_segments_from_sections
|
|
758
|
+
# fixed addresses first
|
|
759
|
+
seclist = @sections.find_all { |sec| sec.addr.kind_of? Integer }.sort_by { |sec| sec.addr } | @sections
|
|
760
|
+
seclist.each { |sec|
|
|
761
|
+
next if not sec.flags.to_a.include? 'ALLOC'
|
|
762
|
+
|
|
763
|
+
# check if we fit in an existing segment
|
|
764
|
+
loadsegs = @segments.find_all { |seg_| seg_.type == 'LOAD' }
|
|
765
|
+
|
|
766
|
+
if sec.addr.kind_of?(::Integer) and seg = loadsegs.find { |seg_|
|
|
767
|
+
seg_.vaddr.kind_of?(::Integer) and seg_.vaddr <= sec.addr and seg_.vaddr + seg_.memsz >= sec.addr + sec.size
|
|
768
|
+
}
|
|
769
|
+
# sections is already inside a segment: we're reencoding an ELF, just patch the section in the segment
|
|
770
|
+
seg.encoded[sec.addr - seg.vaddr, sec.size] = sec.encoded if sec.encoded
|
|
771
|
+
next
|
|
772
|
+
end
|
|
773
|
+
|
|
774
|
+
if not seg = loadsegs.find { |seg_|
|
|
775
|
+
sec.flags.to_a.include?('WRITE') == seg_.flags.to_a.include?('W') and
|
|
776
|
+
#sec.flags.to_a.include?('EXECINSTR') == seg_.flags.to_a.include?('X') and
|
|
777
|
+
not seg_.memsz and
|
|
778
|
+
not loadsegs[loadsegs.index(seg_)+1..-1].find { |sseg|
|
|
779
|
+
# check if another segment would overlap if we add the sec to seg_
|
|
780
|
+
o = Expression[sseg.vaddr, :-, [seg_.vaddr, :+, seg_.encoded.length+sec.encoded.length]].reduce
|
|
781
|
+
o.kind_of? ::Integer and o < 0
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
# nope, create a new one
|
|
785
|
+
seg = Segment.new
|
|
786
|
+
seg.type = 'LOAD'
|
|
787
|
+
seg.flags = ['R']
|
|
788
|
+
seg.flags << 'W' if sec.flags.include? 'WRITE'
|
|
789
|
+
seg.align = 0x1000
|
|
790
|
+
seg.encoded = EncodedData.new
|
|
791
|
+
seg.offset = new_label('segment_offset')
|
|
792
|
+
seg.vaddr = sec.addr || new_label('segment_address')
|
|
793
|
+
@segments << seg
|
|
794
|
+
end
|
|
795
|
+
seg.flags |= ['X'] if sec.flags.include? 'EXECINSTR'
|
|
796
|
+
seg.encoded.align sec.addralign if sec.addralign
|
|
797
|
+
sec.addr = Expression[seg.vaddr, :+, seg.encoded.length]
|
|
798
|
+
sec.offset = Expression[seg.offset, :+, seg.encoded.length]
|
|
799
|
+
seg.encoded << sec.encoded
|
|
800
|
+
}
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
# create the relocations from the sections.encoded.reloc
|
|
804
|
+
# create the dynamic sections
|
|
805
|
+
# put sections/phdr in PT_LOAD segments
|
|
806
|
+
# link
|
|
807
|
+
# TODO support mapped PHDR, obey section-specified base address, handle NOBITS
|
|
808
|
+
# encode ET_REL
|
|
809
|
+
def encode(type='DYN')
|
|
810
|
+
@header.type ||= {:bin => 'EXEC', :lib => 'DYN', :obj => 'REL'}.fetch(type, type)
|
|
811
|
+
@header.machine ||= case @cpu.shortname
|
|
812
|
+
when 'x64'; 'X86_64'
|
|
813
|
+
when 'ia32'; '386'
|
|
814
|
+
when 'mips'; 'MIPS'
|
|
815
|
+
when 'powerpc'; 'PPC'
|
|
816
|
+
when 'arm'; 'ARM'
|
|
817
|
+
end
|
|
818
|
+
|
|
819
|
+
if @header.type == 'REL'
|
|
820
|
+
raise 'ET_REL encoding not supported atm, come back later'
|
|
821
|
+
end
|
|
822
|
+
|
|
823
|
+
@encoded = EncodedData.new
|
|
824
|
+
if @header.type != 'EXEC' or @segments.find { |i| i.type == 'INTERP' }
|
|
825
|
+
# create a .dynamic section unless we are an ET_EXEC with .nointerp
|
|
826
|
+
automagic_symbols
|
|
827
|
+
create_relocations
|
|
828
|
+
encode_segments_dynamic
|
|
829
|
+
end
|
|
830
|
+
|
|
831
|
+
@segments.delete_if { |s| s.type == 'INTERP' } if not @header.entry
|
|
832
|
+
|
|
833
|
+
encode_make_segments_from_sections
|
|
834
|
+
|
|
835
|
+
loadsegs = @segments.find_all { |seg_| seg_.type == 'LOAD' }
|
|
836
|
+
|
|
837
|
+
# ensure PT_INTERP is mapped if present
|
|
838
|
+
if interp = @segments.find { |i| i.type == 'INTERP' }
|
|
839
|
+
if not seg = loadsegs.find { |seg_| not seg_.memsz and interp.flags & seg_.flags == interp.flags and
|
|
840
|
+
not loadsegs[loadsegs.index(seg_)+1..-1].find { |sseg|
|
|
841
|
+
o = Expression[sseg.vaddr, :-, [seg_.vaddr, :+, seg_.encoded.length+interp.encoded.length]].reduce
|
|
842
|
+
o.kind_of? ::Integer and o < 0
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
seg = Segment.new
|
|
846
|
+
seg.type = 'LOAD'
|
|
847
|
+
seg.flags = interp.flags.dup
|
|
848
|
+
seg.align = 0x1000
|
|
849
|
+
seg.encoded = EncodedData.new
|
|
850
|
+
seg.offset = new_label('segment_offset')
|
|
851
|
+
seg.vaddr = new_label('segment_address')
|
|
852
|
+
loadsegs << seg
|
|
853
|
+
@segments << seg
|
|
854
|
+
end
|
|
855
|
+
interp.vaddr = Expression[seg.vaddr, :+, seg.encoded.length]
|
|
856
|
+
interp.offset = Expression[seg.offset, :+, seg.encoded.length]
|
|
857
|
+
seg.encoded << interp.encoded
|
|
858
|
+
interp.encoded = nil
|
|
859
|
+
end
|
|
860
|
+
|
|
861
|
+
# ensure last PT_LOAD is writeable (used for bss)
|
|
862
|
+
seg = loadsegs.last
|
|
863
|
+
if not seg or not seg.flags.include? 'W'
|
|
864
|
+
seg = Segment.new
|
|
865
|
+
seg.type = 'LOAD'
|
|
866
|
+
seg.flags = ['R', 'W']
|
|
867
|
+
seg.encoded = EncodedData.new
|
|
868
|
+
loadsegs << seg
|
|
869
|
+
@segments << seg
|
|
870
|
+
end
|
|
871
|
+
|
|
872
|
+
# add dynamic segment
|
|
873
|
+
if ds = @sections.find { |sec| sec.type == 'DYNAMIC' }
|
|
874
|
+
ds.set_default_values self
|
|
875
|
+
seg = Segment.new
|
|
876
|
+
seg.type = 'DYNAMIC'
|
|
877
|
+
seg.flags = ['R', 'W']
|
|
878
|
+
seg.offset = ds.offset
|
|
879
|
+
seg.vaddr = ds.addr
|
|
880
|
+
seg.memsz = seg.filesz = ds.size
|
|
881
|
+
@segments << seg
|
|
882
|
+
end
|
|
883
|
+
|
|
884
|
+
# use variables in the first segment descriptor, to allow fixup later
|
|
885
|
+
# (when we'll be able to include the program header)
|
|
886
|
+
if first_seg = loadsegs.first
|
|
887
|
+
first_seg_oaddr = first_seg.vaddr # section's vaddr depend on oaddr
|
|
888
|
+
first_seg_off = first_seg.offset
|
|
889
|
+
first_seg.vaddr = new_label('segvaddr')
|
|
890
|
+
first_seg.offset = new_label('segoff')
|
|
891
|
+
first_seg.memsz = new_label('segmemsz')
|
|
892
|
+
first_seg.filesz = new_label('segfilsz')
|
|
893
|
+
end
|
|
894
|
+
|
|
895
|
+
if first_seg and not @segments.find { |seg_| seg_.type == 'PHDR' }
|
|
896
|
+
phdr = Segment.new
|
|
897
|
+
phdr.type = 'PHDR'
|
|
898
|
+
phdr.flags = first_seg.flags
|
|
899
|
+
phdr.offset = new_label('segoff')
|
|
900
|
+
phdr.vaddr = new_label('segvaddr')
|
|
901
|
+
phdr.filesz = phdr.memsz = new_label('segmemsz')
|
|
902
|
+
@segments.unshift phdr
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
# encode section&program headers
|
|
906
|
+
if @header.shnum != 0
|
|
907
|
+
st = @sections.inject(EncodedData.new) { |edata, s| edata << s.encode(self) }
|
|
908
|
+
else
|
|
909
|
+
@header.shoff = @header.shnum = @header.shstrndx = 0
|
|
910
|
+
end
|
|
911
|
+
pt = @segments.inject(EncodedData.new) { |edata, s| edata << s.encode(self) }
|
|
912
|
+
|
|
913
|
+
binding = {}
|
|
914
|
+
@encoded << @header.encode(self)
|
|
915
|
+
@encoded.align 8
|
|
916
|
+
binding[@header.phoff] = @encoded.length
|
|
917
|
+
if phdr
|
|
918
|
+
binding[phdr.offset] = @encoded.length
|
|
919
|
+
pt.add_export phdr.vaddr, 0
|
|
920
|
+
binding[phdr.memsz] = pt.length
|
|
921
|
+
end
|
|
922
|
+
@encoded << pt
|
|
923
|
+
@encoded.align 8
|
|
924
|
+
|
|
925
|
+
if first_seg
|
|
926
|
+
# put headers into the 1st mmaped segment
|
|
927
|
+
if first_seg_oaddr.kind_of? ::Integer
|
|
928
|
+
# pad headers to align the 1st segment's data
|
|
929
|
+
@encoded.virtsize += (first_seg_oaddr - @encoded.virtsize) & 0xfff
|
|
930
|
+
addr = first_seg_oaddr - @encoded.length
|
|
931
|
+
else
|
|
932
|
+
addr = ((@header.type == 'EXEC') ? 0x08048000 : 0)
|
|
933
|
+
binding[first_seg_oaddr] = addr + @encoded.length
|
|
934
|
+
end
|
|
935
|
+
binding[first_seg_off] = @encoded.length if not first_seg_off.kind_of? ::Integer
|
|
936
|
+
first_seg.encoded = @encoded << first_seg.encoded
|
|
937
|
+
@encoded = EncodedData.new
|
|
938
|
+
binding[first_seg.memsz] = first_seg.encoded.virtsize if not first_seg.memsz.kind_of? ::Integer
|
|
939
|
+
binding[first_seg.filesz] = first_seg.encoded.rawsize if not first_seg.filesz.kind_of? ::Integer
|
|
940
|
+
end
|
|
941
|
+
|
|
942
|
+
@segments.each { |seg_|
|
|
943
|
+
next if not seg_.encoded
|
|
944
|
+
if seg_.vaddr.kind_of? ::Integer
|
|
945
|
+
raise "cannot put segment at address #{Expression[seg_.vaddr]} (now at #{Expression[addr]})" if seg_.vaddr < addr
|
|
946
|
+
addr = seg_.vaddr
|
|
947
|
+
else
|
|
948
|
+
binding[seg_.vaddr] = addr
|
|
949
|
+
end
|
|
950
|
+
# ensure seg_.vaddr & page_size == seg_.offset & page_size
|
|
951
|
+
@encoded.virtsize += (addr - @encoded.virtsize) & 0xfff
|
|
952
|
+
binding.update seg_.encoded.binding(addr)
|
|
953
|
+
binding[seg_.offset] = @encoded.length
|
|
954
|
+
seg_.encoded.align 8
|
|
955
|
+
@encoded << seg_.encoded[0, seg_.encoded.rawsize]
|
|
956
|
+
addr += seg_.encoded.length
|
|
957
|
+
|
|
958
|
+
# page break for memory permission enforcement
|
|
959
|
+
if @segments[@segments.index(seg_)+1..-1].find { |seg__| seg__.encoded and seg__.vaddr.kind_of? ::Integer }
|
|
960
|
+
addr += 0x1000 - (addr & 0xfff) if addr & 0xfff != 0 # minimize memory size
|
|
961
|
+
else
|
|
962
|
+
addr += 0x1000 # minimize file size
|
|
963
|
+
end
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
binding[@header.shoff] = @encoded.length if st
|
|
967
|
+
@encoded << st
|
|
968
|
+
@encoded.align 8
|
|
969
|
+
|
|
970
|
+
@sections.each { |sec|
|
|
971
|
+
next if not sec.encoded or sec.flags.include? 'ALLOC' # already in a segment.encoded
|
|
972
|
+
binding[sec.offset] = @encoded.length
|
|
973
|
+
binding.update sec.encoded.binding
|
|
974
|
+
@encoded << sec.encoded
|
|
975
|
+
@encoded.align 8
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
@encoded.fixup! binding
|
|
979
|
+
@encoded.data
|
|
980
|
+
end
|
|
981
|
+
|
|
982
|
+
def parse_init
|
|
983
|
+
# allow the user to specify a section, falls back to .text if none specified
|
|
984
|
+
if not defined? @cursource or not @cursource
|
|
985
|
+
@cursource = Object.new
|
|
986
|
+
class << @cursource
|
|
987
|
+
attr_accessor :elf
|
|
988
|
+
def <<(*a)
|
|
989
|
+
t = Preprocessor::Token.new(nil)
|
|
990
|
+
t.raw = '.text'
|
|
991
|
+
elf.parse_parser_instruction t
|
|
992
|
+
elf.cursource.send(:<<, *a)
|
|
993
|
+
end
|
|
994
|
+
end
|
|
995
|
+
@cursource.elf = self
|
|
996
|
+
end
|
|
997
|
+
|
|
998
|
+
@segments.delete_if { |s| s.type == 'INTERP' }
|
|
999
|
+
seg = Segment.new
|
|
1000
|
+
seg.type = 'INTERP'
|
|
1001
|
+
seg.encoded = EncodedData.new << (@bitsize == 64 ? DEFAULT_INTERP64 : DEFAULT_INTERP) << 0
|
|
1002
|
+
seg.flags = ['R']
|
|
1003
|
+
seg.memsz = seg.filesz = seg.encoded.length
|
|
1004
|
+
@segments.unshift seg
|
|
1005
|
+
|
|
1006
|
+
@source ||= {}
|
|
1007
|
+
super()
|
|
1008
|
+
end
|
|
1009
|
+
|
|
1010
|
+
# handles elf meta-instructions
|
|
1011
|
+
#
|
|
1012
|
+
# syntax:
|
|
1013
|
+
# .section "<name>" [<perms>] [base=<base>]
|
|
1014
|
+
# change current section (where normal instruction/data are put)
|
|
1015
|
+
# perms = list of 'w' 'x' 'alloc', may be prefixed by 'no'
|
|
1016
|
+
# 'r' ignored
|
|
1017
|
+
# defaults to 'alloc'
|
|
1018
|
+
# shortcuts: .text .data .rodata .bss
|
|
1019
|
+
# base: immediate expression representing the section base address
|
|
1020
|
+
# .entrypoint [<label>]
|
|
1021
|
+
# defines the program entrypoint to the specified label / current location
|
|
1022
|
+
# .global "<name>" [<label>] [<label_end>] [type=<FUNC|OBJECT|...>] [plt=<plt_label_name>] [undef]
|
|
1023
|
+
# .weak ...
|
|
1024
|
+
# .local ...
|
|
1025
|
+
# builds a symbol with specified type/scope/size, type defaults to 'func'
|
|
1026
|
+
# if plt_label_name is specified, the compiler will build an entry in the plt for this symbol, with this label (PIC & on-demand resolution)
|
|
1027
|
+
# XXX plt ignored (automagic)
|
|
1028
|
+
# .symbol [global|weak|local] "<name>" ... see .global/.weak/.local
|
|
1029
|
+
# .needed "<libpath>"
|
|
1030
|
+
# marks the elf as requiring the specified library (DT_NEEDED)
|
|
1031
|
+
# .soname "<soname>"
|
|
1032
|
+
# defines the current elf DT_SONAME (exported library name)
|
|
1033
|
+
# .interp "<interpreter_path>"
|
|
1034
|
+
# .nointerp
|
|
1035
|
+
# defines the required ELF interpreter
|
|
1036
|
+
# defaults to '/lib/ld.so'
|
|
1037
|
+
# 'nil'/'none' remove the interpreter specification
|
|
1038
|
+
# .pt_gnu_stack rw|rwx
|
|
1039
|
+
# defines the PT_GNU_STACK flag (default: unspecified, => rwx)
|
|
1040
|
+
# .init/.fini [<label>]
|
|
1041
|
+
# defines the DT_INIT/DT_FINI dynamic tags, same semantic as .entrypoint
|
|
1042
|
+
# .init_array/.fini_array/.preinit_array <label> [, <label>]*
|
|
1043
|
+
# append to the DT_*_ARRAYs
|
|
1044
|
+
#
|
|
1045
|
+
def parse_parser_instruction(instr)
|
|
1046
|
+
readstr = lambda {
|
|
1047
|
+
@lexer.skip_space
|
|
1048
|
+
t = nil
|
|
1049
|
+
raise instr, "string expected, found #{t.raw.inspect if t}" if not t = @lexer.readtok or (t.type != :string and t.type != :quoted)
|
|
1050
|
+
t.value || t.raw
|
|
1051
|
+
}
|
|
1052
|
+
check_eol = lambda {
|
|
1053
|
+
@lexer.skip_space
|
|
1054
|
+
t = nil
|
|
1055
|
+
raise instr, "eol expected, found #{t.raw.inspect if t}" if t = @lexer.nexttok and t.type != :eol
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
case instr.raw.downcase
|
|
1059
|
+
when '.text', '.data', '.rodata', '.bss'
|
|
1060
|
+
sname = instr.raw.downcase
|
|
1061
|
+
if not @sections.find { |s| s.name == sname }
|
|
1062
|
+
s = Section.new
|
|
1063
|
+
s.name = sname
|
|
1064
|
+
s.type = 'PROGBITS'
|
|
1065
|
+
s.encoded = EncodedData.new
|
|
1066
|
+
s.flags = case sname
|
|
1067
|
+
when '.text'; %w[ALLOC EXECINSTR]
|
|
1068
|
+
when '.data', '.bss'; %w[ALLOC WRITE]
|
|
1069
|
+
when '.rodata'; %w[ALLOC]
|
|
1070
|
+
end
|
|
1071
|
+
s.addralign = 8
|
|
1072
|
+
encode_add_section s
|
|
1073
|
+
end
|
|
1074
|
+
@cursource = @source[sname] ||= []
|
|
1075
|
+
check_eol[] if instr.backtrace # special case for magic @cursource
|
|
1076
|
+
|
|
1077
|
+
when '.section'
|
|
1078
|
+
# .section <section name|"section name"> [(no)wxalloc] [base=<expr>]
|
|
1079
|
+
sname = readstr[]
|
|
1080
|
+
if not s = @sections.find { |s_| s_.name == sname }
|
|
1081
|
+
s = Section.new
|
|
1082
|
+
s.type = 'PROGBITS'
|
|
1083
|
+
s.name = sname
|
|
1084
|
+
s.encoded = EncodedData.new
|
|
1085
|
+
s.flags = ['ALLOC']
|
|
1086
|
+
@sections << s
|
|
1087
|
+
end
|
|
1088
|
+
loop do
|
|
1089
|
+
@lexer.skip_space
|
|
1090
|
+
break if not tok = @lexer.nexttok or tok.type != :string
|
|
1091
|
+
case @lexer.readtok.raw.downcase
|
|
1092
|
+
when /^(no)?r?(w)?(x)?(alloc)?$/
|
|
1093
|
+
ar = []
|
|
1094
|
+
ar << 'WRITE' if $2
|
|
1095
|
+
ar << 'EXECINSTR' if $3
|
|
1096
|
+
ar << 'ALLOC' if $4
|
|
1097
|
+
if $1; s.flags -= ar
|
|
1098
|
+
else s.flags |= ar
|
|
1099
|
+
end
|
|
1100
|
+
when 'base'
|
|
1101
|
+
@lexer.skip_space
|
|
1102
|
+
@lexer.readtok if tok = @lexer.nexttok and tok.type == :punct and tok.raw == '='
|
|
1103
|
+
raise instr, 'bad section base' if not s.addr = Expression.parse(@lexer).reduce or not s.addr.kind_of? ::Integer
|
|
1104
|
+
else raise instr, 'unknown specifier'
|
|
1105
|
+
end
|
|
1106
|
+
end
|
|
1107
|
+
@cursource = @source[sname] ||= []
|
|
1108
|
+
check_eol[]
|
|
1109
|
+
|
|
1110
|
+
when '.entrypoint'
|
|
1111
|
+
# ".entrypoint <somelabel/expression>" or ".entrypoint" (here)
|
|
1112
|
+
@lexer.skip_space
|
|
1113
|
+
if tok = @lexer.nexttok and tok.type == :string
|
|
1114
|
+
raise instr if not entrypoint = Expression.parse(@lexer)
|
|
1115
|
+
else
|
|
1116
|
+
entrypoint = new_label('entrypoint')
|
|
1117
|
+
@cursource << Label.new(entrypoint, instr.backtrace.dup)
|
|
1118
|
+
end
|
|
1119
|
+
@header.entry = entrypoint
|
|
1120
|
+
check_eol[]
|
|
1121
|
+
|
|
1122
|
+
when '.global', '.weak', '.local', '.symbol'
|
|
1123
|
+
if instr.raw == '.symbol'
|
|
1124
|
+
bind = readstr[]
|
|
1125
|
+
else
|
|
1126
|
+
bind = instr.raw[1..-1]
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
s = Symbol.new
|
|
1130
|
+
s.name = readstr[]
|
|
1131
|
+
s.type = 'FUNC'
|
|
1132
|
+
s.bind = bind.upcase
|
|
1133
|
+
# define s.section ? should check the section exporting s.target, but it may not be defined now
|
|
1134
|
+
|
|
1135
|
+
# parse pseudo instruction arguments
|
|
1136
|
+
loop do
|
|
1137
|
+
@lexer.skip_space
|
|
1138
|
+
ntok = @lexer.readtok
|
|
1139
|
+
if not ntok or ntok.type == :eol
|
|
1140
|
+
@lexer.unreadtok ntok
|
|
1141
|
+
break
|
|
1142
|
+
end
|
|
1143
|
+
raise instr, "syntax error: string expected, found #{ntok.raw.inspect}" if ntok.type != :string
|
|
1144
|
+
case ntok.raw
|
|
1145
|
+
when 'undef'
|
|
1146
|
+
s.shndx = 'UNDEF'
|
|
1147
|
+
when 'plt'
|
|
1148
|
+
@lexer.skip_space
|
|
1149
|
+
ntok = @lexer.readtok
|
|
1150
|
+
raise "syntax error: = expected, found #{ntok.raw.inspect if ntok}" if not ntok or ntok.type != :punct or ntok.raw != '='
|
|
1151
|
+
@lexer.skip_space
|
|
1152
|
+
ntok = @lexer.readtok
|
|
1153
|
+
raise "syntax error: label expected, found #{ntok.raw.inspect if ntok}" if not ntok or ntok.type != :string
|
|
1154
|
+
s.thunk = ntok.raw
|
|
1155
|
+
when 'type'
|
|
1156
|
+
@lexer.skip_space
|
|
1157
|
+
ntok = @lexer.readtok
|
|
1158
|
+
raise "syntax error: = expected, found #{ntok.raw.inspect if ntok}" if not ntok or ntok.type != :punct or ntok.raw != '='
|
|
1159
|
+
@lexer.skip_space
|
|
1160
|
+
ntok = @lexer.readtok
|
|
1161
|
+
raise "syntax error: symbol type expected, found #{ntok.raw.inspect if ntok}" if not ntok or ntok.type != :string or not SYMBOL_TYPE.index(ntok.raw)
|
|
1162
|
+
s.type = ntok.raw
|
|
1163
|
+
when 'size'
|
|
1164
|
+
@lexer.skip_space
|
|
1165
|
+
ntok = @lexer.readtok
|
|
1166
|
+
raise "syntax error: = expected, found #{ntok.raw.inspect if ntok}" if not ntok or ntok.type != :punct or ntok.raw != '='
|
|
1167
|
+
@lexer.skip_space
|
|
1168
|
+
ntok = @lexer.readtok
|
|
1169
|
+
raise "syntax error: symbol size expected, found #{ntok.raw.inspect if ntok}" if not ntok or ntok.type != :string or not ntok.raw =~ /^\d+$/
|
|
1170
|
+
s.size = ntok.raw.to_i
|
|
1171
|
+
else
|
|
1172
|
+
if not s.value
|
|
1173
|
+
s.value = ntok.raw
|
|
1174
|
+
elsif not s.size
|
|
1175
|
+
s.size = Expression[ntok.raw, :-, s.value]
|
|
1176
|
+
else
|
|
1177
|
+
raise instr, "syntax error: eol expected, found #{ntok.raw.inspect}"
|
|
1178
|
+
end
|
|
1179
|
+
end
|
|
1180
|
+
end
|
|
1181
|
+
s.value ||= s.name if not s.shndx and not s.thunk
|
|
1182
|
+
s.shndx ||= 1 if s.value
|
|
1183
|
+
@symbols << s
|
|
1184
|
+
|
|
1185
|
+
when '.needed'
|
|
1186
|
+
# a required library
|
|
1187
|
+
(@tag['NEEDED'] ||= []) << readstr[]
|
|
1188
|
+
check_eol[]
|
|
1189
|
+
|
|
1190
|
+
when '.soname'
|
|
1191
|
+
# exported library name
|
|
1192
|
+
@tag['SONAME'] = readstr[]
|
|
1193
|
+
check_eol[]
|
|
1194
|
+
@segments.delete_if { |s_| s_.type == 'INTERP' }
|
|
1195
|
+
@header.type = 'DYN'
|
|
1196
|
+
|
|
1197
|
+
when '.interp', '.nointerp'
|
|
1198
|
+
# required ELF interpreter
|
|
1199
|
+
interp = ((instr.raw == '.nointerp') ? 'nil' : readstr[])
|
|
1200
|
+
|
|
1201
|
+
@segments.delete_if { |s_| s_.type == 'INTERP' }
|
|
1202
|
+
case interp.downcase
|
|
1203
|
+
when 'nil', 'no', 'none'
|
|
1204
|
+
@header.shnum = 0
|
|
1205
|
+
else
|
|
1206
|
+
seg = Segment.new
|
|
1207
|
+
seg.type = 'INTERP'
|
|
1208
|
+
seg.encoded = EncodedData.new << interp << 0
|
|
1209
|
+
seg.flags = ['R']
|
|
1210
|
+
seg.memsz = seg.filesz = seg.encoded.length
|
|
1211
|
+
@segments.unshift seg
|
|
1212
|
+
end
|
|
1213
|
+
|
|
1214
|
+
check_eol[]
|
|
1215
|
+
|
|
1216
|
+
when '.pt_gnu_stack'
|
|
1217
|
+
# PT_GNU_STACK marking
|
|
1218
|
+
mode = readstr[]
|
|
1219
|
+
|
|
1220
|
+
@segments.delete_if { |s_| s_.type == 'GNU_STACK' }
|
|
1221
|
+
s = Segment.new
|
|
1222
|
+
s.type = 'GNU_STACK'
|
|
1223
|
+
case mode
|
|
1224
|
+
when /^rw$/i; s.flags = %w[R W]
|
|
1225
|
+
when /^rwx$/i; s.flags = %w[R W X]
|
|
1226
|
+
else raise instr, "syntax error: expected rw|rwx, found #{mode.inspect}"
|
|
1227
|
+
end
|
|
1228
|
+
@segments << s
|
|
1229
|
+
|
|
1230
|
+
when '.init', '.fini'
|
|
1231
|
+
# dynamic tag initialization
|
|
1232
|
+
@lexer.skip_space
|
|
1233
|
+
if tok = @lexer.nexttok and tok.type == :string
|
|
1234
|
+
raise instr, 'syntax error' if not init = Expression.parse(@lexer)
|
|
1235
|
+
else
|
|
1236
|
+
init = new_label(instr.raw[1..-1])
|
|
1237
|
+
@cursource << Label.new(init, instr.backtrace.dup)
|
|
1238
|
+
end
|
|
1239
|
+
@tag[instr.raw[1..-1].upcase] = init
|
|
1240
|
+
check_eol[]
|
|
1241
|
+
|
|
1242
|
+
when '.init_array', '.fini_array', '.preinit_array'
|
|
1243
|
+
t = @tag[instr.raw[1..-1].upcase] ||= []
|
|
1244
|
+
loop do
|
|
1245
|
+
raise instr, 'syntax error' if not e = Expression.parse(@lexer)
|
|
1246
|
+
t << e
|
|
1247
|
+
@lexer.skip_space
|
|
1248
|
+
ntok = @lexer.nexttok
|
|
1249
|
+
break if not ntok or ntok.type == :eol
|
|
1250
|
+
raise instr, "syntax error, ',' expected, found #{ntok.raw.inspect}" if nttok != :punct or ntok.raw != ','
|
|
1251
|
+
@lexer.readtok
|
|
1252
|
+
end
|
|
1253
|
+
|
|
1254
|
+
else super(instr)
|
|
1255
|
+
end
|
|
1256
|
+
end
|
|
1257
|
+
|
|
1258
|
+
# assembles the hash self.source to a section array
|
|
1259
|
+
def assemble(*a)
|
|
1260
|
+
parse(*a) if not a.empty?
|
|
1261
|
+
@source.each { |k, v|
|
|
1262
|
+
raise "no section named #{k} ?" if not s = @sections.find { |s_| s_.name == k }
|
|
1263
|
+
s.encoded << assemble_sequence(v, @cpu)
|
|
1264
|
+
v.clear
|
|
1265
|
+
}
|
|
1266
|
+
end
|
|
1267
|
+
|
|
1268
|
+
def encode_file(path, *a)
|
|
1269
|
+
ret = super(path, *a)
|
|
1270
|
+
File.chmod(0755, path) if @header.entry and @header.entry != 0
|
|
1271
|
+
ret
|
|
1272
|
+
end
|
|
1273
|
+
|
|
1274
|
+
# defines __ELF__
|
|
1275
|
+
def tune_prepro(l)
|
|
1276
|
+
l.define_weak('__ELF__', 1)
|
|
1277
|
+
end
|
|
1278
|
+
|
|
1279
|
+
# set the data model
|
|
1280
|
+
def tune_cparser(cp)
|
|
1281
|
+
super(cp)
|
|
1282
|
+
cp.lp64 if @cpu.size == 64
|
|
1283
|
+
end
|
|
1284
|
+
|
|
1285
|
+
# handles C attributes: export, export_as(foo), import, import_from(libc.so.6), init, fini, entrypoint
|
|
1286
|
+
def read_c_attrs(cp)
|
|
1287
|
+
cp.toplevel.symbol.each_value { |v|
|
|
1288
|
+
next if not v.kind_of? C::Variable
|
|
1289
|
+
if v.has_attribute 'export' or ea = v.has_attribute_var('export_as')
|
|
1290
|
+
s = Symbol.new
|
|
1291
|
+
s.name = ea || v.name
|
|
1292
|
+
s.type = v.type.kind_of?(C::Function) ? 'FUNC' : 'NOTYPE'
|
|
1293
|
+
s.bind = 'GLOBAL'
|
|
1294
|
+
s.shndx = 1
|
|
1295
|
+
s.value = v.name
|
|
1296
|
+
@symbols << s
|
|
1297
|
+
end
|
|
1298
|
+
if v.has_attribute 'import' or ln = v.has_attribute_var('import_from')
|
|
1299
|
+
(@tag['NEEDED'] ||= []) << ln if ln and not @tag['NEEDED'].to_a.include? ln
|
|
1300
|
+
s = Symbol.new
|
|
1301
|
+
s.name = v.name
|
|
1302
|
+
s.type = v.type.kind_of?(C::Function) ? 'FUNC' : 'NOTYPE'
|
|
1303
|
+
s.bind = 'GLOBAL'
|
|
1304
|
+
s.shndx = 'UNDEF'
|
|
1305
|
+
@symbols << s
|
|
1306
|
+
end
|
|
1307
|
+
if v.has_attribute('init') or v.has_attribute('constructor')
|
|
1308
|
+
(@tag['INIT_ARRAY'] ||= []) << v.name
|
|
1309
|
+
end
|
|
1310
|
+
if v.has_attribute('fini') or v.has_attribute('destructor')
|
|
1311
|
+
(@tag['FINI_ARRAY'] ||= []) << v.name
|
|
1312
|
+
end
|
|
1313
|
+
if v.has_attribute 'entrypoint'
|
|
1314
|
+
@header.entry = v.name
|
|
1315
|
+
end
|
|
1316
|
+
}
|
|
1317
|
+
end
|
|
1318
|
+
|
|
1319
|
+
def c_set_default_entrypoint
|
|
1320
|
+
return if @header.entry
|
|
1321
|
+
if @sections.find { |s| s.encoded and s.encoded.export['_start'] }
|
|
1322
|
+
@header.entry = '_start'
|
|
1323
|
+
elsif @sections.find { |s| s.encoded and s.encoded.export['main'] }
|
|
1324
|
+
# entrypoint stack: [sp] = argc, [sp+1] = argv0, [sp+2] = argv1, [sp+argc+1] = 0, [sp+argc+2] = envp0, etc
|
|
1325
|
+
case @cpu.shortname
|
|
1326
|
+
when 'ia32'; assemble <<EOS
|
|
1327
|
+
_start:
|
|
1328
|
+
mov eax, [esp]
|
|
1329
|
+
lea ecx, [esp+4+4*eax+4]
|
|
1330
|
+
push ecx
|
|
1331
|
+
lea ecx, [esp+4+4]
|
|
1332
|
+
push ecx
|
|
1333
|
+
push eax
|
|
1334
|
+
call main
|
|
1335
|
+
push eax
|
|
1336
|
+
call _exit
|
|
1337
|
+
EOS
|
|
1338
|
+
when 'x64'; assemble <<EOS
|
|
1339
|
+
_start:
|
|
1340
|
+
mov rdi, [rsp]
|
|
1341
|
+
lea rsi, [rsp+8]
|
|
1342
|
+
lea rdx, [rsi+8*rdi+8]
|
|
1343
|
+
call main
|
|
1344
|
+
mov rdi, rax
|
|
1345
|
+
call _exit
|
|
1346
|
+
EOS
|
|
1347
|
+
else compile_c <<EOS
|
|
1348
|
+
void _exit(int);
|
|
1349
|
+
int main(int, char**, char**);
|
|
1350
|
+
void _start(void) {
|
|
1351
|
+
_exit(main(0, 0, 0));
|
|
1352
|
+
}
|
|
1353
|
+
EOS
|
|
1354
|
+
end
|
|
1355
|
+
@header.entry = '_start'
|
|
1356
|
+
end
|
|
1357
|
+
end
|
|
1358
|
+
end
|
|
1359
|
+
end
|
|
1360
|
+
|
|
1361
|
+
__END__
|
|
1362
|
+
elf.assemble Ia32.new, <<EOS
|
|
1363
|
+
.text ; @sections << Section.new('.text', ['r' 'x'])
|
|
1364
|
+
.global "foo" foo foo_end ; @symbols ||= [0] << Symbol.new(global, '.foo', addr=foo, size=foo_end - foo)
|
|
1365
|
+
.global "bla" plt=bla_plt
|
|
1366
|
+
.needed 'libc.so.6' ; @tag['NEEDED'] ||= [] << 'libc.so.6'
|
|
1367
|
+
.soname 'lolol' ; @tag['SONAME'] = 'lolol'
|
|
1368
|
+
.interp nil ; @segments.delete_if { |s| s.type == 'INTERP' } ; @sections.delete_if { |s| s.name == '.interp' && vaddr = seg.vaddr etc }
|
|
1369
|
+
|
|
1370
|
+
foo:
|
|
1371
|
+
inc eax
|
|
1372
|
+
call bla_plt
|
|
1373
|
+
ret
|
|
1374
|
+
foo_end:
|
|
1375
|
+
EOS
|