elftools 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,123 @@
1
+ require 'elftools/sections/section'
2
+
3
+ module ELFTools
4
+ module Sections
5
+ # Class of symbol table section.
6
+ # Usually for section .symtab and .dynsym,
7
+ # which will refer to symbols in ELF file.
8
+ class SymTabSection < Section
9
+ # Instantiate a {SymTabSection} object.
10
+ # There's a +section_at+ lambda for {SymTabSection}
11
+ # to easily fetch other sections.
12
+ # @param [ELFTools::ELF_Shdr] header
13
+ # See {Section#initialize} for more information.
14
+ # @param [File] stream
15
+ # See {Section#initialize} for more information.
16
+ # @param [Proc] section_at
17
+ # The method for fetching other sections by index.
18
+ # This lambda should be {ELFTools::ELFFile#section_at}.
19
+ def initialize(header, stream, section_at: nil, **_kwagrs)
20
+ @section_at = section_at
21
+ # For faster #symbol_by_name
22
+ @symbol_name_map = {}
23
+ super
24
+ end
25
+
26
+ # Number of symbols.
27
+ # @return [Integer] The number.
28
+ # @example
29
+ # symtab.num_symbols
30
+ # #=> 75
31
+ def num_symbols
32
+ header.sh_size / header.sh_entsize
33
+ end
34
+
35
+ # Acquire the +n+-th symbol, 0-based.
36
+ #
37
+ # Symbols are lazy loaded.
38
+ # @param [Integer] n The index.
39
+ # @return [ELFTools:Symbol, NilClass]
40
+ # The target symbol.
41
+ # If +n+ is out of bound, +nil+ is returned.
42
+ def symbol_at(n)
43
+ @symbols ||= LazyArray.new(num_symbols, &method(:create_symbol))
44
+ @symbols[n]
45
+ end
46
+
47
+ # Iterate all symbols.
48
+ #
49
+ # All symbols are lazy loading, the symbol
50
+ # only be created whenever accessing it.
51
+ # This method is useful for {#symbol_by_name}
52
+ # since not all symbols need to be created.
53
+ # @param [Block] block
54
+ # Just like +Array#each+, you can give a block.
55
+ # @return [Array<ELFTools::symbol>]
56
+ # The whole symbols will be returned.
57
+ def each_symbols
58
+ Array.new(num_symbols) do |i|
59
+ sym = symbol_at(i)
60
+ block_given? ? yield(sym) : sym
61
+ end
62
+ end
63
+
64
+ alias symbols each_symbols
65
+
66
+ # Get symbol by it's name.
67
+ # @param [String] name
68
+ # The name of symbol.
69
+ # @return [ELFTools::Symbol] Desired symbol.
70
+ def symbol_by_name(name)
71
+ return @symbol_name_map[name] if @symbol_name_map[name]
72
+ each_symbols do |symbol|
73
+ @symbol_name_map[symbol.name] = symbol
74
+ return symbol if symbol.name == name
75
+ end
76
+ nil
77
+ end
78
+
79
+ # Return the symbol string section.
80
+ # Lazy loaded.
81
+ # @return [ELFTools::Sections::StrTabSection] The string table section.
82
+ def symstr
83
+ @symstr ||= @section_at.call(header.sh_link)
84
+ end
85
+
86
+ private
87
+
88
+ def create_symbol(n)
89
+ stream.pos = header.sh_offset + n * header.sh_entsize
90
+ sym = ELF_sym[header.elf_class].new(endian: header.class.self_endian)
91
+ sym.read(stream)
92
+ Symbol.new(sym, stream, symstr: method(:symstr))
93
+ end
94
+ end
95
+
96
+ # Class of symbol.
97
+ # XXX: Should this class be defined in an independent file?
98
+ class Symbol
99
+ attr_reader :header # @return [ELFTools::ELF32_sym, ELFTools::ELF64_sym] Section header.
100
+ attr_reader :stream # @return [File] Streaming object.
101
+
102
+ # Instantiate a {ELFTools::Symbol} object.
103
+ # @param [ELFTools::ELF32_sym, ELFTools::ELF64_sym] header
104
+ # The symbol header.
105
+ # @param [File] stream The streaming object.
106
+ # @param [ELFTools::Sections::StrTabSection, Proc] symstr
107
+ # The symbol string section.
108
+ # If +Proc+ is given, it will be called at the first time
109
+ # access {Symbol#name}.
110
+ def initialize(header, stream, symstr: nil)
111
+ @header = header
112
+ @stream = stream
113
+ @symstr = symstr
114
+ end
115
+
116
+ # Return the symbol name.
117
+ # @return [String] The name.
118
+ def name
119
+ @name ||= @symstr.call.name_at(header.st_name)
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,18 @@
1
+ require 'elftools/segments/segment'
2
+ require 'elftools/dynamic'
3
+
4
+ module ELFTools
5
+ module Segments
6
+ # Class for dynamic table segment.
7
+ #
8
+ # This class knows how to get the list of dynamic tags.
9
+ class DynamicSegment < Segment
10
+ include Dynamic # rock!
11
+ # Get the start address of tags.
12
+ # @return [Integer] Start address of tags.
13
+ def tag_start
14
+ header.p_offset
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ require 'elftools/segments/segment'
2
+
3
+ module ELFTools
4
+ module Segments
5
+ # For DT_INTERP segment, knows how to get path of
6
+ # ELF interpreter.
7
+ class InterpSegment < Segment
8
+ # Get the path of interpreter.
9
+ # @return [String] Path to the interpreter.
10
+ # @example
11
+ # interp_segment.interp_name
12
+ # #=> '/lib64/ld-linux-x86-64.so.2'
13
+ def interp_name
14
+ data[0..-2] # remove last null byte
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ require 'elftools/note'
2
+ require 'elftools/segments/segment'
3
+
4
+ module ELFTools
5
+ module Segments
6
+ # Class of note segment.
7
+ class NoteSegment < Segment
8
+ # Load note related methods.
9
+ include ELFTools::Note
10
+
11
+ # Address offset of notes start.
12
+ # @return [Integer] The offset.
13
+ def note_start
14
+ header.p_offset
15
+ end
16
+
17
+ # The total size of notes in this segment.
18
+ # @return [Integer] The size.
19
+ def note_total_size
20
+ header.p_filesz
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,44 @@
1
+ module ELFTools
2
+ module Segments
3
+ # Base class of segments.
4
+ class Segment
5
+ attr_reader :header # @return [ELFTools::ELF32_Phdr, ELFTools::ELF64_Phdr] Program header.
6
+ attr_reader :stream # @return [File] Streaming object.
7
+
8
+ # Instantiate a {Segment} object.
9
+ # @param [ELFTools::ELF32_Phdr, ELFTools::ELF64_Phdr] header
10
+ # Program header.
11
+ # @param [File] stream
12
+ # Streaming object.
13
+ def initialize(header, stream)
14
+ @header = header
15
+ @stream = stream
16
+ end
17
+
18
+ # The content in this segment.
19
+ # @return [String] The content.
20
+ def data
21
+ stream.pos = header.p_offset
22
+ stream.read(header.p_filesz)
23
+ end
24
+
25
+ # Is this segment readable?
26
+ # @return [Boolean] Ture or false.
27
+ def readable?
28
+ (header.p_flags & 4) == 4
29
+ end
30
+
31
+ # Is this segment writable?
32
+ # @return [Boolean] Ture or false.
33
+ def writable?
34
+ (header.p_flags & 2) == 2
35
+ end
36
+
37
+ # Is this segment executable?
38
+ # @return [Boolean] Ture or false.
39
+ def executable?
40
+ (header.p_flags & 1) == 1
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,30 @@
1
+ # Require this file to load all segment classes.
2
+
3
+ require 'elftools/segments/segment'
4
+
5
+ require 'elftools/segments/dynamic_segment'
6
+ require 'elftools/segments/interp_segment'
7
+ require 'elftools/segments/note_segment'
8
+
9
+ module ELFTools
10
+ # Module for defining different types of segments.
11
+ module Segments
12
+ # Class methods of {Segments::Segment}.
13
+ class << Segment
14
+ # Use different class according to +header.p_type+.
15
+ # @param [ELFTools::ELF32_Phdr, ELFTools::ELF64_Phdr] header Program header of a segment.
16
+ # @param [File] stream Streaming object.
17
+ # @return [ELFTools::Segments::Segment]
18
+ # Return object dependes on +header.p_type+.
19
+ def create(header, stream, *args)
20
+ klass = case header.p_type
21
+ when Constants::PT_DYNAMIC then DynamicSegment
22
+ when Constants::PT_INTERP then InterpSegment
23
+ when Constants::PT_NOTE then NoteSegment
24
+ else Segment
25
+ end
26
+ klass.new(header, stream, *args)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,133 @@
1
+ require 'bindata'
2
+ module ELFTools
3
+ # The base structure to define common methods.
4
+ class ELFStruct < BinData::Record
5
+ CHOICE_SIZE_T = {
6
+ selection: :elf_class, choices: { 32 => :uint32, 64 => :uint64 }
7
+ }.freeze
8
+
9
+ attr_accessor :elf_class # @return [Integer] 32 or 64.
10
+
11
+ # Hacking to get endian of current class
12
+ # @return [Symbol, NilClass] +:little+ or +:big+.
13
+ def self.self_endian
14
+ bindata_name[-2..-1] == 'ge' ? :big : :little
15
+ end
16
+ end
17
+
18
+ # ELF header structure.
19
+ class ELF_Ehdr < ELFStruct
20
+ endian :big_and_little
21
+ struct :e_ident do
22
+ string :magic, read_length: 4
23
+ int8 :ei_class
24
+ int8 :ei_data
25
+ int8 :ei_version
26
+ int8 :ei_osabi
27
+ int8 :ei_abiversion
28
+ string :ei_padding, read_length: 7 # no use
29
+ end
30
+ uint16 :e_type
31
+ uint16 :e_machine
32
+ uint32 :e_version
33
+ # entry point
34
+ choice :e_entry, **CHOICE_SIZE_T
35
+ choice :e_phoff, **CHOICE_SIZE_T
36
+ choice :e_shoff, **CHOICE_SIZE_T
37
+ uint32 :e_flags
38
+ uint16 :e_ehsize # size of this header
39
+ uint16 :e_phentsize # size of each segment
40
+ uint16 :e_phnum # number of segments
41
+ uint16 :e_shentsize # size of each section
42
+ uint16 :e_shnum # number of sections
43
+ uint16 :e_shstrndx # index of string table section
44
+ end
45
+
46
+ # Section header structure.
47
+ class ELF_Shdr < ELFStruct
48
+ endian :big_and_little
49
+ uint32 :sh_name
50
+ uint32 :sh_type
51
+ choice :sh_flags, **CHOICE_SIZE_T
52
+ choice :sh_addr, **CHOICE_SIZE_T
53
+ choice :sh_offset, **CHOICE_SIZE_T
54
+ choice :sh_size, **CHOICE_SIZE_T
55
+ uint32 :sh_link
56
+ uint32 :sh_info
57
+ choice :sh_addralign, **CHOICE_SIZE_T
58
+ choice :sh_entsize, **CHOICE_SIZE_T
59
+ end
60
+
61
+ # Program header structure for 32bit.
62
+ class ELF32_Phdr < ELFStruct
63
+ endian :big_and_little
64
+ uint32 :p_type
65
+ uint32 :p_offset
66
+ uint32 :p_vaddr
67
+ uint32 :p_paddr
68
+ uint32 :p_filesz
69
+ uint32 :p_memsz
70
+ uint32 :p_flags
71
+ uint32 :p_align
72
+ end
73
+
74
+ # Program header structure for 64bit.
75
+ class ELF64_Phdr < ELFStruct
76
+ endian :big_and_little
77
+ uint32 :p_type
78
+ uint32 :p_flags
79
+ uint64 :p_offset
80
+ uint64 :p_vaddr
81
+ uint64 :p_paddr
82
+ uint64 :p_filesz
83
+ uint64 :p_memsz
84
+ uint64 :p_align
85
+ end
86
+ ELF_Phdr = {
87
+ 32 => ELF32_Phdr,
88
+ 64 => ELF64_Phdr
89
+ }.freeze
90
+
91
+ # Symbol structure for 32bit.
92
+ class ELF32_sym < ELFStruct
93
+ endian :big_and_little
94
+ uint32 :st_name
95
+ uint32 :st_value
96
+ uint32 :st_size
97
+ uint8 :st_info
98
+ uint8 :st_other
99
+ uint16 :st_shndx
100
+ end
101
+
102
+ # Symbol structure for 64bit.
103
+ class ELF64_sym < ELFStruct
104
+ endian :big_and_little
105
+ uint32 :st_name # Symbol name, index in string tbl
106
+ uint8 :st_info # Type and binding attributes
107
+ uint8 :st_other # No defined meaning, 0
108
+ uint16 :st_shndx # Associated section index
109
+ uint64 :st_value # Value of the symbol
110
+ uint64 :st_size # Associated symbol size
111
+ end
112
+ ELF_sym = {
113
+ 32 => ELF32_sym,
114
+ 64 => ELF64_sym
115
+ }.freeze
116
+
117
+ # Note header.
118
+ class ELF_Nhdr < ELFStruct
119
+ endian :big_and_little
120
+ uint32 :n_namesz # Name size
121
+ uint32 :n_descsz # Content size
122
+ uint32 :n_type # Content type
123
+ end
124
+
125
+ # Dynamic tag header.
126
+ class ELF_Dyn < ELFStruct
127
+ endian :big_and_little
128
+ choice :d_tag, selection: :elf_class, choices: { 32 => :int32, 64 => :int64 }
129
+ # This is an union type named +d_un+ in original source,
130
+ # simplify it to be +d_val+ here.
131
+ choice :d_val, **CHOICE_SIZE_T
132
+ end
133
+ end
@@ -0,0 +1,51 @@
1
+ module ELFTools
2
+ # Define some util methods.
3
+ module Util
4
+ # Class methods.
5
+ module ClassMethods
6
+ # Round up the number to be mulitple of
7
+ # +2**bit+.
8
+ # @param [Integer] num Number to be rounded-up.
9
+ # @param [Integer] bit How many bit to be aligned.
10
+ # @return [Integer] See examples.
11
+ # @example
12
+ # align(10, 1) #=> 10
13
+ # align(10, 2) #=> 12
14
+ # align(10, 3) #=> 16
15
+ # align(10, 4) #=> 16
16
+ # align(10, 5) #=> 32
17
+ def align(num, bit)
18
+ n = 2**bit
19
+ return num if (num % n).zero?
20
+ (num + n) & ~(n - 1)
21
+ end
22
+
23
+ # Fetch the correct value from module +mod+.
24
+ #
25
+ # See {ELFTools::ELFFile#segment_by_type} for how to
26
+ # use this method.
27
+ # @param [Module] mod The module defined constant numbers.
28
+ # @param [Integer, Symbol, String] val
29
+ # Desired value.
30
+ # @return [Integer]
31
+ # Currently this method always return a value
32
+ # from {ELFTools::Constants}.
33
+ def to_constant(mod, val)
34
+ # Ignore the outest name.
35
+ module_name = mod.name.sub('ELFTools::', '')
36
+ # if val is an integer, check if exists in mod
37
+ if val.is_a?(Integer)
38
+ return val if mod.constants.any? { |c| mod.const_get(c) == val }
39
+ raise ArgumentError, "No constants in #{module_name} is #{val}"
40
+ end
41
+ val = val.to_s.upcase
42
+ prefix = module_name.split('::')[-1]
43
+ val = prefix + '_' + val unless val.start_with?(prefix)
44
+ val = val.to_sym
45
+ raise ArgumentError, "No constants in #{module_name} named \"#{val}\"" unless mod.const_defined?(val)
46
+ mod.const_get(val)
47
+ end
48
+ end
49
+ extend ClassMethods
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ module ELFTools
2
+ VERSION = '0.1.0'.freeze
3
+ end
data/lib/elftools.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'elftools/constants'
2
+ require 'elftools/elf_file'
3
+ require 'elftools/version'
4
+
5
+ # The ELF parsing tools!
6
+ # Main entry point is {ELFTools::ELFFile}, see it
7
+ # for more information.
8
+ module ELFTools
9
+ end
metadata ADDED
@@ -0,0 +1,153 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elftools
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - david942j
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-03-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bindata
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.47'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.47'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '12.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '12.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.13.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.13.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: codeclimate-test-reporter
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.6'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.6'
97
+ description: |2
98
+ A light weight ELF parser. elftools is designed to be a low-level ELF parser.
99
+ Inspired by https://github.com/eliben/pyelftools.
100
+ email:
101
+ - david942j@gmail.com
102
+ executables: []
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - README.md
107
+ - lib/elftools.rb
108
+ - lib/elftools/constants.rb
109
+ - lib/elftools/dynamic.rb
110
+ - lib/elftools/elf_file.rb
111
+ - lib/elftools/exceptions.rb
112
+ - lib/elftools/lazy_array.rb
113
+ - lib/elftools/note.rb
114
+ - lib/elftools/sections/dynamic_section.rb
115
+ - lib/elftools/sections/note_section.rb
116
+ - lib/elftools/sections/null_section.rb
117
+ - lib/elftools/sections/section.rb
118
+ - lib/elftools/sections/sections.rb
119
+ - lib/elftools/sections/str_tab_section.rb
120
+ - lib/elftools/sections/sym_tab_section.rb
121
+ - lib/elftools/segments/dynamic_segment.rb
122
+ - lib/elftools/segments/interp_segment.rb
123
+ - lib/elftools/segments/note_segment.rb
124
+ - lib/elftools/segments/segment.rb
125
+ - lib/elftools/segments/segments.rb
126
+ - lib/elftools/structures.rb
127
+ - lib/elftools/util.rb
128
+ - lib/elftools/version.rb
129
+ homepage: https://github.com/david942j/rbelftools
130
+ licenses:
131
+ - MIT
132
+ metadata: {}
133
+ post_install_message:
134
+ rdoc_options: []
135
+ require_paths:
136
+ - lib
137
+ required_ruby_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: 2.1.0
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubyforge_project:
149
+ rubygems_version: 2.6.10
150
+ signing_key:
151
+ specification_version: 4
152
+ summary: ELFTools - Pure ruby library for parsing ELF files
153
+ test_files: []