Pure ruby library for parsing and patching ELF files.
ELF parser in pure ruby implementation. This work is inspired by pyelftools by Eli Bendersky.
The motivation to create this repository is want to be a dependency of 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 parsing library in ruby. More features remain a work in progress.
Available on RubyGems.org!
gem install elftools
- Supports both big and little endian
- ELF parser
- ELF headers patcher
See example usage for more details.
require 'elftools'
elf = ELFTools::ELFFile.new(File.open('spec/files/amd64.elf'))
#=> #<ELFTools::ELFFile:0x00560b147f8328 @elf_class=64, @endian=:little, @stream=#<File:spec/files/amd64>>
elf.machine
#=> 'Advanced Micro Devices X86-64'
elf.build_id
#=> '73ab62cb7bc9959ce053c2b711322158708cdc07'
elf.section_by_name('.dynstr')
#=>
# #<ELFTools::Sections::StrTabSection:0x00560b148cef40
# @header=
# {:sh_name=>86,
# :sh_type=>3,
# :sh_flags=>2,
# :sh_addr=>4195224,
# :sh_offset=>920,
# :sh_size=>113,
# :sh_link=>0,
# :sh_info=>0,
# :sh_addralign=>1,
# :sh_entsize=>0},
# @name=".dynstr">
elf.sections.map(&:name).join(' ')
#=> " .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss .comment .shstrtab .symtab .strtab"
elf.section_by_name('.note.gnu.build-id').data
#=> "\x04\x00\x00\x00\x14\x00\x00\x00\x03\x00\x00\x00GNU\x00s\xABb\xCB{\xC9\x95\x9C\xE0S\xC2\xB7\x112!Xp\x8C\xDC\a"
symtab_section = elf.section_by_name('.symtab')
symtab_section.num_symbols
#=> 75
symtab_section.symbol_by_name('puts@@GLIBC_2.2.5')
#=>
# #<ELFTools::Sections::Symbol:0x00560b14af67a0
# @header={:st_name=>348, :st_info=>18, :st_other=>0, :st_shndx=>0, :st_value=>0, :st_size=>0},
# @name="puts@@GLIBC_2.2.5">
symbols = symtab_section.symbols # Array of symbols
symbols.map(&:name).reject(&:empty?).first(5).join(' ')
#=> "crtstuff.c __JCR_LIST__ deregister_tm_clones register_tm_clones __do_global_dtors_aux"
elf.segment_by_type(:note)
#=>
# #<ELFTools::Segments::NoteSegment:0x00555beaafe218
# @header=
# {:p_type=>4,
# :p_flags=>4,
# :p_offset=>624,
# :p_vaddr=>624,
# :p_paddr=>624,
# :p_filesz=>68,
# :p_memsz=>68,
# :p_align=>4}>
elf.segment_by_type(:interp).interp_name
#=> "/lib64/ld-linux-x86-64.so.2"
elf = ELFTools::ELFFile.new(File.open('spec/files/amd64.elf'))
# Use relocation to get plt names.
rela_section = elf.sections_by_type(:rela).last
rela_section.name
#=> ".rela.plt"
relocations = rela_section.relocations
relocations.map { |r| '%x' % r.header.r_info }
#=> ["100000007", "200000007", "300000007", "400000007", "500000007", "700000007"]
symtab = elf.section_at(rela_section.header.sh_link) # get the symbol table section
relocations.map { |r| symtab.symbol_at(r.symbol_index).name }
#=> ["puts", "__stack_chk_fail", "printf", "__libc_start_main", "fgets", "scanf"]
Patch ELF is so easy!
All kinds of headers (i.e. Ehdr
, Shdr
, Phdr
, etc.) can be patched.
Patched slots will not be applied on the opened file.
Invoke elf.save(filename)
to save the patched ELF into filename
.
elf = ELFTools::ELFFile.new(File.open('spec/files/amd64.elf'))
elf.machine
#=> "Advanced Micro Devices X86-64"
elf.header.e_machine = 40
elf.machine
#=> "ARM"
interp_segment = elf.segment_by_type(:interp)
interp_segment.interp_name
#=> "/lib64/ld-linux-x86-64.so.2"
interp_segment.header.p_filesz
#=> 28
interp_segment.header.p_filesz = 20
interp_segment.interp_name
#=> "/lib64/ld-linux-x86"
# save the patched ELF
elf.save('elf.patched')
# in bash
# $ file elf.patched
# elf.patched: ELF 64-bit LSB executable, ARM, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86, for GNU...
- Fully documented
Always important for an Open-Source project. Online document is here - Fully tested
Of course. - 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 the file. Most information will not be fetched until you need it, which makes rbelftools efficient. - To be a library
rbelftools is designed to be a library for further 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 notelf.nx?
- Section and segment parser
Providing common sections and segments parser. For example,.symtab
,.shstrtab
.dynamic
sections andINTERP
,DYNAMIC
segments, etc.
git clone https://github.com/david942j/rbelftools
cd rbelftools
bundle
bundle exec rake
Any comments or suggestions are welcome!
rbelftools can be used and has been fully tested on all platforms include Linux, OSX, and Windows!
MIT License