Class: ELFTools::ELFFile

Inherits:
Object
  • Object
show all
Defined in:
lib/elftools/elf_file.rb

Overview

The main class for using elftools.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stream) ⇒ ELFFile

Instantiate an ELFTools::ELFFile object.

Examples:

ELFFile.new(File.open('/bin/cat'))
#=> #<ELFTools::ELFFile:0x00564b106c32a0 @elf_class=64, @endian=:little, @stream=#<File:/bin/cat>>

Parameters:

  • stream (#pos=, #read)

    The File object to be fetch information from.



24
25
26
27
28
29
# File 'lib/elftools/elf_file.rb', line 24

def initialize(stream)
  @stream = stream
  # always set binmode if stream is an IO object.
  @stream.binmode if @stream.respond_to?(:binmode)
  identify # fetch the most basic information
end

Instance Attribute Details

#elf_classInteger (readonly)

Returns 32 or 64.

Returns:

  • (Integer)

    32 or 64.



14
15
16
# File 'lib/elftools/elf_file.rb', line 14

def elf_class
  @elf_class
end

#endianSymbol (readonly)

Returns :little or :big.

Returns:

  • (Symbol)

    :little or :big.



15
16
17
# File 'lib/elftools/elf_file.rb', line 15

def endian
  @endian
end

#stream#pos=, #read (readonly)

Returns The File object.

Returns:

  • (#pos=, #read)

    The File object.



13
14
15
# File 'lib/elftools/elf_file.rb', line 13

def stream
  @stream
end

Instance Method Details

#build_idString?

Return the BuildID of ELF.

Examples:

elf.build_id
#=> '73ab62cb7bc9959ce053c2b711322158708cdc07'

Returns:

  • (String, nil)

    BuildID in hex form will be returned. nil is returned if the .note.gnu.build-id section is not found.



52
53
54
55
56
57
58
59
60
# File 'lib/elftools/elf_file.rb', line 52

def build_id
  section = section_by_name('.note.gnu.build-id')
  return nil if section.nil?

  note = section.notes.first
  return nil if note.nil?

  note.desc.unpack1('H*')
end

#each_sections {|section| ... } ⇒ Enumerator<ELFTools::Sections::Section>, Array<ELFTools::Sections::Section>

Iterate all sections.

All sections are lazy loading, the section only be created whenever accessing it. This method is useful for #section_by_name since not all sections need to be created.

Yield Parameters:

Yield Returns:

  • (void)

Returns:



122
123
124
125
126
127
128
# File 'lib/elftools/elf_file.rb', line 122

def each_sections(&block)
  return enum_for(:each_sections) unless block_given?

  Array.new(num_sections) do |i|
    section_at(i).tap(&block)
  end
end

#each_segments {|segment| ... } ⇒ Array<ELFTools::Segments::Segment>

Iterate all segments.

All segments are lazy loading, the segment only be created whenever accessing it. This method is useful for #segment_by_type since not all segments need to be created.

Yield Parameters:

Yield Returns:

  • (void)

Returns:



195
196
197
198
199
200
201
# File 'lib/elftools/elf_file.rb', line 195

def each_segments(&block)
  return enum_for(:each_segments) unless block_given?

  Array.new(num_segments) do |i|
    segment_at(i).tap(&block)
  end
end

#elf_typeString

Return the ELF type according to e_type.

Examples:

ELFFile.new(File.open('spec/files/libc.so.6')).elf_type
#=> 'DYN'
ELFFile.new(File.open('spec/files/amd64.elf')).elf_type
#=> 'EXEC'

Returns:

  • (String)

    Type in string format.



82
83
84
# File 'lib/elftools/elf_file.rb', line 82

def elf_type
  ELFTools::Constants::ET.mapping(header.e_type)
end

#headerELFTools::Structs::ELF_Ehdr

Return the file header.

Lazy loading.

Returns:



35
36
37
38
39
40
41
42
# File 'lib/elftools/elf_file.rb', line 35

def header
  return @header if defined?(@header)

  stream.pos = 0
  @header = Structs::ELF_Ehdr.new(endian:, offset: stream.pos)
  @header.elf_class = elf_class
  @header.read(stream)
end

#machineString

Get machine architecture.

Mappings of architecture can be found in Constants::EM.mapping.

Examples:

elf.machine
#=> 'Advanced Micro Devices X86-64'

Returns:

  • (String)

    Name of architecture.



71
72
73
# File 'lib/elftools/elf_file.rb', line 71

def machine
  ELFTools::Constants::EM.mapping(header.e_machine)
end

#num_sectionsInteger

Number of sections in this file.

Examples:

elf.num_sections
#=> 29

Returns:

  • (Integer)

    The desired number.



93
94
95
# File 'lib/elftools/elf_file.rb', line 93

def num_sections
  header.e_shnum
end

#num_segmentsInteger

Number of segments in this file.

Returns:

  • (Integer)

    The desited number.



181
182
183
# File 'lib/elftools/elf_file.rb', line 181

def num_segments
  header.e_phnum
end

#offset_from_vma(vma, size = 1) ⇒ Integer

Get the offset related to file, given virtual memory address.

This method should work no matter ELF is a PIE or not. This method refers from (actually equals to) binutils/readelf.c#offset_from_vma.

Examples:

elf = ELFTools::ELFFile.new(File.open('/bin/cat'))
elf.offset_from_vma(0x401337)
#=> 4919 # 0x1337

Parameters:

  • vma (Integer)

    The virtual address to be queried.

Returns:

  • (Integer)

    Related file offset.



293
294
295
296
297
# File 'lib/elftools/elf_file.rb', line 293

def offset_from_vma(vma, size = 1)
  segments_by_type(:load) do |seg|
    return seg.vma_to_offset(vma) if seg.vma_in?(vma, size)
  end
end

#patchesHash{Integer => String}

The patch status.

Returns:

  • (Hash{Integer => String})


301
302
303
304
305
306
307
308
309
# File 'lib/elftools/elf_file.rb', line 301

def patches
  patch = {}
  loaded_headers.each do |header|
    header.patches.each do |key, val|
      patch[key + header.offset] = val
    end
  end
  patch
end

#save(filename) ⇒ void

This method returns an undefined value.

Apply patches and save as filename.

Parameters:

  • filename (String)


315
316
317
318
319
320
321
322
# File 'lib/elftools/elf_file.rb', line 315

def save(filename)
  stream.pos = 0
  all = stream.read.force_encoding('ascii-8bit')
  patches.each do |pos, val|
    all[pos, val.size] = val
  end
  File.binwrite(filename, all)
end

#section_at(n) ⇒ ELFTools::Sections::Section?

Acquire the n-th section, 0-based.

Sections are lazy loaded.

Parameters:

  • n (Integer)

    The index.

Returns:



144
145
146
147
# File 'lib/elftools/elf_file.rb', line 144

def section_at(n)
  @sections ||= LazyArray.new(num_sections, &method(:create_section))
  @sections[n]
end

#section_by_name(name) ⇒ ELFTools::Sections::Section?

Acquire the section named as name.

Examples:

elf.section_by_name('.note.gnu.build-id')
#=> #<ELFTools::Sections::Section:0x005647b1282428>
elf.section_by_name('')
#=> #<ELFTools::Sections::NullSection:0x005647b11da110>
elf.section_by_name('no such section')
#=> nil

Parameters:

  • name (String)

    The desired section name.

Returns:



107
108
109
# File 'lib/elftools/elf_file.rb', line 107

def section_by_name(name)
  each_sections.find { |sec| sec.name == name }
end

#sectionsArray<ELFTools::Sections::Section>

Simply use #sections to get all sections.

Returns:



133
134
135
# File 'lib/elftools/elf_file.rb', line 133

def sections
  each_sections.to_a
end

#sections_by_type(type) {|section| ... } ⇒ Array<ELFTools::Sections::section>

Fetch all sections with specific type.

The available types are listed in Constants::PT. This method accept giving block.

Examples:

elf = ELFTools::ELFFile.new(File.open('spec/files/amd64.elf'))
elf.sections_by_type(:rela)
#=> [#<ELFTools::Sections::RelocationSection:0x00563cd3219970>,
#    #<ELFTools::Sections::RelocationSection:0x00563cd3b89d70>]

Parameters:

  • type (Integer, Symbol, String)

    The type needed, similar format as #segment_by_type.

Yield Parameters:

Yield Returns:

  • (void)

Returns:

  • (Array<ELFTools::Sections::section>)

    The target sections.



163
164
165
166
# File 'lib/elftools/elf_file.rb', line 163

def sections_by_type(type, &block)
  type = Util.to_constant(Constants::SHT, type)
  Util.select_by_type(each_sections, type, &block)
end

#segment_at(n) ⇒ ELFTools::Segments::Segment?

Acquire the n-th segment, 0-based.

Segments are lazy loaded.

Parameters:

  • n (Integer)

    The index.

Returns:



278
279
280
281
# File 'lib/elftools/elf_file.rb', line 278

def segment_at(n)
  @segments ||= LazyArray.new(num_segments, &method(:create_segment))
  @segments[n]
end

#segment_by_type(type) ⇒ ELFTools::Segments::Segment

Note:

This method will return the first segment found, to found all segments with specific type you can use #segments_by_type.

Get the first segment with p_type=type. The available types are listed in Constants::PT.

Examples:

# type as an integer
elf.segment_by_type(ELFTools::Constants::PT_NOTE)
#=>  #<ELFTools::Segments::NoteSegment:0x005629dda1e4f8>

elf.segment_by_type(4) # PT_NOTE
#=>  #<ELFTools::Segments::NoteSegment:0x005629dda1e4f8>

# type as a symbol
elf.segment_by_type(:PT_NOTE)
#=>  #<ELFTools::Segments::NoteSegment:0x005629dda1e4f8>

# you can do this
elf.segment_by_type(:note) # will be transformed into `PT_NOTE`
#=>  #<ELFTools::Segments::NoteSegment:0x005629dda1e4f8>

# type as a string
elf.segment_by_type('PT_NOTE')
#=>  #<ELFTools::Segments::NoteSegment:0x005629dda1e4f8>

# this is ok
elf.segment_by_type('note') # will be transformed into `PT_NOTE`
#=>  #<ELFTools::Segments::NoteSegment:0x005629dda1e4f8>
elf.segment_by_type(1337)
# ArgumentError: No constants in Constants::PT is 1337

elf.segment_by_type('oao')
# ArgumentError: No constants in Constants::PT named "PT_OAO"
elf.segment_by_type(0)
#=> nil # no such segment exists

Parameters:

  • type (Integer, Symbol, String)

    See examples for clear usage.

Returns:



251
252
253
254
# File 'lib/elftools/elf_file.rb', line 251

def segment_by_type(type)
  type = Util.to_constant(Constants::PT, type)
  each_segments.find { |seg| seg.header.p_type == type }
end

#segmentsArray<ELFTools::Segments::Segment>

Simply use #segments to get all segments.

Returns:



206
207
208
# File 'lib/elftools/elf_file.rb', line 206

def segments
  each_segments.to_a
end

#segments_by_type(type) {|segment| ... } ⇒ Array<ELFTools::Segments::Segment>

Fetch all segments with specific type.

If you want to find only one segment, use #segment_by_type instead. This method accept giving block.

Parameters:

  • type (Integer, Symbol, String)

    The type needed, same format as #segment_by_type.

Yield Parameters:

Yield Returns:

  • (void)

Returns:



266
267
268
269
# File 'lib/elftools/elf_file.rb', line 266

def segments_by_type(type, &block)
  type = Util.to_constant(Constants::PT, type)
  Util.select_by_type(each_segments, type, &block)
end

#strtab_sectionELFTools::Sections::StrTabSection

Get the string table section.

This section is acquired by using the e_shstrndx in ELF header.

Returns:



173
174
175
# File 'lib/elftools/elf_file.rb', line 173

def strtab_section
  section_at(header.e_shstrndx)
end