Skip to content

Commit

Permalink
fix duplication and bump 0.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
david942j committed Mar 15, 2017
1 parent ed51d31 commit 65c3d25
Show file tree
Hide file tree
Showing 18 changed files with 66 additions and 45 deletions.
3 changes: 2 additions & 1 deletion .codeclimate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ engines:
enabled: true
config:
languages:
- ruby
ruby:
mass_threshold: 21
exclude_paths:
- lib/elftools/structures.rb
fixme:
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
elftools (0.0.0)
elftools (0.1.0)
bindata (~> 2)

GEM
Expand Down
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@ ELF parser in pure ruby implementation. This work is inspired by [pyelftools](ht

The motivation to create this repository is want to be a dependency of [pwntools-ruby](https://github.com/peter50216/pwntools-ruby). Since ELF parser is a big work, it should not be implemented directly in pwntools.

**rbelftools**'s target is to create a nice ELF parser library in ruby. More features are work in progress.

# Install

Coming soon(?)
Available on RubyGems.org!
```bash
gem install elftools
```

# Example Usage

Expand All @@ -27,7 +32,7 @@ elf.machine
#=> 'Advanced Micro Devices X86-64'

elf.build_id
#=> 73ab62cb7bc9959ce053c2b711322158708cdc07
#=> '73ab62cb7bc9959ce053c2b711322158708cdc07'
```

## Sections
Expand Down Expand Up @@ -97,19 +102,19 @@ elf.segment_by_type(:interp).interp_name

1. Fully documented

Always important for an Open-Source project.
Always important for an Open-Source project. Online document is [here](http://www.rubydoc.info/github/david942j/rbelftools/master/frames)
2. Fully tested

Of course.
3. Lazy loading on everything

To use **rbelftools**, only need to pass the stream object of ELF file.
**rbelftools** will read the stream object **as least times as possible** when parsing
this file. Most information will not be fetched until you access it, which makes
the file. Most information will not be fetched until you need it, which makes
**rbelftools** efficient.
4. To be a library

**rbelftools** are designed to be a library for furthur usage.
**rbelftools** is designed to be a library for furthur usage.
It will _not_ add any too trivial features.
For example, to check if NX disabled, you can use
`elf.segment_by_type(:gnu_stack).executable?` but not `elf.nx?`
Expand Down
5 changes: 4 additions & 1 deletion elftools.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ Gem::Specification.new do |s|
s.version = ::ELFTools::VERSION
s.date = Date.today.to_s
s.summary = 'ELFTools - Pure ruby library for parsing ELF files'
s.description = 'A light weight ELF parser.'
s.description = <<-EOS
A light weight ELF parser. elftools is designed to be a low-level ELF parser.
Inspired by https://github.com/eliben/pyelftools.
EOS
s.authors = ['david942j']
s.email = ['[email protected]']
s.files = Dir['lib/**/*.rb'] + %w(README.md)
Expand Down
3 changes: 1 addition & 2 deletions lib/elftools/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,7 @@ module EM
EM_FRV = 0x5441 # Fujitsu FR-V
EM_AVR32 = 0x18ad # Atmel AVR32

# This is an interim value that we will use until the committee comes
# up with a final number.
# This is an interim value that we will use until the committee comes up with a final number.
EM_ALPHA = 0x9026

# Bogus old m32r magic number, used by old tools.
Expand Down
2 changes: 1 addition & 1 deletion lib/elftools/dynamic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def each_tags
# dynamic.tag_by_type('DT_PLTGOT')
# #=> #<ELFTools::Dynamic::Tag:0x0055d3d2d91b28 @header={:d_tag=>3, :d_val=>6295552}>
def tag_by_type(type)
type = Util.to_constant(Constants::DT, type, msg: 'DT type')
type = Util.to_constant(Constants::DT, type)
each_tags do |tag|
return tag if tag.header.d_tag == type
end
Expand Down
31 changes: 17 additions & 14 deletions lib/elftools/elf_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ class ELFFile
attr_reader :elf_class # @return [Integer] 32 or 64.
attr_reader :endian # @return [Symbol] +:little+ or +:big+.

# Instantiate a {ELFFile} object.
# Instantiate an {ELFFile} object.
#
# @param [File] stream
# The +File+ object to be fetch information from.
# @example
# ELFFile.new(File.open('/bin/cat'))
# #=> #<ELFTools::ELFFile:0x005647b1182ed8>
# #=> #<ELFTools::ELFFile:0x00564b106c32a0 @elf_class=64, @endian=:little, @stream=#<File:/bin/cat>>
def initialize(stream)
@stream = stream
identify # fetch the most basic information
Expand All @@ -36,7 +36,7 @@ def header
@header.read(stream)
end

# Return the BuildID of current file.
# Return the BuildID of ELF.
# @return [String, NilClass]
# BuildID in hex form will be returned.
# +nil+ is returned if the .note.gnu.build-id section
Expand All @@ -55,7 +55,7 @@ def build_id
# Get Machine architecture.
#
# Mappings of architecture can be found
# in {ELFTools::Constants::EM#mapping}.
# in {ELFTools::Constants::EM.mapping}.
# @return [String]
# Name of architecture.
# @example
Expand Down Expand Up @@ -99,7 +99,7 @@ def section_by_name(name)
# Iterate all sections.
#
# All sections are lazy loading, the section
# only create whenever accessing it.
# only be created whenever accessing it.
# This method is useful for {#section_by_name}
# since not all sections need to be created.
# @param [Block] block
Expand Down Expand Up @@ -128,8 +128,11 @@ def section_at(n)
@sections[n]
end

# Get the StringTable section.
# @return [ELFTools::Sections::Section] The desired section.
# Get the string table section.
#
# This section is acquired by using the +e_shstrndx+
# in ELF header.
# @return [ELFTools::Sections::StrTabSection] The desired section.
def strtab_section
section_at(header.e_shstrndx)
end
Expand All @@ -145,7 +148,7 @@ def num_segments
# Iterate all segments.
#
# All segments are lazy loading, the segment
# only create whenever accessing it.
# only be created whenever accessing it.
# This method is useful for {#segment_by_type}
# since not all segments need to be created.
# @param [Block] block
Expand Down Expand Up @@ -196,17 +199,17 @@ def each_segments
# #=> #<ELFTools::Segments::NoteSegment:0x005629dda1e4f8>
# @example
# elf.segment_by_type(1337)
# # ArgumentError: No PT type is "1337"
# # ArgumentError: No constants in Constants::PT is 1337
#
# elf.segment_by_type('oao')
# # ArgumentError: No PT type named "PT_OAO"
# # ArgumentError: No constants in Constants::PT named "PT_OAO"
# @example
# elf.segment_by_type(0)
# #=> nil # no such segment exists
def segment_by_type(type)
type = Util.to_constant(Constants::PT, type, msg: 'PT type')
each_segments do |segment|
return segment if segment.header.p_type == type
type = Util.to_constant(Constants::PT, type)
each_segments do |seg|
return seg if seg.header.p_type == type
end
nil
end
Expand All @@ -218,7 +221,7 @@ def segment_by_type(type)
# The type needed, same format as {#segment_by_type}.
# @return [Array<ELFTools::Segments::Segment>] The target segments.
def segments_by_type(type)
type = Util.to_constant(Constants::PT, type, msg: 'PT type')
type = Util.to_constant(Constants::PT, type)
segments.select { |segment| segment.header.p_type == type }
end

Expand Down
3 changes: 1 addition & 2 deletions lib/elftools/note.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ def initialize(header, stream, offset)
def name
return @name if @name
stream.pos = @offset + SIZE_OF_NHDR
# XXX: Should we remove the last null byte?
@name = stream.read(header.n_namesz)
@name = stream.read(header.n_namesz)[0..-2]
end

# Description of this note.
Expand Down
2 changes: 2 additions & 0 deletions lib/elftools/sections/dynamic_section.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ module Sections
class DynamicSection < Section
include ELFTools::Dynamic

# Get the start address of tags.
# @return [Integer] Start address of tags.
def tag_start
header.sh_offset
end
Expand Down
4 changes: 3 additions & 1 deletion lib/elftools/sections/null_section.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ module ELFTools
module Sections
# Class of null section.
# Null section is for specific the end
# of linked list(+sh_link+) between sections.
# of linked list (+sh_link+) between sections.
class NullSection < Section
# Is this a null section?
# @return [Boolean] Yes it is.
def null?
true
end
Expand Down
2 changes: 2 additions & 0 deletions lib/elftools/sections/section.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def data
stream.read(header.sh_size)
end

# Is this a null section?
# @return [Boolean] No it's not.
def null?
false
end
Expand Down
4 changes: 2 additions & 2 deletions lib/elftools/sections/sym_tab_section.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def symbol_at(n)
# Iterate all symbols.
#
# All symbols are lazy loading, the symbol
# only create whenever accessing it.
# only be created whenever accessing it.
# This method is useful for {#symbol_by_name}
# since not all symbols need to be created.
# @param [Block] block
Expand Down Expand Up @@ -103,7 +103,7 @@ class Symbol
# @param [ELFTools::ELF32_sym, ELFTools::ELF64_sym] header
# The symbol header.
# @param [File] stream The streaming object.
# @param [ELFTools::Sections::SymStrSection, Proc] symstr
# @param [ELFTools::Sections::StrTabSection, Proc] symstr
# The symbol string section.
# If +Proc+ is given, it will be called at the first time
# access {Symbol#name}.
Expand Down
2 changes: 2 additions & 0 deletions lib/elftools/segments/dynamic_segment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ module Segments
# This class knows how to get the list of dynamic tags.
class DynamicSegment < Segment
include Dynamic # rock!
# Get the start address of tags.
# @return [Integer] Start address of tags.
def tag_start
header.p_offset
end
Expand Down
6 changes: 3 additions & 3 deletions lib/elftools/segments/interp_segment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ module Segments
# ELF interpreter.
class InterpSegment < Segment
# Get the path of interpreter.
# @return [String] The interpreter name.
# @return [String] Path to the interpreter.
# @example
# interp_name
# #=> /lib64/ld-linux-x86-64.so.2
# interp_segment.interp_name
# #=> '/lib64/ld-linux-x86-64.so.2'
def interp_name
data[0..-2] # remove last null byte
end
Expand Down
13 changes: 8 additions & 5 deletions lib/elftools/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module ClassMethods
# +2**bit+.
# @param [Integer] num Number to be rounded-up.
# @param [Integer] bit How many bit to be aligned.
# @return [Integer] See examples.
# @example
# align(10, 1) #=> 10
# align(10, 2) #=> 12
Expand All @@ -27,19 +28,21 @@ def align(num, bit)
# @param [Integer, Symbol, String] val
# Desired value.
# @return [Integer]
# Rurrently this method should always return a value
# Currently this method always return a value
# from {ELFTools::Constants}.
def to_constant(mod, val, msg: 'constant')
def to_constant(mod, val)
# Ignore the outest name.
module_name = mod.name.sub('ELFTools::', '')
# if val is an integer, check if exists in mod
if val.is_a?(Integer)
return val if mod.constants.any? { |c| mod.const_get(c) == val }
raise ArgumentError, "No #{msg} is \"#{val}\""
raise ArgumentError, "No constants in #{module_name} is #{val}"
end
val = val.to_s.upcase
prefix = mod.name.split('::')[-1]
prefix = module_name.split('::')[-1]
val = prefix + '_' + val unless val.start_with?(prefix)
val = val.to_sym
raise ArgumentError, "No #{msg} named \"#{val}\"" unless mod.const_defined?(val)
raise ArgumentError, "No constants in #{module_name} named \"#{val}\"" unless mod.const_defined?(val)
mod.const_get(val)
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/elftools/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module ELFTools
VERSION = '0.0.0'.freeze
VERSION = '0.1.0'.freeze
end
4 changes: 2 additions & 2 deletions spec/dynamic_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
expect(@segment.tag_by_type('DT_SYMTAB').header.d_tag).to eq 6
expect(@segment.tag_by_type('SymTab').header.d_tag).to eq 6

expect { @segment.tag_by_type(1337) }.to raise_error(ArgumentError, 'No DT type is "1337"')
expect { @segment.tag_by_type(:oao) }.to raise_error(ArgumentError, 'No DT type named "DT_OAO"')
expect { @segment.tag_by_type(1337) }.to raise_error(ArgumentError, 'No constants in Constants::DT is 1337')
expect { @segment.tag_by_type(:xx) }.to raise_error(ArgumentError, 'No constants in Constants::DT named "DT_XX"')
end

it 'tags size' do
Expand Down
8 changes: 4 additions & 4 deletions spec/elf_file_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
# There're two note sections
expect(secs.size).to be 2
bid_sec = secs.last
expect(bid_sec.notes[0].name).to eq "GNU\x00"
expect(bid_sec.notes[0].name).to eq 'GNU'
# The build id
expect(bid_sec.notes[0].desc.unpack('H*')[0]).to eq '73ab62cb7bc9959ce053c2b711322158708cdc07'
end
Expand Down Expand Up @@ -105,7 +105,7 @@

it 'notes' do
seg = @elf.segment_by_type(ELFTools::Constants::PT_NOTE)
expect(seg.notes[1].name).to eq "GNU\x00"
expect(seg.notes[1].name).to eq 'GNU'
# The build id
expect(seg.notes[1].desc.unpack('H*')[0]).to eq '73ab62cb7bc9959ce053c2b711322158708cdc07'
end
Expand All @@ -118,8 +118,8 @@
expect(@elf.segment_by_type('NoTe')).to be_a ELFTools::Segments::NoteSegment
expect(@elf.segment_by_type('PT_NOTE')).to be_a ELFTools::Segments::NoteSegment
expect(@elf.segment_by_type(:PT_NOTE)).to be_a ELFTools::Segments::NoteSegment
expect { @elf.segment_by_type(1337) }.to raise_error(ArgumentError, 'No PT type is "1337"')
expect { @elf.segment_by_type(:oao) }.to raise_error(ArgumentError, 'No PT type named "PT_OAO"')
expect { @elf.segment_by_type(1337) }.to raise_error(ArgumentError, 'No constants in Constants::PT is 1337')
expect { @elf.segment_by_type(:xx) }.to raise_error(ArgumentError, 'No constants in Constants::PT named "PT_XX"')
end
end
end

0 comments on commit 65c3d25

Please sign in to comment.