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.


22
23
24
25
26
27
# File 'lib/elftools/elf_file.rb', line 22

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.


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

def elf_class
  @elf_class
end

#endianSymbol (readonly)

Returns :little or :big.

Returns:

  • (Symbol)

    :little or :big.


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

def endian
  @endian
end

#stream#pos=, #read (readonly)

Returns The File object.

Returns:

  • (#pos=, #read)

    The File object.


11
12
13
# File 'lib/elftools/elf_file.rb', line 11

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.


49
50
51
52
53
54
55
# File 'lib/elftools/elf_file.rb', line 49

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.unpack('H*').first
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:


117
118
119
120
121
122
# File 'lib/elftools/elf_file.rb', line 117

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:


189
190
191
192
193
194
# File 'lib/elftools/elf_file.rb', line 189

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.


77
78
79
# File 'lib/elftools/elf_file.rb', line 77

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

#headerELFTools::Structs::ELF_Ehdr

Return the file header.

Lazy loading.

Returns:


33
34
35
36
37
38
39
# File 'lib/elftools/elf_file.rb', line 33

def header
  return @header if defined?(@header)
  stream.pos = 0
  @header = Structs::ELF_Ehdr.new(endian: 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.


66
67
68
# File 'lib/elftools/elf_file.rb', line 66

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.


88
89
90
# File 'lib/elftools/elf_file.rb', line 88

def num_sections
  header.e_shnum
end

#num_segmentsInteger

Number of segments in this file.

Returns:

  • (Integer)

    The desited number.


175
176
177
# File 'lib/elftools/elf_file.rb', line 175

def num_segments
  header.e_phnum
end

#offset_from_vma(vma, size = 0) ⇒ 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 address need query.

Returns:

  • (Integer)

    Offset related to file.


286
287
288
289
290
291
292
293
# File 'lib/elftools/elf_file.rb', line 286

def offset_from_vma(vma, size = 0)
  segments_by_type(:load) do |seg|
    if vma >= (seg.header.p_vaddr & -seg.header.p_align) &&
       vma + size <= seg.header.p_vaddr + seg.header.p_filesz
      return vma - seg.header.p_vaddr + seg.header.p_offset
    end
  end
end

#patchesHash{Integer => String}

The patch status.

Returns:

  • (Hash{Integer => String})

297
298
299
300
301
302
303
304
305
# File 'lib/elftools/elf_file.rb', line 297

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)

311
312
313
314
315
316
317
318
# File 'lib/elftools/elf_file.rb', line 311

def save(filename)
  stream.pos = 0
  all = stream.read.force_encoding('ascii-8bit')
  patches.each do |pos, val|
    all[pos, val.size] = val
  end
  IO.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:


138
139
140
141
# File 'lib/elftools/elf_file.rb', line 138

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:


102
103
104
# File 'lib/elftools/elf_file.rb', line 102

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:


127
128
129
# File 'lib/elftools/elf_file.rb', line 127

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.


157
158
159
160
# File 'lib/elftools/elf_file.rb', line 157

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:


271
272
273
274
# File 'lib/elftools/elf_file.rb', line 271

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 tranformed 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:


244
245
246
247
# File 'lib/elftools/elf_file.rb', line 244

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:


199
200
201
# File 'lib/elftools/elf_file.rb', line 199

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:


259
260
261
262
# File 'lib/elftools/elf_file.rb', line 259

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:


167
168
169
# File 'lib/elftools/elf_file.rb', line 167

def strtab_section
  section_at(header.e_shstrndx)
end