elf-mithril 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
File without changes
@@ -0,0 +1,335 @@
1
+
2
+ require 'bindata'
3
+ require_relative 'elf_enums'
4
+ module ElfStructs
5
+ class ElfIdentification < BinData::Record
6
+ endian :little
7
+ string :id_magic, :length=>4 , :initial_value => "\x7FELF" #TODO: validate?
8
+ uint8 :id_class
9
+ uint8 :id_data
10
+ uint8 :id_version
11
+ skip :length => 9
12
+ end
13
+
14
+ def alias_recordtype(from,to)
15
+ self.class.class_eval do
16
+ define_method from do |*args|
17
+ send(to,*args)
18
+ end
19
+ end
20
+ end
21
+ def hdr
22
+ elf_identification :ident
23
+ #end of identification
24
+ half :type #File type.
25
+ half :machine #Machine architecture.
26
+ word :version #ELF format version.
27
+ addr :entry #Entry point.
28
+ off :phoff #Program header file offset.
29
+ off :shoff #Section header file offset.
30
+ word :flags #Architecture-specific flags.
31
+ half :ehsize #Size of ELF header in bytes.
32
+ half :phentsize #Size of program header entry.
33
+ half :phnum #Number of program header entries.
34
+ half :shentsize #Size of section header entry.
35
+ half :shnum #Number of section header entries.
36
+ half :shstrndx #Section name strings section.
37
+ end
38
+
39
+ def note
40
+ uint32 :namesz, :value => lambda{name.num_bytes} #Length of name.
41
+ uint32 :descsz, :value => lambda{desc.num_bytes} # Length of descriptor.
42
+ uint32 :type # Type of this note.
43
+ string :name, :read_length => lambda{ (namesz.to_i * 4 + 3)/4 } # Round up
44
+ string :desc, :read_length => lambda{ (descsz.to_i * 4 + 3)/4 }
45
+ attr_accessor :section_name
46
+ end
47
+
48
+ def shdr
49
+ word :name #Section name (index into section header string table
50
+ word :type #Section type.
51
+ xword :flags #Section flags.
52
+ addr :vaddr #Address in memory image.
53
+ off :off #Offset in file.
54
+ xword :siz#Size in bytes. Patch: Had to change to siz
55
+ word :link #Index of a related section.
56
+ word :info #Depends on section type.
57
+ xword :addralign #Alignment in bytes.
58
+ xword :entsize #Size of each entry in section.
59
+
60
+
61
+ attr_accessor :index # So we retain the index after parsing
62
+ end
63
+ def phdr32 # FAIL different layout with 32 and 64
64
+ word :type #Entry type.
65
+ off :off #File offset of contents.
66
+ addr :vaddr #Virtual address in memory image.
67
+ addr :paddr #Physical address (not used).
68
+ xword :filesz #Size of contents in file.
69
+ xword :memsz #Size of contents in memory.
70
+ word :flags #Access permission flags.
71
+ xword :align #Alignment in memory and file.
72
+ end
73
+ def phdr64
74
+ word :type
75
+ word :flags
76
+ off :off
77
+ addr :vaddr
78
+ addr :paddr
79
+ xword :filesz
80
+ xword :memsz
81
+ xword :align
82
+ end
83
+ # Dynamic structure. The ".dynamic" section contains an array of them.
84
+ def dyn
85
+ sxword :tag #Entry type.
86
+ addr :val #Address value or raw value
87
+ end
88
+ # * Relocation entries
89
+ # Relocations that don't need an addend field. */
90
+ def rel_common
91
+ case @bits
92
+ when 32
93
+ define_method :sym do
94
+ info.to_i >> 8
95
+ end
96
+ define_method :type do
97
+ info.to_i & 0xff
98
+ end
99
+ define_method :sym= do |val|
100
+ self.info = type | (val << 8)
101
+ end
102
+ define_method :type= do |val|
103
+ self.info = (val &0xff) | (sym << 8)
104
+ end
105
+ when 64
106
+ define_method :sym do
107
+ info.to_i >> 32
108
+ end
109
+ define_method :type do
110
+ info.to_i & 0xffffffff
111
+ end
112
+ define_method :sym= do |val|
113
+ self.info = type | (val << 32)
114
+ end
115
+ define_method :type= do |val|
116
+ self.info = (val &0xffffffff) | (sym << 32)
117
+ end
118
+ end
119
+ end
120
+ def rel
121
+ addr :off #Location to be relocated.
122
+ xword :info #Relocation type and symbol index.
123
+ define_method :addend do
124
+ nil
125
+ end
126
+ rel_common
127
+ end
128
+
129
+ # Relocations that need an addend field. */
130
+ def rela
131
+ addr :off #Location to be relocated.
132
+ xword :info #Relocation type and symbol index.
133
+ sxword :addend #Addend.
134
+ rel_common
135
+ end
136
+
137
+ #Elf Symbol
138
+ def sym_common
139
+ define_method :type do
140
+ info & 0xf
141
+ end
142
+ define_method :binding do
143
+ info >> 4
144
+ end
145
+ define_method :type= do |val|
146
+ raise RuntimeError.new "Invalid param" unless val & 0xf == val
147
+ self.info = (info&0xf0) | val
148
+ end
149
+ define_method :binding= do |val|
150
+ raise RuntimeError.new "Invalid param" unless val & 0xf == val
151
+ self.info = (info&0xf) | (val << 4)
152
+ end
153
+ end
154
+ def sym32
155
+ word :name #String table index of name.
156
+ addr :val #Symbol value. PATCH: change to val so as to avoid name conflict
157
+ word :siz #Size of associated object. PATCH: Change to val
158
+ char :info #Type and binding information.
159
+ char :other #Reserved (not used).
160
+ half :shndx #Section index of symbol.
161
+ sym_common
162
+ end
163
+ def sym64
164
+ word :name
165
+ uint8 :info
166
+ uint8 :other
167
+ half :shndx
168
+ addr :val
169
+ xword :siz
170
+ sym_common
171
+ end
172
+ def versym
173
+ half :veridx
174
+ end
175
+ def verdaux
176
+ word :name
177
+ word :nextoff
178
+ end
179
+ def verdef
180
+ half :version
181
+ half :flags
182
+ half :ndx
183
+ half :cnt
184
+ word :hsh
185
+ word :aux
186
+ word :nextoff
187
+ end
188
+ def verneed
189
+ half :version
190
+ half :cnt
191
+ word :file
192
+ word :aux
193
+ word :nextoff
194
+ end
195
+ def vernaux
196
+ word :hsh
197
+ half :flags
198
+ half :other
199
+ word :name
200
+ word :nextoff
201
+ end
202
+ def stringtable
203
+ array :strings, :type => :stringz, :initial_length=> 0
204
+ end
205
+ #ELFP redesign - in progress
206
+ =begin
207
+ def elfp_chunk_desc # Each chunk of ELFbac metadata is also a section
208
+ uint32 :type
209
+ uint32 :offset
210
+ uint32 :size
211
+ end
212
+ def elfp_header
213
+ uint32 :sectioncount, value: lambda { sectionheaders.length }
214
+ array :sectionheaders, type: @@factory.elfp_chunk_desc, read_length: initia
215
+ end
216
+ =end
217
+ #ELFP old
218
+ def elfp_header
219
+ uint32 :chunkcount, value: lambda {states.size + tags.size+ calls.size + data.size}
220
+ array :states, type: @factory.elfp_state
221
+ array :tags, type: @factory.elfp_tag
222
+ array :calls, type: @factory.elfp_call
223
+ array :data, type: @factory.elfp_data
224
+ end
225
+ def elfp_state
226
+ uint32 :chunkid , value: 1
227
+ uint32 :id
228
+ uint32 :stackid, default_value: 0
229
+ end
230
+ def elfp_call
231
+ uint32 :chunktype, value: 2
232
+ uint32 :from
233
+ uint32 :to
234
+ uint64 :off
235
+ uint16 :parambytes
236
+ uint16 :returnbytes
237
+ end
238
+ def elfp_data
239
+ uint32 :chunktype, value: 3
240
+ uint32 :from
241
+ uint32 :to
242
+ uint64 :tag
243
+ uint32 :type
244
+ end
245
+ def elfp_tag
246
+ uint32 :chunktype, value:4
247
+ uint64 :tag
248
+ uint64 :addr
249
+ uint64 :siz
250
+ end
251
+ ELF_OBJECTS = [:sym, :rela, :rel, :dyn, :phdr, :shdr, :hdr, :note, :vernaux, :verneed, :verdef, :verdaux, :versym, :elfp_state, :elfp_call, :elfp_data, :elfp_tag, :elfp_header]
252
+ Split = {
253
+ phdr: {
254
+ 32 => :phdr32,
255
+ 64 => :phdr64
256
+ },
257
+ sym: {
258
+ 32 => :sym32,
259
+ 64 => :sym64
260
+ }
261
+ }
262
+ def bitness(bits)
263
+ @bits = bits
264
+ alias_recordtype :char, :uint8
265
+ case bits
266
+ when 32
267
+ alias_recordtype :word, :uint32
268
+ alias_recordtype :sword, :int32
269
+ alias_recordtype :half, :uint16
270
+ alias_recordtype :off, :uint32
271
+ alias_recordtype :addr, :uint32
272
+ alias_recordtype :xword, :uint32
273
+ alias_recordtype :sxword, :uint32
274
+ when 64
275
+ alias_recordtype :addr, :uint64
276
+ alias_recordtype :off, :uint64
277
+ alias_recordtype :half, :uint16
278
+ alias_recordtype :word, :uint32
279
+ alias_recordtype :sword, :int32
280
+ alias_recordtype :xword, :uint64
281
+ alias_recordtype :sxword, :int64
282
+ else
283
+ raise RuntimeError.new "We only know about 32-bit or 64-bit ELF formats, not about #{bits}"
284
+ end
285
+ end
286
+ end
287
+ class ElfStructFactory
288
+ attr_reader *ElfStructs::ELF_OBJECTS
289
+ def self.instance(endian, bits)
290
+ @@instances[endian][bits]
291
+ end
292
+ def rel_info_sym(info)
293
+ if @width == 32
294
+ info.to_i >> 8
295
+ else
296
+ info.to_i >> 32
297
+ end
298
+ end
299
+ def rel_info_type(info)
300
+ if @width == 32
301
+ info.to_i & 0xff
302
+ else
303
+ info.to_i & 0xffffffff
304
+ end
305
+ end
306
+ def rel_build_info(sym,type)
307
+ if @width == 32
308
+ (sym.to_i << 8) | (type.to_i &0xff)
309
+ else
310
+ (sym.to_i << 32) | (type.to_i &0xffffffff)
311
+ end
312
+ end
313
+
314
+ def initialize(endian,width)
315
+ @endian = endian
316
+ @width = width
317
+ factory = self
318
+ ElfStructs::ELF_OBJECTS.each do |object|
319
+ klass = Class.new BinData::Record do
320
+ extend ElfStructs
321
+ endian endian
322
+ bitness width
323
+ @factory = factory
324
+ if(ElfStructs::Split.include? object)
325
+ self.send(ElfStructs::Split[object][width])
326
+ else
327
+ self.send(object)
328
+ end
329
+ end
330
+ instance_variable_set("@#{object.to_s}",klass)
331
+ end
332
+ end
333
+ @@instances = {:little => {32 => ElfStructFactory.new(:little,32), 64=>ElfStructFactory.new(:little,64) },
334
+ :big => {32 => ElfStructFactory.new(:big,32) , 64 => ElfStructFactory.new(:big,64)} }
335
+ end
@@ -0,0 +1,22 @@
1
+ module Elf::Policy
2
+ def self.inject_symbols(file)
3
+ case file.filetype
4
+ when ElfFlags::Type::ET_EXEC
5
+ filename = ""
6
+ when ElfFlags::Type::ET_DYN
7
+ filename = file.dynamic.soname
8
+ else
9
+ raise RuntimeError.new "section_symbols works only for ET_DYN and ET_EXEC files"
10
+ end
11
+ (file.progbits + file.nobits).each{|section|
12
+ name = Elf::Policy::section_symbol_name(filename,section.name)
13
+ next if file.symbols.include? name
14
+ sym = Elf::Symbol.new(name,section,ElfFlags::SymbolType::STT_OBJECT,0,ElfFlags::SymbolBinding::STB_GLOBAL,section.size) # STT_SECTION is ignored for lookup!
15
+ # print "Injecting #{name}"
16
+ sym.gnu_version = :global
17
+ sym.hidden = false
18
+ sym.is_dynamic = true
19
+ file.symbols << sym
20
+ }
21
+ end
22
+ end
@@ -0,0 +1,706 @@
1
+ require_relative 'elf'
2
+
3
+ $UNSAFE_PARSER = ! ENV['HOWDY_NEIGHBOURS_BX'].nil? # shoutout to one
4
+ # of the best ELF hackers on the block.
5
+
6
+ def expect_value(desc,is,should)
7
+ unless $UNSAFE_PARSER
8
+ raise RuntimeError.new "Invalid #{desc}, expected #{should} instead of #{is}" if is != should
9
+ end
10
+ end
11
+
12
+ module Elf
13
+ module Parser
14
+ class StringTable
15
+ def initialize(data)
16
+ expect_value "First byte of string table", data.bytes.first, 0
17
+ @data = StringIO.new(data)
18
+ end
19
+ def [](offset)
20
+ @data.seek offset
21
+ obj = BinData::Stringz.new
22
+ obj.read(@data)
23
+ obj.snapshot
24
+ end
25
+ end
26
+ class NilStringTable
27
+ def [](offset)
28
+ return ""
29
+ end
30
+ end
31
+ class Parser
32
+ attr_reader :file
33
+ def initialize(string)
34
+ @data = StringIO.new(string)
35
+ @file = ElfFile.new
36
+ ident = ElfStructs::ElfIdentification.read(@data)
37
+ print ident.snapshot.inspect
38
+ raise RuntimeError.new "Invalid ELF version #{ident.id_version}" if ident.id_version != ElfFlags::Version::EV_CURRENT
39
+ case ident.id_class
40
+ when ElfFlags::IdentClass::ELFCLASS64
41
+ @file.bits = 64
42
+ when ElfFlags::IdentClass::ELFCLASS32
43
+ @file.bits = 32
44
+ else
45
+ RuntimeError.new "Invalid ELF class #{ident.id_class}"
46
+ end
47
+ case ident.id_data
48
+ when ElfFlags::IdentData::ELFDATA2LSB
49
+ @file.endian = :little
50
+ when ElfFlags::IdentData::ELFDATA2MSB
51
+ @file.endian = :big
52
+ else
53
+ RuntimeError.new "Invalid ELF endianness #{ident.id_data}"
54
+ end
55
+ @versions = {}
56
+ @reladyn_indices = []
57
+ @factory = ElfStructFactory.instance(@file.endian,@file.bits)
58
+ parse_with_factory()
59
+ end
60
+
61
+ private
62
+ def unique_section(sects, type)
63
+ if sects.include? type
64
+ expect_value "Number of #{type} sections", sects[type].size, 1
65
+ return sects[type].first
66
+ else
67
+ return nil
68
+ end
69
+ end
70
+ def safe_strtab(index)
71
+ if(index ==0)
72
+ NilStringTable.new()
73
+ else
74
+ hdr = @shdrs[index]
75
+ expect_value "STRTAB type", hdr.type, ElfFlags::SectionType::SHT_STRTAB
76
+ @data.seek hdr.off
77
+ @unparsed_sections.delete index
78
+ StringTable.new(@data.read(hdr.siz))
79
+ end
80
+ end
81
+ def parse_symtable(sect,strtab)
82
+ return [] if sect.nil?
83
+ expect_value "Size of symbol table entry", @factory.sym.new.num_bytes, sect.entsize
84
+ @data.seek sect.off
85
+ @unparsed_sections.delete sect.index
86
+ BinData::Array.new( :type=> @factory.sym, :initial_length => sect.siz / sect.entsize).read(@data).map do |sym|
87
+ #TODO: find appropriate section
88
+ unless @bits_by_index.include? sym.shndx.to_i
89
+ section = nil
90
+ value = sym.val.to_i
91
+ else
92
+ section = @bits_by_index[sym.shndx.to_i]
93
+ if([ET::ET_EXEC, ET::ET_DYN].include? @file.filetype)
94
+ value = sym.val.to_i - section.addr #TODO: Only if absolute.
95
+ else
96
+ value = sym.val.to_i
97
+ end
98
+ section = ".interp" if section.name == ".interp" #HACK: this should not be the case
99
+ # expect_value "Section index #{sym.shndx.to_i} in symbol should be in progbits", false, section.nil?
100
+ end
101
+ x= Symbol.new(strtab[sym.name],section, sym.type.to_i, value, sym.binding.to_i, sym.siz.to_i)
102
+ if(x.name.empty? and x.type == STT::STT_SECTION)
103
+ x.name = @shstrtab[@shdrs[sym.shndx.to_i].name]
104
+ end
105
+ x.visibility = sym.other.to_i & 0x3
106
+ if [SHN::SHN_ABS, SHN::SHN_COMMON, SHN::SHN_UNDEF, SHN::SHN_XINDEX].include? sym.shndx.to_i
107
+ x.semantics = sym.shndx.to_i
108
+ else
109
+ x.semantics = nil
110
+ end
111
+ x
112
+ end
113
+ end
114
+ def parse_progbits(shdr)
115
+ @data.seek shdr.off
116
+ @unparsed_sections.delete shdr.index
117
+ expect_value "PROGBITS link",shdr.link,0
118
+ if shdr.type.to_i == SHT::SHT_NOBITS
119
+ NoBits.new(@shstrtab[shdr.name],shdr.snapshot)
120
+ else
121
+ ProgBits.new(@shstrtab[shdr.name], shdr.snapshot, @data.read(shdr.siz))
122
+ end
123
+ end
124
+ def parse_verdef(shdr)
125
+ @unparsed_sections.delete shdr.index
126
+ strtab = safe_strtab(shdr.link)
127
+ verdefoff = shdr.off
128
+ file = @file.dynamic.soname
129
+ versions = {}
130
+ parents = {}
131
+ shdr.info.to_i.times {
132
+ @data.seek verdefoff
133
+ verdef = @factory.verdef.read(@data)
134
+ expect_value "VERDEF version", verdef.version, 1
135
+ verdauxoff = verdefoff + verdef.aux
136
+ aux = []
137
+ verdef.cnt.to_i.times {
138
+ @data.seek verdauxoff
139
+ verdaux = @factory.verdaux.read(@data)
140
+ aux << strtab[verdaux.name]
141
+ verdauxoff += verdaux.nextoff.to_i
142
+ # expect_value "Nonzero verdaux.nextoff #{verdaux.nextoff}", true,verdaux.nextoff != 0 unless
143
+ }
144
+ name = aux.first
145
+ par = aux[1..-1] || []
146
+ expect_value "Name present", false, name.nil?
147
+ parents[name] = par
148
+ # expect_value "Version #{verdef.idx} unique", false, @versions.include? verdef.idx.to_i
149
+ @versions[verdef.ndx.to_i] = GnuVersion.new(file,name,verdef.flags.to_i,false)
150
+ versions[name] = @versions[verdef.ndx.to_i]
151
+ # expect_value "Nonzero verdef.nextoff #{verdef.nextoff}", true,verdef.nextoff != 0
152
+ verdefoff += verdef.nextoff.to_i
153
+ }
154
+ parents.each{|version,p|
155
+ p.each{|x|
156
+ # expect_value "Valid parent version #{x}", true, versions.include? x
157
+ versions[version].parents << versions[x]
158
+ }
159
+ }
160
+ if @versions.include? 1
161
+ @file.dynamic.gnu_version_basename = @versions[1].version
162
+ end
163
+ end
164
+ def parse_verneed(shdr)
165
+ @unparsed_sections.delete shdr.index
166
+ strtab = safe_strtab(shdr.link)
167
+ @data.seek shdr.off
168
+ data = StringIO.new(@data.read(shdr.siz.to_i))
169
+ # This is the weird, screwed up 'array', that actually is a
170
+ # linked list
171
+ verneedoff = 0
172
+ shdr.info.to_i.times{ #SHDR.info has number of entries
173
+ data.seek verneedoff
174
+ verneed = @factory.verneed.read(data)
175
+ expect_value "VERNEED version", verneed.version, 1
176
+ file = strtab[verneed.file]
177
+ vernauxoff = verneedoff + verneed.aux
178
+
179
+ verneed.cnt.times {
180
+ data.seek vernauxoff
181
+
182
+ vernaux = @factory.vernaux.read(data)
183
+ versionname = strtab[vernaux.name]
184
+ flags = vernaux.flags.to_i
185
+ version = vernaux.other.to_i
186
+ # expect_value "Nonzero vernaux.nextoff #{vernaux.nextoff}", true,vernaux.nextoff != 0
187
+
188
+ @versions[version] = GnuVersion.new( file, versionname, flags,true)
189
+ vernauxoff += vernaux.nextoff
190
+ }
191
+ # expect_value "Nonzero verneedoff ",true, verneed.nextoff != 0
192
+ verneedoff += verneed.nextoff
193
+ }
194
+ end
195
+ VERSYM_HIDDEN = 0x8000
196
+ VERSYM_IDX_MASK = 0xffff & ~VERSYM_HIDDEN
197
+ def parse_versym(shdr,dynsym)
198
+ @data.seek shdr.off
199
+ data = StringIO.new(@data.read(shdr.siz.to_i))
200
+ BinData::Array.new(type: @factory.versym, initial_length: shdr.siz / @factory.versym.new.num_bytes).read(data).to_enum.with_index {|versym,index|
201
+ veridx = versym.veridx & VERSYM_IDX_MASK
202
+ dynsym[index].gnu_version =case veridx
203
+ when 0
204
+ :local
205
+ when 1
206
+ :global
207
+ else
208
+ unless @versions.include? veridx
209
+ raise RuntimeError.new "Invalid veridx #{versym.veridx} in dynamic symbol #{index}"
210
+ end
211
+ @versions[veridx]
212
+ end
213
+ dynsym[index].hidden = (versym.veridx & VERSYM_HIDDEN != 0)
214
+ }
215
+ end
216
+ DYNAMIC_FLAGS = {
217
+ DT::DT_TEXTREL=>:@textrel,
218
+ DT::DT_BIND_NOW => :@bind_now,
219
+ DT::DT_SYMBOLIC => :@symbolic
220
+ }
221
+ def parse_rel_common(relocations,sect_idx,symtab_idx, uses_addresses,is_jmprel)
222
+ case @shdrs[symtab_idx].type.to_i
223
+ when SHT::SHT_DYNSYM
224
+ symtab= @dynsym
225
+ when SHT::SHT_SYMTAB
226
+ symtab= @symtab
227
+ else
228
+ raise ArgumentError.new "Invalid link field #{symtab_idx} in relocation section"
229
+ end
230
+
231
+ if sect_idx == 0 and uses_addresses
232
+ applies_to = nil
233
+ else
234
+ applies_to = @bits_by_index[sect_idx]
235
+ raise ArgumentError.new "Section index #{sect_idx} not referring to PROGBITS for relocation table" if applies_to.nil?
236
+ end
237
+
238
+ relocations.map {|rel_entry|
239
+ Relocation.new.tap { |rel|
240
+ if uses_addresses
241
+ rel.section = @relocatable_sections.find(rel_entry.off.to_i).andand(&:value)
242
+ print "Warning: Invalid relocation address 0x#{rel_entry.off.snapshot.to_s(16)}\n" unless rel.section
243
+ rel.offset = rel_entry.off.to_i - rel.section.addr
244
+ else
245
+ rel.section = applies_to
246
+ rel.offset = rel_entry.off.to_i
247
+ end
248
+ rel.type = rel_entry.type
249
+ if rel_entry.sym == 0
250
+ rel.symbol = nil
251
+ else
252
+ rel.symbol = @canonical_symbol[symtab[ rel_entry.sym]] or raise(RuntimeError.new("Symbol could not be canonicalized"))
253
+ end
254
+ rel.addend = rel_entry.addend.to_i
255
+ rel.is_lazy = is_jmprel
256
+ }
257
+ }
258
+ end
259
+ def is_jmprel(shdr)
260
+ @jmprel_addr == shdr.vaddr
261
+ end
262
+ def parse_rela(shdr,has_addrs)
263
+ @unparsed_sections.delete shdr.index
264
+ @data.seek shdr.off
265
+ expect_value "RELA entsize", shdr.entsize, @factory.rela.new.num_bytes
266
+ rela = BinData::Array.new(:type => @factory.rela, :initial_length => shdr.siz/shdr.entsize).read(@data)
267
+ parse_rel_common(rela,shdr.info, shdr.link,has_addrs,is_jmprel(shdr))
268
+ end
269
+ def parse_rel(shdr,has_addrs)
270
+ @unparsed_sections.delete shdr.index
271
+ @data.seek shdr.off
272
+ expect_value "REL entsize", shdr.entsize, @factory.rel.new.num_bytes
273
+ rela = BinData::Array.new(:type => @factory.rel, :initial_length => shdr.siz/shdr.entsize).read(@data)
274
+ parse_rel_common(rela,shdr.info, shdr.link,has_addrs,is_jmprel(shdr))
275
+ end
276
+ def parse_dynamic(shdr)
277
+ retval = Dynamic.new
278
+
279
+ @data.seek shdr.off
280
+ #TODO: find unused dynamic entries
281
+ expect_value "Size of dynamic entry", @factory.dyn.new.num_bytes, shdr.entsize
282
+ dynamic = BinData::Array.new(:type=> @factory.dyn, :initial_length => shdr.siz/ shdr.entsize).read(@data)
283
+ @unparsed_sections.delete shdr.index
284
+ by_type = dynamic.group_by {|x| x.tag.to_i}
285
+ expect_unique = lambda do |sym,optional| # Validates that either one
286
+ # or zero entries of this type exist, returning the one entry
287
+ # if it exists
288
+ if(by_type.include? sym)
289
+ expect_value "Dynamic entry #{sym} count", by_type[sym].size,1
290
+ by_type[sym].first
291
+ else
292
+ if(optional)
293
+ nil
294
+ else
295
+ raise ArgumentError.new "Missing mandatory dynamic entry #{sym}"
296
+ end
297
+ end
298
+ end
299
+
300
+ expect_value "DT_NULL", dynamic.last, @factory.dyn.new()
301
+
302
+ by_type.delete DT::DT_NULL
303
+
304
+ expect_unique.call DT::DT_STRTAB,false
305
+ expect_unique.call DT::DT_STRSZ,false #TODO: check that this a strtab and get a strtab
306
+ strtab_hdr= @sect_types[SHT::SHT_STRTAB].group_by(&:vaddr)[by_type[DT::DT_STRTAB].first.val].andand(&:first)
307
+
308
+ expect_value "Some STRTAB section should be mapped at DT_STRTAB", strtab_hdr.nil?,false
309
+ by_type.delete DT::DT_STRTAB
310
+ expect_value "STRSZ", by_type[DT::DT_STRSZ].first.val, strtab_hdr.siz
311
+ by_type.delete DT::DT_STRSZ
312
+ @dynstr = safe_strtab(strtab_hdr.index)
313
+
314
+ expect_unique.call DT::DT_SYMENT, false
315
+ expect_value "Dynamic SYMENT",by_type[DT::DT_SYMENT].first.val, @factory.sym.new.num_bytes
316
+ by_type.delete DT::DT_SYMENT
317
+ expect_unique.call DT::DT_SYMTAB, false
318
+ expect_value "Dynamic symbol table needs to be mapped", by_type[DT::DT_SYMTAB].first.val,@sect_types[SHT::SHT_DYNSYM].first.vaddr
319
+ by_type.delete DT::DT_SYMTAB
320
+ expect_unique.call DT::DT_HASH, true# We totally ignore the hash
321
+ by_type.delete DT::DT_HASH
322
+ expect_unique.call DT::DT_GNU_HASH,true
323
+ by_type.delete DT::DT_GNU_HASH
324
+
325
+ retval.needed = []
326
+ by_type[DT::DT_NEEDED].andand.each do |needed|
327
+ retval.needed << @dynstr[needed.val]
328
+ end
329
+ by_type.delete DT::DT_NEEDED
330
+
331
+ DYNAMIC_FLAGS.each do |tag, var|
332
+ val = false
333
+ expect_unique.call(tag,true) { |x| val = true}
334
+ instance_variable_set(var,val)
335
+ by_type.delete tag
336
+ end
337
+
338
+
339
+ progbits_by_addr = (@progbits+@nobits).group_by(&:addr) #TODO: check
340
+ #that vaddrs don't overlap
341
+
342
+ expect_unique.call(DT::DT_INIT,true).andand { |init|
343
+ # expect_value "DT_INIT should point to a valid progbits section",
344
+ # progbits_by_addr.include?(init.val), true
345
+
346
+ retval.init = init.val # progbits_by_addr[init.val].first
347
+ }
348
+ by_type.delete DT::DT_INIT
349
+ expect_unique.call(DT::DT_FINI,true).andand { |init|
350
+ # expect_value "DT_FINI should point to a valid progbits section",
351
+ # progbits_by_addr.include?(init.val), true
352
+
353
+ retval.fini = init.val #progbits_by_addr[init.val].first
354
+ }
355
+ by_type.delete DT::DT_FINI
356
+
357
+ expect_unique.call(DT::DT_INIT_ARRAY,true).andand{|initarray|
358
+ expect_value "DT_INITARRAY needs to point to a section", true, progbits_by_addr.include?(initarray.val)
359
+ sect = progbits_by_addr[initarray.val].first
360
+ expect_value "DT_INITARRAY section type", SHT::SHT_INIT_ARRAY,sect.sect_type
361
+ retval.init_array = sect
362
+ by_type.delete DT::DT_INIT_ARRAYSZ
363
+ }
364
+ by_type.delete DT::DT_INIT_ARRAY
365
+ expect_unique.call(DT::DT_FINI_ARRAY,true).andand{|finiarray|
366
+ expect_value "DT_FINIARRAY needs to point to a section", true, progbits_by_addr.include?(finiarray.val)
367
+ sect = progbits_by_addr[finiarray.val].first
368
+ expect_value "DT_FINIARRAY section type", SHT::SHT_FINI_ARRAY,sect.sect_type
369
+ retval.fini_array = sect
370
+ by_type.delete DT::DT_FINI_ARRAYSZ
371
+ }
372
+ by_type.delete DT::DT_FINI_ARRAY
373
+ expect_unique.call(DT::DT_PLTGOT,true).andand { |init|
374
+ expect_value "DT_PLTGOT should point to a valid progbits section",
375
+ progbits_by_addr.include?(init.val), true
376
+
377
+ retval.pltgot = progbits_by_addr[init.val].first
378
+ }#TODO: check processor supplements
379
+ by_type.delete DT::DT_PLTGOT
380
+ expect_unique.call(DT::DT_SONAME,true).andand {|soname|
381
+ retval.soname = @dynstr[soname.val]
382
+ }
383
+ by_type.delete DT::DT_SONAME
384
+ expect_unique.call(DT::DT_RPATH,true).andand{|rpath|
385
+ retval.rpath = @dynstr[rpath.val]
386
+ }
387
+ by_type.delete DT::DT_RPATH
388
+ #TODO: write 'expect_group'
389
+ expect_unique.call(DT::DT_RELA,true).andand{ |rela|
390
+ x= @sect_types[SHT::SHT_RELA].group_by{|x| x.vaddr.to_i}
391
+ expect_value "DT_RELA should point to a valid relocation section", x.include?(rela.val), true
392
+ #assert that no overlap?
393
+ reladyn_hdr = x[rela.val].first #TODO: Use parsed relocations!
394
+ expect_unique.call(DT::DT_RELAENT,false).andand {|relaent|
395
+ expect_value "DT_RELAENT size", relaent.val, @factory.rela.new.num_bytes
396
+ }
397
+ expect_unique.call(DT::DT_RELASZ,false).andand {|relasz|
398
+ expect_value "DT_RELASZ", relasz.val, reladyn_hdr.siz
399
+ }
400
+ # expect_unique.call(DT::DT_RELACOUNT,false).andand{|relacount|
401
+ # expect_value "DT_RELACOUNT", reladyn_hdr.siz / @factory.rela.new.num_bytes, relacount.val
402
+ # }
403
+ #RELACOUNT is a different beast
404
+ @reladyn_indices << reladyn_hdr.index
405
+ }
406
+ #TODO: maybe use eval to delete duplication?
407
+ expect_unique.call(DT::DT_REL,true).andand{ |rela|
408
+ x= @sect_types[SHT::SHT_REL].group_by{|x| x.vaddr.to_i}
409
+ expect_value "DT_REL should point to a valid relocation section", x.include?(rela.val), true
410
+ reladyn_hdr = x[rela.val] #TODO: Use parsed relocations!
411
+ expect_unique.call(DT::DT_RELENT,false).andand {|relaent|
412
+ expect_value "DT_RELENT size", relaent.val, @factory.rela.new.num_bytes
413
+ }
414
+ expect_unique.call(DT::DT_RELSZ,false).andand {|relasz|
415
+ expect_value "DT_RELSZ", relasz.val, reladyn_hdr.siz
416
+ }
417
+ @reladyn_indices << reladyn_hdr.index
418
+ }
419
+ [DT::DT_RELA, DT::DT_RELAENT, DT::DT_RELASZ, DT::DT_REL, DT::DT_RELENT, DT::DT_RELSZ, DT::DT_RELACOUNT].each {|x| by_type.delete x}
420
+ expect_unique.call(DT::DT_VERSYM,true).andand{|versym|
421
+ x = @sect_types[SHT::SHT_GNU_VERSYM].first
422
+ expect_value "One versym", @sect_types[SHT::SHT_GNU_VERSYM].size, 1
423
+ expect_value "SHT_GNU_VERSYM points to versym", x.vaddr == versym.val.to_i, true
424
+ }
425
+ by_type.delete DT::DT_VERSYM
426
+ expect_unique.call(DT::DT_VERDEF,true).andand{|verdef|
427
+ expect_value "Only one verdef", @sect_types[SHT::SHT_GNU_VERDEF].size, 1
428
+ x= @sect_types[SHT::SHT_GNU_VERDEF].first
429
+ expect_value "DT_VERDEF points to a SHT_GNU_VERDEF", x.vaddr.to_i, verdef.val.to_i
430
+ expect_unique.call(DT::DT_VERDEFNUM,false).andand{|verdefnum|
431
+ expect_value "DT_VERDEFNUM",x.info,verdefnum.val.to_i
432
+ }
433
+ }
434
+ expect_unique.call(DT::DT_VERNEED,true).andand{|verdef|
435
+ expect_value "Only one verdef", @sect_types[SHT::SHT_GNU_VERNEED].size, 1
436
+ x= @sect_types[SHT::SHT_GNU_VERNEED].first
437
+ expect_value "DT_VERNEED points to a SHT_GNU_VERNEED", x.vaddr.to_i, verdef.val.to_i
438
+ expect_unique.call(DT::DT_VERNEEDNUM,true).andand{|verdefnum|
439
+ expect_value "DT_VERNEEDNUM",x.info,verdefnum.val.to_i
440
+ }
441
+ }
442
+ [DT::DT_VERDEFNUM,DT::DT_VERNEEDNUM,DT::DT_VERNEED,DT::DT_VERDEF].each{|x| by_type.delete x}
443
+ #Parse RELA.plt or REL.plt
444
+ expect_unique.call(DT::DT_FLAGS,true).andand{|flags|
445
+ retval.flags = flags.val.to_i
446
+ }
447
+ expect_unique.call(DT::DT_FLAGS_1,true).andand{|flags1|
448
+ retval.flags1 = flags1.val.to_i
449
+ }
450
+ by_type.delete DT::DT_FLAGS
451
+ by_type.delete DT::DT_FLAGS_1
452
+ expect_unique.call(DT::DT_JMPREL,true).andand{ |rela| #TODO:Make
453
+ #this better too!!!
454
+ @jmprel_addr = rela.val
455
+ expect_unique.call(DT::DT_PLTREL,false).andand {|pltrel|
456
+ if pltrel.val == DT::DT_RELA
457
+ type = SHT::SHT_RELA
458
+ elsif pltrel.val == DT::DT_REL
459
+ type = SHT::SHT_REL
460
+ else
461
+ raise ArgumentError.new "Invalid DT_PLTREL"
462
+ end
463
+ x= @sect_types[type].group_by{|x| x.vaddr.to_i}
464
+ expect_value "DT_PLREL should point to a valid relocation section", x.include?(rela.val), true
465
+ reladyn_hdr = x[rela.val].first
466
+ #TODO: Use parsed #relocations!
467
+ expect_unique.call(DT::DT_PLTRELSZ,false).andand {|relasz|
468
+ expect_value "DT_PLTRELSZ", relasz.val, reladyn_hdr.siz
469
+ }
470
+ @reladyn_indices << reladyn_hdr.index
471
+ by_type.delete DT::DT_PLTRELSZ
472
+ }
473
+ by_type.delete DT::DT_PLTREL
474
+ }
475
+ by_type.delete DT::DT_JMPREL
476
+
477
+ retval.debug_val = []
478
+ (by_type[DT::DT_DEBUG] || []).each {|x| retval.debug_val << x.val}
479
+ by_type.delete DT::DT_DEBUG
480
+
481
+ #TODO: gnu extensions
482
+ retval.extra_dynamic = by_type.values.flatten.map(&:snapshot)
483
+ unless by_type.empty?
484
+ print "Warning, unparsed dynamic entries \n"
485
+ pp by_type
486
+ end
487
+ retval
488
+ end
489
+ def parse_note(note)
490
+ note_name = @shstrtab[note.name] || ".note.unk.#{note.off}"
491
+ @data.seek note.off
492
+ expect_value "Note alignment", note.addralign, NOTE_ALIGN
493
+ expect_value "Note flags", note.flags, NOTE_FLAGS
494
+ expect_value "Note entsize", note.entsize, NOTE_ENTSIZE
495
+ @unparsed_sections.delete @data
496
+ [note_name, @factory.note.read(@data).tap {|n|
497
+ expect_value "Note size",n.num_bytes, note.siz
498
+ } ]
499
+ end
500
+
501
+ def parse_phdrs()
502
+ #TODO: validate flags
503
+ by_type = @phdrs.group_by{|x| x.type.to_i}
504
+ by_type.delete PT::PT_NULL
505
+ process_unique = lambda do |sym| # Validates that either one
506
+ # or zero entries of this type exist, returning the one entry
507
+ # if it exists
508
+ if(by_type.include? sym)
509
+ expect_value "PHDR #{sym} count", by_type[sym].size,1
510
+ by_type[sym].first.tap { by_type.delete sym }
511
+ else
512
+ nil
513
+ end
514
+ end
515
+
516
+ process_unique.call(PT::PT_PHDR).andand do |pt_phdr|
517
+ expect_value "PHD offset",pt_phdr.off, @hdr.phoff
518
+ expect_value "PHDR size",pt_phdr.filesz, @hdr.phnum * @hdr.phentsize
519
+ end
520
+
521
+ by_type.delete PT::PT_LOAD # TODO:: validate range and that
522
+ # section vaddr is correct!
523
+ =begin all notes go into one or multiple program headers.
524
+ by_type[PT::PT_NOTE].each {|note|
525
+ expect_value "SHT_NOTE at this address",
526
+ @sect_types[SHT::SHT_NOTE].find{|n| note.vaddr.to_i == n.vaddr.to_i}.andand {|n|
527
+ [n.off.to_i,n.siz.to_i]
528
+ }, [note.off.to_i,note.filesz.to_i] }
529
+ =end
530
+ by_type.delete PT::PT_NOTE
531
+
532
+ process_unique.call(PT::PT_INTERP).andand do |pt_interp| #Technically
533
+ #not needed according to spec, INTERP doesn't need to have its
534
+ #own section. Instead just check what is at that vaddr
535
+ interp_section = @progbits.select {|x| x.addr == pt_interp.vaddr.to_i}.first
536
+ expect_value ".interp section", interp_section.nil?, false
537
+ @file.interp = BinData::Stringz.read(interp_section.data.read).snapshot
538
+ @progbits.delete interp_section
539
+ end
540
+ process_unique.call(PT::PT_DYNAMIC).andand do |pt_dynamic|
541
+ dynamic_section = @sect_types[SHT::SHT_DYNAMIC].first
542
+ expect_value "PT_dynamic address", pt_dynamic.vaddr, dynamic_section.vaddr
543
+ expect_value "PT_dynamic offset" , pt_dynamic.off, dynamic_section.off
544
+ expect_value "PT_dynamic size", pt_dynamic.filesz, dynamic_section.siz
545
+ end
546
+ @file.extra_phdrs = by_type.values.flatten
547
+ unless(@file.extra_phdrs.empty?)
548
+ print "Unparsed PHDR\n"
549
+ pp @file.extra_phdrs
550
+ end
551
+ end
552
+
553
+ def parse_with_factory()
554
+ @data.rewind
555
+ @hdr = @factory.hdr.read(@data)
556
+ @file.filetype = @hdr.type
557
+ @file.machine = @hdr.machine
558
+ @file.version = @hdr.version # Shouldn't this always be the current one
559
+ @file.flags = @hdr.flags
560
+ @file.entry = @hdr.entry
561
+ expect_value "ELF version",@file.version, ElfFlags::Version::EV_CURRENT
562
+ #pp hdr.snapshot
563
+ if @hdr.phnum != 0
564
+ expect_value "PHT size", @factory.phdr.new.num_bytes, @hdr.phentsize
565
+ @data.seek @hdr.phoff
566
+ @phdrs = BinData::Array.new(:type => @factory.phdr, :initial_length => @hdr.phnum)
567
+ @phdrs.read(@data)
568
+ else
569
+ @phdrs =[]
570
+ end
571
+
572
+ @data.seek @hdr.shoff
573
+ @shdrs = BinData::Array.new(:type => @factory.shdr, :initial_length => @hdr.shnum)
574
+ @shdrs.read(@data)
575
+ @unparsed_sections = Set.new []
576
+ expect_value "SHT size", @shdrs[0].num_bytes, @hdr.shentsize
577
+ @shstrtab = safe_strtab(@hdr.shstrndx)
578
+
579
+ @shdrs.to_enum.with_index.each do |elem, i|
580
+ elem.index = i
581
+ @unparsed_sections.add i
582
+ unless elem.flags & SHF::SHF_ALLOC
583
+ expect_value "Unallocated section address", elem.vaddr, 0
584
+ end
585
+ end
586
+
587
+
588
+ #Keep a hash of sections by type
589
+ @sect_types = @shdrs.group_by {|x| x.type.to_i}
590
+ #TODO: keep track which #sections we have already parsed to find unparsed sections
591
+ @bits_by_index = Hash.new.tap{|h| (@sect_types.values_at SHT::SHT_PROGBITS,SHT::SHT_NOBITS,SHT::SHT_INIT_ARRAY,SHT::SHT_FINI_ARRAY).reject(&:nil?).flatten.each { |s| h[s.index] = parse_progbits(s)} }
592
+ @progbits = @bits_by_index.values.select {|x| x.sect_type != SHT::SHT_NOBITS}
593
+ #TODO: just make NOBITS progbits for now
594
+ @file.progbits = @progbits
595
+
596
+ @progbits.select{|x| x.flags & SHF::SHF_TLS != 0}.each {|tdata|
597
+ @file.gnu_tls ||= TLS.new
598
+ expect_value "Only one .tdata per file",@file.gnu_tls.tdata.nil?,true
599
+ @file.gnu_tls.tdata = tdata
600
+ tdata.phdr = PT::PT_TLS
601
+ tdata.phdr_flags = PF::PF_R
602
+ }
603
+ @nobits = @bits_by_index.values.select {|x| x.sect_type == SHT::SHT_NOBITS}
604
+ @nobits.each{|nobit|
605
+ if nobit.flags & SHF::SHF_TLS != 0
606
+ @file.gnu_tls ||= TLS.new
607
+ @file.gnu_tls.tbss = nobit
608
+ else
609
+ pagesize = 4096
610
+ pagemask = 4095
611
+ nobit.size += pagesize - ( (nobit.size + nobit.addr.to_i) % pagesize)#HACK: This makes the BSS
612
+ expect_value "Nobit end alignment", (nobit.addr + nobit.size) % pagesize, 0
613
+ #section span a page, which it implicitly does due to the semantics of the mmap call
614
+ #HACK: TODO: Hardcoded pagesize
615
+ end
616
+ }
617
+ @file.nobits = @nobits
618
+
619
+ @relocatable_sections = SegmentTree.new(Hash.new.tap{|h|
620
+ (@progbits + @nobits).each{ |pb|
621
+ h[(pb.addr)..(pb.addr + pb.size)]=pb
622
+ }
623
+ })
624
+
625
+ parse_phdrs()
626
+
627
+ @file.dynamic = unique_section(@sect_types, ElfFlags::SectionType::SHT_DYNAMIC).andand{|dynamic| parse_dynamic dynamic} || Elf::Dynamic.new
628
+
629
+ @symtab = unique_section(@sect_types, ElfFlags::SectionType::SHT_SYMTAB).andand {|symtab| parse_symtable symtab, safe_strtab(symtab.link) } || []
630
+ @dynsym = unique_section(@sect_types, ElfFlags::SectionType::SHT_DYNSYM).andand {|symtab| parse_symtable symtab, safe_strtab(symtab.link) } || []
631
+
632
+ unique_section(@sect_types,ElfFlags::SectionType::SHT_GNU_VERNEED).andand{|verneed| parse_verneed verneed}
633
+ unique_section(@sect_types,ElfFlags::SectionType::SHT_GNU_VERDEF).andand{|verdef| parse_verdef verdef}
634
+ unique_section(@sect_types,ElfFlags::SectionType::SHT_GNU_VERSYM).andand{|versym|
635
+ expect_value "Need a dynsym when we have versym", @dynsym.nil?, false
636
+ parse_versym versym,@dynsym
637
+ }
638
+ #TODO: Parse versions in static symbols
639
+ @file.symbols = SymbolTable.new.tap{|h| (@symtab || []).each{|sym|
640
+ h<< sym if sym.name != "" #TODO: Represent nameless symbols - why did we drop these?
641
+ }}
642
+ if(@file.symbols.include? "_DYNAMIC" or @file.dynamic.soname =~ /^ld/) #HACK: This is how we detect ld.so and friends
643
+ @file.pinned_sections ||= {}
644
+ dyn_section = unique_section(@sect_types,Elf::SHT::SHT_DYNAMIC)
645
+ #expect_value "_DYNAMIC symbol points to dynamic section", @file.symbols["_DYNAMIC"].section, nil
646
+ #expect_value "_DYNAMIC symbol points to dynamic section", @file.symbols["_DYNAMIC"].sectoffset, dyn_section.vaddr.to_i
647
+ @file.pinned_sections[".dynamic"] = {vaddr: dyn_section.vaddr.to_i, size: dyn_section.siz.to_i}
648
+ end
649
+ dyn_by_name = {}
650
+ @dynsym.each{|d|
651
+ d.is_dynamic= true
652
+ dyn_by_name[d.name]||=[];
653
+ dyn_by_name[d.name] << d
654
+ }
655
+ @canonical_symbol = {}
656
+ @file.symbols.each {|staticsym|
657
+ @canonical_symbol[staticsym] = staticsym
658
+ next if staticsym.name == ""
659
+ next if staticsym.visibility == STB::STB_LOCAL
660
+ dyn_by_name[staticsym.name].andand.each {|sym|
661
+ next if sym.sectoffset != dynsym.sectoffset
662
+ next if sym.section != dynsym.section
663
+ @canonical_symbol[dynsym] = sym
664
+ staticsym.is_dynamic = true
665
+ staticsym.gnu_version = sym.gnu_version
666
+ expect_value "Dynamic #{sym.name} size", sym.size, staticsym.size
667
+ }
668
+ }
669
+ @dynsym.each {|sym|
670
+ unless @canonical_symbol.include? sym
671
+ @canonical_symbol[sym]= sym
672
+ @file.symbols << sym
673
+ end
674
+ }
675
+ # symtab_array = @file.symbols.to_a
676
+ # @canonical_symbol.each_value{|x| expect_value "x",symtab_array.include?(x), true}
677
+
678
+ rels_addrs = [ET::ET_EXEC, ET::ET_DYN].include? @hdr.type
679
+ rel = (@sect_types[SHT::SHT_RELA] || []).map {|rela| [rela.index, parse_rela(rela,rels_addrs)] }+ (@sect_types[SHT::SHT_REL] || []).map{|rel| [rela.index,parse_rel(rela,rels_addrs)]}
680
+
681
+ rels_by_index = Hash[*rel.flatten(1)]
682
+ @reladyn_indices.each {|reladyn|
683
+ rels_by_index[reladyn].each{|r|
684
+ r.is_dynamic = true
685
+ }
686
+ }
687
+
688
+
689
+
690
+
691
+
692
+ @file.notes = Hash[(@sect_types[SHT::SHT_NOTE] || []).map{|note| parse_note note}]
693
+ #TODO: expect non-nil dynamic for some types
694
+ @file.relocations = rels_by_index.values.flatten
695
+
696
+ #TODO: Validate flags
697
+ #TODO: Validate header?
698
+
699
+ end
700
+ end
701
+ def self.from_file(filename)
702
+ contents = IO.read(filename)
703
+ Parser.new(contents).file
704
+ end
705
+ end
706
+ end