elftools 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -1
- data/lib/elftools/constants.rb +74 -43
- data/lib/elftools/dynamic.rb +67 -13
- data/lib/elftools/elf_file.rb +88 -29
- data/lib/elftools/note.rb +5 -5
- data/lib/elftools/sections/relocation_section.rb +108 -0
- data/lib/elftools/sections/section.rb +13 -3
- data/lib/elftools/sections/sections.rb +3 -1
- data/lib/elftools/sections/str_tab_section.rb +2 -10
- data/lib/elftools/sections/sym_tab_section.rb +6 -5
- data/lib/elftools/segments/segment.rb +13 -3
- data/lib/elftools/segments/segments.rb +1 -1
- data/lib/elftools/structs.rb +155 -0
- data/lib/elftools/util.rb +42 -0
- data/lib/elftools/version.rb +1 -1
- metadata +4 -3
- data/lib/elftools/structures.rb +0 -133
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2aa2517a78c13a631e9889e03e380b024c75585
|
4
|
+
data.tar.gz: 6d1de34194028eba0b787b148b648a518e6f0e1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e3b9b1c4f4e0c7cf38efd85ae0c5c250e875fb50bda990286fcf0dc69684970b6eae99f6b2881a0938ea5597da6439ae46fa694823dc3e69322e202b60f622b
|
7
|
+
data.tar.gz: fff51965be7467711d36af1db1374370654a7d8a771ca8f4dcc48e38fd3daff3fe928dc2dd034f3d7ded3ecadb4e8304bdc8feb996623fc052c6eeea03251742
|
data/README.md
CHANGED
@@ -98,6 +98,21 @@ elf.segment_by_type(:interp).interp_name
|
|
98
98
|
#=> "/lib64/ld-linux-x86-64.so.2"
|
99
99
|
```
|
100
100
|
|
101
|
+
## Relocations
|
102
|
+
```ruby
|
103
|
+
elf = ELFTools::ELFFile.new(File.open('spec/files/amd64.elf'))
|
104
|
+
# Use relocation to get plt names.
|
105
|
+
rela_section = elf.sections_by_type(:rela).last
|
106
|
+
rela_section.name
|
107
|
+
#=> ".rela.plt"
|
108
|
+
relocations = rela_section.relocations
|
109
|
+
relocations.map { |r| '%x' % r.header.r_info }
|
110
|
+
#=> ["100000007", "200000007", "300000007", "400000007", "500000007", "700000007"]
|
111
|
+
symtab = elf.section_at(rela_section.header.sh_link) # get the symbol table section
|
112
|
+
relocations.map { |r| symtab.symbol_at(r.symbol_index).name }
|
113
|
+
#=> ["puts", "__stack_chk_fail", "printf", "__libc_start_main", "fgets", "scanf"]
|
114
|
+
```
|
115
|
+
|
101
116
|
# Why rbelftools
|
102
117
|
|
103
118
|
1. Fully documented
|
@@ -117,7 +132,7 @@ elf.segment_by_type(:interp).interp_name
|
|
117
132
|
**rbelftools** is designed to be a library for furthur usage.
|
118
133
|
It will _not_ add any too trivial features.
|
119
134
|
For example, to check if NX disabled, you can use
|
120
|
-
|
135
|
+
`!elf.segment_by_type(:gnu_stack).executable?` but not `elf.nx?`
|
121
136
|
5. Section and segment parser
|
122
137
|
|
123
138
|
Providing common sections and segments parser. For example, .symtab, .shstrtab
|
data/lib/elftools/constants.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module ELFTools
|
2
2
|
# Define constants from elf.h.
|
3
|
-
# Mostly refer from https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h
|
3
|
+
# Mostly refer from https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h
|
4
|
+
# and binutils/elfcpp/elfcpp.h.
|
4
5
|
module Constants
|
5
6
|
# ELF magic header
|
6
7
|
ELFMAG = "\x7FELF".freeze
|
@@ -42,53 +43,61 @@ module ELFTools
|
|
42
43
|
PT_LOPROC = 0x70000000
|
43
44
|
PT_HIPROC = 0x7fffffff
|
44
45
|
PT_GNU_EH_FRAME = 0x6474e550
|
45
|
-
PT_GNU_STACK =
|
46
|
+
PT_GNU_STACK = 0x6474e551
|
47
|
+
PT_GNU_RELRO = 0x6474e552 # Read only after relocation
|
46
48
|
end
|
47
49
|
include PT
|
48
50
|
|
49
51
|
# Dynamic table types, records in +d_tag+.
|
50
52
|
module DT
|
51
|
-
DT_NULL
|
52
|
-
DT_NEEDED
|
53
|
-
DT_PLTRELSZ
|
54
|
-
DT_PLTGOT
|
55
|
-
DT_HASH
|
56
|
-
DT_STRTAB
|
57
|
-
DT_SYMTAB
|
58
|
-
DT_RELA
|
59
|
-
DT_RELASZ
|
60
|
-
DT_RELAENT
|
61
|
-
DT_STRSZ
|
62
|
-
DT_SYMENT
|
63
|
-
DT_INIT
|
64
|
-
DT_FINI
|
65
|
-
DT_SONAME
|
66
|
-
DT_RPATH
|
67
|
-
DT_SYMBOLIC
|
68
|
-
DT_REL
|
69
|
-
DT_RELSZ
|
70
|
-
DT_RELENT
|
71
|
-
DT_PLTREL
|
72
|
-
DT_DEBUG
|
73
|
-
DT_TEXTREL
|
74
|
-
DT_JMPREL
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
53
|
+
DT_NULL = 0
|
54
|
+
DT_NEEDED = 1
|
55
|
+
DT_PLTRELSZ = 2
|
56
|
+
DT_PLTGOT = 3
|
57
|
+
DT_HASH = 4
|
58
|
+
DT_STRTAB = 5
|
59
|
+
DT_SYMTAB = 6
|
60
|
+
DT_RELA = 7
|
61
|
+
DT_RELASZ = 8
|
62
|
+
DT_RELAENT = 9
|
63
|
+
DT_STRSZ = 10
|
64
|
+
DT_SYMENT = 11
|
65
|
+
DT_INIT = 12
|
66
|
+
DT_FINI = 13
|
67
|
+
DT_SONAME = 14
|
68
|
+
DT_RPATH = 15
|
69
|
+
DT_SYMBOLIC = 16
|
70
|
+
DT_REL = 17
|
71
|
+
DT_RELSZ = 18
|
72
|
+
DT_RELENT = 19
|
73
|
+
DT_PLTREL = 20
|
74
|
+
DT_DEBUG = 21
|
75
|
+
DT_TEXTREL = 22
|
76
|
+
DT_JMPREL = 23
|
77
|
+
DT_BIND_NOW = 24
|
78
|
+
DT_INIT_ARRAY = 25
|
79
|
+
DT_FINI_ARRAY = 26
|
80
|
+
DT_INIT_ARRAYSZ = 27
|
81
|
+
DT_FINI_ARRAYSZ = 28
|
82
|
+
DT_RUNPATH = 29
|
83
|
+
DT_FLAGS = 30
|
84
|
+
DT_ENCODING = 32
|
85
|
+
DT_LOOS = 0x6000000d
|
86
|
+
DT_HIOS = 0x6ffff000
|
87
|
+
DT_VALRNGLO = 0x6ffffd00
|
88
|
+
DT_VALRNGHI = 0x6ffffdff
|
89
|
+
DT_ADDRRNGLO = 0x6ffffe00
|
90
|
+
DT_ADDRRNGHI = 0x6ffffeff
|
91
|
+
DT_VERSYM = 0x6ffffff0
|
92
|
+
DT_RELACOUNT = 0x6ffffff9
|
93
|
+
DT_RELCOUNT = 0x6ffffffa
|
94
|
+
DT_FLAGS_1 = 0x6ffffffb
|
95
|
+
DT_VERDEF = 0x6ffffffc
|
96
|
+
DT_VERDEFNUM = 0x6ffffffd
|
97
|
+
DT_VERNEED = 0x6ffffffe
|
98
|
+
DT_VERNEEDNUM = 0x6fffffff
|
99
|
+
DT_LOPROC = 0x70000000
|
100
|
+
DT_HIPROC = 0x7fffffff
|
92
101
|
end
|
93
102
|
include DT
|
94
103
|
|
@@ -178,5 +187,27 @@ module ELFTools
|
|
178
187
|
end
|
179
188
|
end
|
180
189
|
include EM
|
190
|
+
|
191
|
+
# This module defines elf file types.
|
192
|
+
module ET
|
193
|
+
ET_NONE = 0
|
194
|
+
ET_REL = 1
|
195
|
+
ET_EXEC = 2
|
196
|
+
ET_DYN = 3
|
197
|
+
ET_CORE = 4
|
198
|
+
# Return the type name according to +e_type+ in ELF file header.
|
199
|
+
# @return [String] Type in string format.
|
200
|
+
def self.mapping(type)
|
201
|
+
case type
|
202
|
+
when Constants::ET_NONE then 'NONE'
|
203
|
+
when Constants::ET_REL then 'REL'
|
204
|
+
when Constants::ET_EXEC then 'EXEC'
|
205
|
+
when Constants::ET_DYN then 'DYN'
|
206
|
+
when Constants::ET_CORE then 'CORE'
|
207
|
+
else '<unknown>'
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
include ET
|
181
212
|
end
|
182
213
|
end
|
data/lib/elftools/dynamic.rb
CHANGED
@@ -14,18 +14,27 @@ module ELFTools
|
|
14
14
|
# header
|
15
15
|
# tag_start
|
16
16
|
# @param [Block] block You can give a block.
|
17
|
-
# @return [Array<ELFTools::Dynamic::Tag>]
|
17
|
+
# @return [Enumerator<ELFTools::Dynamic::Tag>, Array<ELFTools::Dynamic::Tag>]
|
18
|
+
# If block is not given, an enumerator will be returned.
|
19
|
+
# Otherwise, return array of tags.
|
18
20
|
def each_tags
|
21
|
+
return enum_for(:each_tags) unless block_given?
|
19
22
|
arr = []
|
20
23
|
0.step do |i|
|
21
24
|
tag = tag_at(i)
|
22
|
-
yield tag
|
25
|
+
yield tag
|
23
26
|
arr << tag
|
24
27
|
break if tag.header.d_tag == ELFTools::Constants::DT_NULL
|
25
28
|
end
|
26
29
|
arr
|
27
30
|
end
|
28
|
-
|
31
|
+
|
32
|
+
# Use {#tags} to get all tags.
|
33
|
+
# @return [Array<ELFTools::Dynamic::Tag>]
|
34
|
+
# Array of tags.
|
35
|
+
def tags
|
36
|
+
each_tags.to_a
|
37
|
+
end
|
29
38
|
|
30
39
|
# Get a tag of specific type.
|
31
40
|
# @param [Integer, Symbol, String] type
|
@@ -53,10 +62,7 @@ module ELFTools
|
|
53
62
|
# #=> #<ELFTools::Dynamic::Tag:0x0055d3d2d91b28 @header={:d_tag=>3, :d_val=>6295552}>
|
54
63
|
def tag_by_type(type)
|
55
64
|
type = Util.to_constant(Constants::DT, type)
|
56
|
-
each_tags
|
57
|
-
return tag if tag.header.d_tag == type
|
58
|
-
end
|
59
|
-
nil
|
65
|
+
each_tags.find { |tag| tag.header.d_tag == type }
|
60
66
|
end
|
61
67
|
|
62
68
|
# Get the +n+-th tag.
|
@@ -75,10 +81,10 @@ module ELFTools
|
|
75
81
|
return if n < 0
|
76
82
|
@tag_at_map ||= {}
|
77
83
|
return @tag_at_map[n] if @tag_at_map[n]
|
78
|
-
dyn = ELF_Dyn.new(endian: endian)
|
84
|
+
dyn = Structs::ELF_Dyn.new(endian: endian)
|
79
85
|
dyn.elf_class = header.elf_class
|
80
86
|
stream.pos = tag_start + n * dyn.num_bytes
|
81
|
-
@tag_at_map[n] = Tag.new(dyn.read(stream), stream)
|
87
|
+
@tag_at_map[n] = Tag.new(dyn.read(stream), stream, method(:str_offset))
|
82
88
|
end
|
83
89
|
|
84
90
|
private
|
@@ -87,20 +93,68 @@ module ELFTools
|
|
87
93
|
header.class.self_endian
|
88
94
|
end
|
89
95
|
|
96
|
+
# Get the DT_STRTAB's +d_val+ offset related to file.
|
97
|
+
def str_offset
|
98
|
+
# TODO: handle DT_STRTAB not exitsts.
|
99
|
+
@str_offset ||= @offset_from_vma.call(tag_by_type(:strtab).header.d_val.to_i)
|
100
|
+
end
|
101
|
+
|
90
102
|
# A tag class.
|
91
103
|
class Tag
|
92
|
-
attr_reader :header # @return [ELFTools::ELF_Dyn] The dynamic tag header.
|
104
|
+
attr_reader :header # @return [ELFTools::Structs::ELF_Dyn] The dynamic tag header.
|
93
105
|
attr_reader :stream # @return [File] Streaming object.
|
94
106
|
|
95
107
|
# Instantiate a {ELFTools::Dynamic::Tag} object.
|
96
108
|
# @param [ELF_Dyn] header The dynamic tag header.
|
97
109
|
# @param [File] stream Streaming object.
|
98
|
-
|
110
|
+
# @param [Method] str_offset
|
111
|
+
# Call this method to get the string offset related
|
112
|
+
# to file.
|
113
|
+
def initialize(header, stream, str_offset)
|
99
114
|
@header = header
|
100
115
|
@stream = stream
|
116
|
+
@str_offset = str_offset
|
117
|
+
end
|
118
|
+
|
119
|
+
TYPE_WITH_NAME = [Constants::DT_NEEDED,
|
120
|
+
Constants::DT_SONAME,
|
121
|
+
Constants::DT_RPATH,
|
122
|
+
Constants::DT_RUNPATH].freeze
|
123
|
+
# Return the content of this tag records.
|
124
|
+
#
|
125
|
+
# For normal tags, this method just return
|
126
|
+
# +header.d_val+. For tags with +header.d_val+
|
127
|
+
# in meaning of string offset (e.g. DT_NEEDED), this method would
|
128
|
+
# return the string it specified.
|
129
|
+
# Tags with type in {TYPE_WITH_NAME} are those tags with name.
|
130
|
+
# @return [Integer, String] The content this tag records.
|
131
|
+
# @example
|
132
|
+
# dynamic = elf.segment_by_type(:dynamic)
|
133
|
+
# dynamic.tag_by_type(:init).value
|
134
|
+
# #=> 4195600 # 0x400510
|
135
|
+
# dynamic.tag_by_type(:needed).value
|
136
|
+
# #=> 'libc.so.6'
|
137
|
+
def value
|
138
|
+
name || header.d_val.to_i
|
139
|
+
end
|
140
|
+
|
141
|
+
# Is this tag has a name?
|
142
|
+
#
|
143
|
+
# The criteria here is if this tag's type is in {TYPE_WITH_NAME}.
|
144
|
+
# @return [Boolean] Is this tag has a name.
|
145
|
+
def name?
|
146
|
+
TYPE_WITH_NAME.include?(header.d_tag)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Return the name of this tag.
|
150
|
+
#
|
151
|
+
# Only tags with name would return a name.
|
152
|
+
# Others would return +nil+.
|
153
|
+
# @return [String, NilClass] The name.
|
154
|
+
def name
|
155
|
+
return nil unless name?
|
156
|
+
Util.cstring(stream, @str_offset.call + header.d_val.to_i)
|
101
157
|
end
|
102
|
-
# TODO: Get the name of tags, e.g. SONAME
|
103
|
-
# TODO: Handle (non)-PIE ELF correctly.
|
104
158
|
end
|
105
159
|
end
|
106
160
|
end
|
data/lib/elftools/elf_file.rb
CHANGED
@@ -3,7 +3,7 @@ require 'elftools/exceptions'
|
|
3
3
|
require 'elftools/lazy_array'
|
4
4
|
require 'elftools/sections/sections'
|
5
5
|
require 'elftools/segments/segments'
|
6
|
-
require 'elftools/
|
6
|
+
require 'elftools/structs'
|
7
7
|
|
8
8
|
module ELFTools
|
9
9
|
# The main class for using elftools.
|
@@ -27,11 +27,11 @@ module ELFTools
|
|
27
27
|
# Return the file header.
|
28
28
|
#
|
29
29
|
# Lazy loading.
|
30
|
-
# @retrn [ELFTools::ELF_Ehdr] The header.
|
30
|
+
# @retrn [ELFTools::Structs::ELF_Ehdr] The header.
|
31
31
|
def header
|
32
|
-
return @header if @header
|
32
|
+
return @header if defined?(@header)
|
33
33
|
stream.pos = 0
|
34
|
-
@header = ELF_Ehdr.new(endian: endian)
|
34
|
+
@header = Structs::ELF_Ehdr.new(endian: endian)
|
35
35
|
@header.elf_class = elf_class
|
36
36
|
@header.read(stream)
|
37
37
|
end
|
@@ -65,6 +65,17 @@ module ELFTools
|
|
65
65
|
ELFTools::Constants::EM.mapping(header.e_machine)
|
66
66
|
end
|
67
67
|
|
68
|
+
# Return the ELF type according to +e_type+.
|
69
|
+
# @return [String] Type in string format.
|
70
|
+
# @example
|
71
|
+
# ELFFile.new(File.open('spec/files/libc.so.6')).elf_type
|
72
|
+
# #=> 'DYN'
|
73
|
+
# ELFFile.new(File.open('spec/files/amd64.elf')).elf_type
|
74
|
+
# #=> 'EXEC'
|
75
|
+
def elf_type
|
76
|
+
ELFTools::Constants::ET.mapping(header.e_type)
|
77
|
+
end
|
78
|
+
|
68
79
|
#========= method about sections
|
69
80
|
|
70
81
|
# Number of sections in this file.
|
@@ -87,13 +98,7 @@ module ELFTools
|
|
87
98
|
# elf.section_by_name('no such section')
|
88
99
|
# #=> nil
|
89
100
|
def section_by_name(name)
|
90
|
-
|
91
|
-
return @section_name_map[name] if @section_name_map[name]
|
92
|
-
each_sections do |section|
|
93
|
-
@section_name_map[section.name] = section
|
94
|
-
return section if section.name == name
|
95
|
-
end
|
96
|
-
nil
|
101
|
+
each_sections.find { |sec| sec.name == name }
|
97
102
|
end
|
98
103
|
|
99
104
|
# Iterate all sections.
|
@@ -104,17 +109,24 @@ module ELFTools
|
|
104
109
|
# since not all sections need to be created.
|
105
110
|
# @param [Block] block
|
106
111
|
# Just like +Array#each+, you can give a block.
|
107
|
-
# @return [ Array<ELFTools::Sections::Section>]
|
108
|
-
#
|
112
|
+
# @return [Enumerator<ELFTools::Sections::Section>, Array<ELFTools::Sections::Section>]
|
113
|
+
# As +Array#each+, if block is not given, a enumerator will be returned,
|
114
|
+
# otherwise, the whole sections will be returned.
|
109
115
|
def each_sections
|
116
|
+
return enum_for(:each_sections) unless block_given?
|
110
117
|
Array.new(num_sections) do |i|
|
111
118
|
sec = section_at(i)
|
112
|
-
|
119
|
+
yield sec
|
120
|
+
sec
|
113
121
|
end
|
114
122
|
end
|
115
123
|
|
116
|
-
# Simply use {#sections}
|
117
|
-
|
124
|
+
# Simply use {#sections} to get all sections.
|
125
|
+
# @return [Array<ELFTools::Sections::Section>]
|
126
|
+
# Whole sections.
|
127
|
+
def sections
|
128
|
+
each_sections.to_a
|
129
|
+
end
|
118
130
|
|
119
131
|
# Acquire the +n+-th section, 0-based.
|
120
132
|
#
|
@@ -128,6 +140,26 @@ module ELFTools
|
|
128
140
|
@sections[n]
|
129
141
|
end
|
130
142
|
|
143
|
+
# Fetch all sections with specific type.
|
144
|
+
#
|
145
|
+
# The available types are listed in {ELFTools::Constants},
|
146
|
+
# start with +SHT_+.
|
147
|
+
# This method accept giving block.
|
148
|
+
# @param [Integer, Symbol, String] type
|
149
|
+
# The type needed, similar format as {#segment_by_type}.
|
150
|
+
# @param [Block] block
|
151
|
+
# Block will be yielded whenever find a section.
|
152
|
+
# @return [Array<ELFTools::Sections::section>] The target sections.
|
153
|
+
# @example
|
154
|
+
# elf = ELFTools::ELFFile.new(File.open('spec/files/amd64.elf'))
|
155
|
+
# elf.sections_by_type(:rela)
|
156
|
+
# #=> [#<ELFTools::Sections::RelocationSection:0x00563cd3219970>,
|
157
|
+
# # #<ELFTools::Sections::RelocationSection:0x00563cd3b89d70>]
|
158
|
+
def sections_by_type(type, &block)
|
159
|
+
type = Util.to_constant(Constants::SHT, type)
|
160
|
+
Util.select_by_type(each_sections, type, &block)
|
161
|
+
end
|
162
|
+
|
131
163
|
# Get the string table section.
|
132
164
|
#
|
133
165
|
# This section is acquired by using the +e_shstrndx+
|
@@ -156,18 +188,24 @@ module ELFTools
|
|
156
188
|
# @return [Array<ELFTools::Segments::Segment>]
|
157
189
|
# Whole segments will be returned.
|
158
190
|
def each_segments
|
191
|
+
return enum_for(:each_segments) unless block_given?
|
159
192
|
Array.new(num_segments) do |i|
|
160
193
|
seg = segment_at(i)
|
161
|
-
|
194
|
+
yield seg
|
195
|
+
seg
|
162
196
|
end
|
163
197
|
end
|
164
198
|
|
165
|
-
# Simply use {#segments}
|
166
|
-
|
199
|
+
# Simply use {#segments} to get all segments.
|
200
|
+
# @return [Array<ELFTools::Segments::Segment>]
|
201
|
+
# Whole segments.
|
202
|
+
def segments
|
203
|
+
each_segments.to_a
|
204
|
+
end
|
167
205
|
|
168
206
|
# Get the first segment with +p_type=type+.
|
169
207
|
# The available types are listed in {ELFTools::Constants},
|
170
|
-
#
|
208
|
+
# start with +PT_+.
|
171
209
|
#
|
172
210
|
# Notice: this method will return the first segment found,
|
173
211
|
# to found all segments with specific type you can use {#segments_by_type}.
|
@@ -208,21 +246,22 @@ module ELFTools
|
|
208
246
|
# #=> nil # no such segment exists
|
209
247
|
def segment_by_type(type)
|
210
248
|
type = Util.to_constant(Constants::PT, type)
|
211
|
-
each_segments
|
212
|
-
return seg if seg.header.p_type == type
|
213
|
-
end
|
214
|
-
nil
|
249
|
+
each_segments.find { |seg| seg.header.p_type == type }
|
215
250
|
end
|
216
251
|
|
217
252
|
# Fetch all segments with specific type.
|
253
|
+
#
|
218
254
|
# If you want to find only one segment,
|
219
255
|
# use {#segment_by_type} instead.
|
256
|
+
# This method accept giving block.
|
220
257
|
# @param [Integer, Symbol, String] type
|
221
258
|
# The type needed, same format as {#segment_by_type}.
|
259
|
+
# @param [Block] block
|
260
|
+
# Block will be yielded whenever find a segement.
|
222
261
|
# @return [Array<ELFTools::Segments::Segment>] The target segments.
|
223
|
-
def segments_by_type(type)
|
262
|
+
def segments_by_type(type, &block)
|
224
263
|
type = Util.to_constant(Constants::PT, type)
|
225
|
-
|
264
|
+
Util.select_by_type(each_segments, type, &block)
|
226
265
|
end
|
227
266
|
|
228
267
|
# Acquire the +n+-th segment, 0-based.
|
@@ -237,6 +276,25 @@ module ELFTools
|
|
237
276
|
@segments[n]
|
238
277
|
end
|
239
278
|
|
279
|
+
# Get the offset related to file, given virtual memory address.
|
280
|
+
#
|
281
|
+
# This method should work no matter ELF is a PIE or not.
|
282
|
+
# This method refers from (actually equals to) binutils/readelf.c#offset_from_vma.
|
283
|
+
# @param [Integer] vma The address need query.
|
284
|
+
# @return [Integer] Offset related to file.
|
285
|
+
# @example
|
286
|
+
# elf = ELFTools::ELFFile.new(File.open('/bin/cat'))
|
287
|
+
# elf.offset_from_vma(0x401337)
|
288
|
+
# #=> 4919 # 0x1337
|
289
|
+
def offset_from_vma(vma, size = 0)
|
290
|
+
segments_by_type(:load) do |seg|
|
291
|
+
if vma >= (seg.header.p_vaddr & -seg.header.p_align) &&
|
292
|
+
vma + size <= seg.header.p_vaddr + seg.header.p_filesz
|
293
|
+
return vma - seg.header.p_vaddr + seg.header.p_offset
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
240
298
|
private
|
241
299
|
|
242
300
|
def identify
|
@@ -259,19 +317,20 @@ module ELFTools
|
|
259
317
|
|
260
318
|
def create_section(n)
|
261
319
|
stream.pos = header.e_shoff + n * header.e_shentsize
|
262
|
-
shdr = ELF_Shdr.new(endian: endian)
|
320
|
+
shdr = Structs::ELF_Shdr.new(endian: endian)
|
263
321
|
shdr.elf_class = elf_class
|
264
322
|
shdr.read(stream)
|
265
323
|
Sections::Section.create(shdr, stream,
|
324
|
+
offset_from_vma: method(:offset_from_vma),
|
266
325
|
strtab: method(:strtab_section),
|
267
326
|
section_at: method(:section_at))
|
268
327
|
end
|
269
328
|
|
270
329
|
def create_segment(n)
|
271
330
|
stream.pos = header.e_phoff + n * header.e_phentsize
|
272
|
-
phdr = ELF_Phdr[elf_class].new(endian: endian)
|
331
|
+
phdr = Structs::ELF_Phdr[elf_class].new(endian: endian)
|
273
332
|
phdr.elf_class = elf_class
|
274
|
-
Segments::Segment.create(phdr.read(stream), stream)
|
333
|
+
Segments::Segment.create(phdr.read(stream), stream, offset_from_vma: method(:offset_from_vma))
|
275
334
|
end
|
276
335
|
end
|
277
336
|
end
|
data/lib/elftools/note.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'elftools/
|
1
|
+
require 'elftools/structs'
|
2
2
|
require 'elftools/util'
|
3
3
|
|
4
4
|
module ELFTools
|
@@ -10,10 +10,10 @@ module ELFTools
|
|
10
10
|
# {ELFTools::Segments::NoteSegment} since some methods assume some attributes already
|
11
11
|
# exist.
|
12
12
|
module Note
|
13
|
-
# Since size of {ELFTools::ELF_Nhdr} will not change no
|
13
|
+
# Since size of {ELFTools::Structs::ELF_Nhdr} will not change no
|
14
14
|
# matter what endian and what arch, we can do this here.
|
15
15
|
# This value should equal to 12.
|
16
|
-
SIZE_OF_NHDR = ELF_Nhdr.new(endian: :little).num_bytes
|
16
|
+
SIZE_OF_NHDR = Structs::ELF_Nhdr.new(endian: :little).num_bytes
|
17
17
|
|
18
18
|
# Iterate all notes in a note section or segment.
|
19
19
|
#
|
@@ -68,13 +68,13 @@ module ELFTools
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def create_note(cur)
|
71
|
-
nhdr = ELF_Nhdr.new(endian: endian).read(stream)
|
71
|
+
nhdr = Structs::ELF_Nhdr.new(endian: endian).read(stream)
|
72
72
|
ELFTools::Note::Note.new(nhdr, stream, cur)
|
73
73
|
end
|
74
74
|
|
75
75
|
# Class of a note.
|
76
76
|
class Note
|
77
|
-
attr_reader :header # @return [ELFTools::ELF_Nhdr] Note header.
|
77
|
+
attr_reader :header # @return [ELFTools::Structs::ELF_Nhdr] Note header.
|
78
78
|
attr_reader :stream # @return [File] Streaming object.
|
79
79
|
attr_reader :offset # @return [Integer] Address of this note start, includes note header.
|
80
80
|
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'elftools/constants'
|
2
|
+
require 'elftools/sections/section'
|
3
|
+
require 'elftools/structs'
|
4
|
+
|
5
|
+
module ELFTools
|
6
|
+
module Sections
|
7
|
+
# Class of note section.
|
8
|
+
# Note section records notes
|
9
|
+
class RelocationSection < Section
|
10
|
+
# Is this relocation a RELA or REL type.
|
11
|
+
# @return [Boolean] If is RELA.
|
12
|
+
def rela?
|
13
|
+
header.sh_type == Constants::SHT_RELA
|
14
|
+
end
|
15
|
+
|
16
|
+
# Number of relocations in this section.
|
17
|
+
# @return [Integer] The number.
|
18
|
+
def num_relocations
|
19
|
+
header.sh_size / header.sh_entsize
|
20
|
+
end
|
21
|
+
|
22
|
+
# Acquire the +n+-th relocation, 0-based.
|
23
|
+
#
|
24
|
+
# relocations are lazy loaded.
|
25
|
+
# @param [Integer] n The index.
|
26
|
+
# @return [ELFTools::Relocation, NilClass]
|
27
|
+
# The target relocation.
|
28
|
+
# If +n+ is out of bound, +nil+ is returned.
|
29
|
+
def relocation_at(n)
|
30
|
+
@relocations ||= LazyArray.new(num_relocations, &method(:create_relocation))
|
31
|
+
@relocations[n]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Iterate all relocations.
|
35
|
+
#
|
36
|
+
# All relocations are lazy loading, the relocation
|
37
|
+
# only be created whenever accessing it.
|
38
|
+
# @param [Block] block
|
39
|
+
# Just like +Array#each+, you can give a block.
|
40
|
+
# @return [Enumerator<ELFTools::Relocation>, Array<ELFTools::Relocation>]
|
41
|
+
# If block is not given, an enumerator will be returned.
|
42
|
+
# Otherwise, the whole relocations will be returned.
|
43
|
+
def each_relocations
|
44
|
+
return enum_for(:each_relocations) unless block_given?
|
45
|
+
Array.new(num_relocations) do |i|
|
46
|
+
rel = relocation_at(i)
|
47
|
+
yield rel
|
48
|
+
rel
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Simply use {#relocations} to get all relocations.
|
53
|
+
# @return [Array<ELFTools::Relocation>]
|
54
|
+
# Whole relocations.
|
55
|
+
def relocations
|
56
|
+
each_relocations.to_a
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def create_relocation(n)
|
62
|
+
stream.pos = header.sh_offset + n * header.sh_entsize
|
63
|
+
klass = rela? ? Structs::ELF_Rela : Structs::ELF_Rel
|
64
|
+
rel = klass.new(endian: header.class.self_endian)
|
65
|
+
rel.elf_class = header.elf_class
|
66
|
+
rel.read(stream)
|
67
|
+
Relocation.new(rel, stream)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# A relocation entry.
|
73
|
+
#
|
74
|
+
# Can be either a REL or RELA relocation.
|
75
|
+
# XXX: move this to an independent file?
|
76
|
+
class Relocation
|
77
|
+
attr_reader :header # @return [ELFTools::Structs::ELF_Rel, ELFTools::Structs::ELF_Rela] Rel(a) header.
|
78
|
+
attr_reader :stream # @return [File] Streaming object.
|
79
|
+
|
80
|
+
# Instantiate a {Relocation} object.
|
81
|
+
def initialize(header, stream)
|
82
|
+
@header = header
|
83
|
+
@stream = stream
|
84
|
+
end
|
85
|
+
|
86
|
+
# +r_info+ contains sym and type, use two methods
|
87
|
+
# to access them easier.
|
88
|
+
# @return [Integer] sym infor.
|
89
|
+
def r_info_sym
|
90
|
+
header.r_info >> mask_bit
|
91
|
+
end
|
92
|
+
alias symbol_index r_info_sym
|
93
|
+
|
94
|
+
# +r_info+ contains sym and type, use two methods
|
95
|
+
# to access them easier.
|
96
|
+
# @return [Integer] type infor.
|
97
|
+
def r_info_type
|
98
|
+
header.r_info & ((1 << mask_bit) - 1)
|
99
|
+
end
|
100
|
+
alias type r_info_type
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def mask_bit
|
105
|
+
header.elf_class == 32 ? 8 : 32
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -3,11 +3,11 @@ module ELFTools
|
|
3
3
|
module Sections
|
4
4
|
# Base class of sections.
|
5
5
|
class Section
|
6
|
-
attr_reader :header # @return [ELFTools::ELF_Shdr] Section header.
|
6
|
+
attr_reader :header # @return [ELFTools::Structs::ELF_Shdr] Section header.
|
7
7
|
attr_reader :stream # @return [File] Streaming object.
|
8
8
|
|
9
9
|
# Instantiate a {Section} object.
|
10
|
-
# @param [ELFTools::ELF_Shdr] header
|
10
|
+
# @param [ELFTools::Structs::ELF_Shdr] header
|
11
11
|
# The section header object.
|
12
12
|
# @param [File] stream
|
13
13
|
# The streaming object for further dump.
|
@@ -15,10 +15,20 @@ module ELFTools
|
|
15
15
|
# The string table object. For fetching section names.
|
16
16
|
# If +Proc+ if given, it will call at the first
|
17
17
|
# time access +#name+.
|
18
|
-
|
18
|
+
# @param [Method] offset_from_vma
|
19
|
+
# The method to get offset of file, given virtual memory address.
|
20
|
+
def initialize(header, stream, offset_from_vma: nil, strtab: nil, **_kwargs)
|
19
21
|
@header = header
|
20
22
|
@stream = stream
|
21
23
|
@strtab = strtab
|
24
|
+
@offset_from_vma = offset_from_vma
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return +header.sh_type+ in a simplier way.
|
28
|
+
# @return [Integer]
|
29
|
+
# The type, meaning of types are defined in {Constants::SHT}.
|
30
|
+
def type
|
31
|
+
header.sh_type
|
22
32
|
end
|
23
33
|
|
24
34
|
# Get name of this section.
|
@@ -5,6 +5,7 @@ require 'elftools/sections/section'
|
|
5
5
|
require 'elftools/sections/dynamic_section'
|
6
6
|
require 'elftools/sections/note_section'
|
7
7
|
require 'elftools/sections/null_section'
|
8
|
+
require 'elftools/sections/relocation_section'
|
8
9
|
require 'elftools/sections/str_tab_section'
|
9
10
|
require 'elftools/sections/sym_tab_section'
|
10
11
|
|
@@ -14,7 +15,7 @@ module ELFTools
|
|
14
15
|
# Class methods of {Sections::Section}.
|
15
16
|
class << Section
|
16
17
|
# Use different class according to +header.sh_type+.
|
17
|
-
# @param [ELFTools::ELF_Shdr] header Section header.
|
18
|
+
# @param [ELFTools::Structs::ELF_Shdr] header Section header.
|
18
19
|
# @param [File] stream Streaming object.
|
19
20
|
# @return [ELFTools::Sections::Section]
|
20
21
|
# Return object dependes on +header.sh_type+.
|
@@ -23,6 +24,7 @@ module ELFTools
|
|
23
24
|
when Constants::SHT_DYNAMIC then DynamicSection
|
24
25
|
when Constants::SHT_NULL then NullSection
|
25
26
|
when Constants::SHT_NOTE then NoteSection
|
27
|
+
when Constants::SHT_RELA, Constants::SHT_REL then RelocationSection
|
26
28
|
when Constants::SHT_STRTAB then StrTabSection
|
27
29
|
when Constants::SHT_SYMTAB, Constants::SHT_DYNSYM then SymTabSection
|
28
30
|
else Section
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'elftools/sections/section'
|
2
|
+
require 'elftools/util'
|
2
3
|
|
3
4
|
module ELFTools
|
4
5
|
module Sections
|
@@ -11,16 +12,7 @@ module ELFTools
|
|
11
12
|
# Usually from +shdr.sh_name+ or +sym.st_name+.
|
12
13
|
# @return [String] The name without null bytes.
|
13
14
|
def name_at(offset)
|
14
|
-
stream
|
15
|
-
# read until "\x00"
|
16
|
-
ret = ''
|
17
|
-
loop do
|
18
|
-
c = stream.read(1)
|
19
|
-
return nil if c.nil? # reach EOF
|
20
|
-
break if c == "\x00"
|
21
|
-
ret += c
|
22
|
-
end
|
23
|
-
ret
|
15
|
+
Util.cstring(stream, header.sh_offset + offset)
|
24
16
|
end
|
25
17
|
end
|
26
18
|
end
|
@@ -9,7 +9,7 @@ module ELFTools
|
|
9
9
|
# Instantiate a {SymTabSection} object.
|
10
10
|
# There's a +section_at+ lambda for {SymTabSection}
|
11
11
|
# to easily fetch other sections.
|
12
|
-
# @param [ELFTools::ELF_Shdr] header
|
12
|
+
# @param [ELFTools::Structs::ELF_Shdr] header
|
13
13
|
# See {Section#initialize} for more information.
|
14
14
|
# @param [File] stream
|
15
15
|
# See {Section#initialize} for more information.
|
@@ -36,7 +36,7 @@ module ELFTools
|
|
36
36
|
#
|
37
37
|
# Symbols are lazy loaded.
|
38
38
|
# @param [Integer] n The index.
|
39
|
-
# @return [ELFTools
|
39
|
+
# @return [ELFTools::Symbol, NilClass]
|
40
40
|
# The target symbol.
|
41
41
|
# If +n+ is out of bound, +nil+ is returned.
|
42
42
|
def symbol_at(n)
|
@@ -87,20 +87,21 @@ module ELFTools
|
|
87
87
|
|
88
88
|
def create_symbol(n)
|
89
89
|
stream.pos = header.sh_offset + n * header.sh_entsize
|
90
|
-
sym = ELF_sym[header.elf_class].new(endian: header.class.self_endian)
|
90
|
+
sym = Structs::ELF_sym[header.elf_class].new(endian: header.class.self_endian)
|
91
91
|
sym.read(stream)
|
92
92
|
Symbol.new(sym, stream, symstr: method(:symstr))
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
96
|
# Class of symbol.
|
97
|
+
#
|
97
98
|
# XXX: Should this class be defined in an independent file?
|
98
99
|
class Symbol
|
99
|
-
attr_reader :header # @return [ELFTools::ELF32_sym, ELFTools::ELF64_sym] Section header.
|
100
|
+
attr_reader :header # @return [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] Section header.
|
100
101
|
attr_reader :stream # @return [File] Streaming object.
|
101
102
|
|
102
103
|
# Instantiate a {ELFTools::Symbol} object.
|
103
|
-
# @param [ELFTools::ELF32_sym, ELFTools::ELF64_sym] header
|
104
|
+
# @param [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] header
|
104
105
|
# The symbol header.
|
105
106
|
# @param [File] stream The streaming object.
|
106
107
|
# @param [ELFTools::Sections::StrTabSection, Proc] symstr
|
@@ -2,17 +2,27 @@ module ELFTools
|
|
2
2
|
module Segments
|
3
3
|
# Base class of segments.
|
4
4
|
class Segment
|
5
|
-
attr_reader :header # @return [ELFTools::ELF32_Phdr, ELFTools::ELF64_Phdr] Program header.
|
5
|
+
attr_reader :header # @return [ELFTools::Structs::ELF32_Phdr, ELFTools::Structs::ELF64_Phdr] Program header.
|
6
6
|
attr_reader :stream # @return [File] Streaming object.
|
7
7
|
|
8
8
|
# Instantiate a {Segment} object.
|
9
|
-
# @param [ELFTools::ELF32_Phdr, ELFTools::ELF64_Phdr] header
|
9
|
+
# @param [ELFTools::Structs::ELF32_Phdr, ELFTools::Structs::ELF64_Phdr] header
|
10
10
|
# Program header.
|
11
11
|
# @param [File] stream
|
12
12
|
# Streaming object.
|
13
|
-
|
13
|
+
# @param [Method] offset_from_vma
|
14
|
+
# The method to get offset of file, given virtual memory address.
|
15
|
+
def initialize(header, stream, offset_from_vma: nil)
|
14
16
|
@header = header
|
15
17
|
@stream = stream
|
18
|
+
@offset_from_vma = offset_from_vma
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return +header.p_type+ in a simplier way.
|
22
|
+
# @return [Integer]
|
23
|
+
# The type, meaning of types are defined in {Constants::PT}.
|
24
|
+
def type
|
25
|
+
header.p_type
|
16
26
|
end
|
17
27
|
|
18
28
|
# The content in this segment.
|
@@ -12,7 +12,7 @@ module ELFTools
|
|
12
12
|
# Class methods of {Segments::Segment}.
|
13
13
|
class << Segment
|
14
14
|
# Use different class according to +header.p_type+.
|
15
|
-
# @param [ELFTools::ELF32_Phdr, ELFTools::ELF64_Phdr] header Program header of a segment.
|
15
|
+
# @param [ELFTools::Structs::ELF32_Phdr, ELFTools::Structs::ELF64_Phdr] header Program header of a segment.
|
16
16
|
# @param [File] stream Streaming object.
|
17
17
|
# @return [ELFTools::Segments::Segment]
|
18
18
|
# Return object dependes on +header.p_type+.
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
module ELFTools
|
3
|
+
# Define ELF related structures in this module.
|
4
|
+
#
|
5
|
+
# Structures are fetch from https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h.
|
6
|
+
# Using the bindata gem to make these structures support 32/64 bits and
|
7
|
+
# little/big endian simultaneously.
|
8
|
+
module Structs
|
9
|
+
# The base structure to define common methods.
|
10
|
+
class ELFStruct < BinData::Record
|
11
|
+
CHOICE_SIZE_T = {
|
12
|
+
selection: :elf_class, choices: { 32 => :uint32, 64 => :uint64 }
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
attr_accessor :elf_class # @return [Integer] 32 or 64.
|
16
|
+
|
17
|
+
# Hacking to get endian of current class
|
18
|
+
# @return [Symbol, NilClass] +:little+ or +:big+.
|
19
|
+
def self.self_endian
|
20
|
+
bindata_name[-2..-1] == 'ge' ? :big : :little
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# ELF header structure.
|
25
|
+
class ELF_Ehdr < ELFStruct
|
26
|
+
endian :big_and_little
|
27
|
+
struct :e_ident do
|
28
|
+
string :magic, read_length: 4
|
29
|
+
int8 :ei_class
|
30
|
+
int8 :ei_data
|
31
|
+
int8 :ei_version
|
32
|
+
int8 :ei_osabi
|
33
|
+
int8 :ei_abiversion
|
34
|
+
string :ei_padding, read_length: 7 # no use
|
35
|
+
end
|
36
|
+
uint16 :e_type
|
37
|
+
uint16 :e_machine
|
38
|
+
uint32 :e_version
|
39
|
+
# entry point
|
40
|
+
choice :e_entry, **CHOICE_SIZE_T
|
41
|
+
choice :e_phoff, **CHOICE_SIZE_T
|
42
|
+
choice :e_shoff, **CHOICE_SIZE_T
|
43
|
+
uint32 :e_flags
|
44
|
+
uint16 :e_ehsize # size of this header
|
45
|
+
uint16 :e_phentsize # size of each segment
|
46
|
+
uint16 :e_phnum # number of segments
|
47
|
+
uint16 :e_shentsize # size of each section
|
48
|
+
uint16 :e_shnum # number of sections
|
49
|
+
uint16 :e_shstrndx # index of string table section
|
50
|
+
end
|
51
|
+
|
52
|
+
# Section header structure.
|
53
|
+
class ELF_Shdr < ELFStruct
|
54
|
+
endian :big_and_little
|
55
|
+
uint32 :sh_name
|
56
|
+
uint32 :sh_type
|
57
|
+
choice :sh_flags, **CHOICE_SIZE_T
|
58
|
+
choice :sh_addr, **CHOICE_SIZE_T
|
59
|
+
choice :sh_offset, **CHOICE_SIZE_T
|
60
|
+
choice :sh_size, **CHOICE_SIZE_T
|
61
|
+
uint32 :sh_link
|
62
|
+
uint32 :sh_info
|
63
|
+
choice :sh_addralign, **CHOICE_SIZE_T
|
64
|
+
choice :sh_entsize, **CHOICE_SIZE_T
|
65
|
+
end
|
66
|
+
|
67
|
+
# Program header structure for 32bit.
|
68
|
+
class ELF32_Phdr < ELFStruct
|
69
|
+
endian :big_and_little
|
70
|
+
uint32 :p_type
|
71
|
+
uint32 :p_offset
|
72
|
+
uint32 :p_vaddr
|
73
|
+
uint32 :p_paddr
|
74
|
+
uint32 :p_filesz
|
75
|
+
uint32 :p_memsz
|
76
|
+
uint32 :p_flags
|
77
|
+
uint32 :p_align
|
78
|
+
end
|
79
|
+
|
80
|
+
# Program header structure for 64bit.
|
81
|
+
class ELF64_Phdr < ELFStruct
|
82
|
+
endian :big_and_little
|
83
|
+
uint32 :p_type
|
84
|
+
uint32 :p_flags
|
85
|
+
uint64 :p_offset
|
86
|
+
uint64 :p_vaddr
|
87
|
+
uint64 :p_paddr
|
88
|
+
uint64 :p_filesz
|
89
|
+
uint64 :p_memsz
|
90
|
+
uint64 :p_align
|
91
|
+
end
|
92
|
+
ELF_Phdr = {
|
93
|
+
32 => ELF32_Phdr,
|
94
|
+
64 => ELF64_Phdr
|
95
|
+
}.freeze
|
96
|
+
|
97
|
+
# Symbol structure for 32bit.
|
98
|
+
class ELF32_sym < ELFStruct
|
99
|
+
endian :big_and_little
|
100
|
+
uint32 :st_name
|
101
|
+
uint32 :st_value
|
102
|
+
uint32 :st_size
|
103
|
+
uint8 :st_info
|
104
|
+
uint8 :st_other
|
105
|
+
uint16 :st_shndx
|
106
|
+
end
|
107
|
+
|
108
|
+
# Symbol structure for 64bit.
|
109
|
+
class ELF64_sym < ELFStruct
|
110
|
+
endian :big_and_little
|
111
|
+
uint32 :st_name # Symbol name, index in string tbl
|
112
|
+
uint8 :st_info # Type and binding attributes
|
113
|
+
uint8 :st_other # No defined meaning, 0
|
114
|
+
uint16 :st_shndx # Associated section index
|
115
|
+
uint64 :st_value # Value of the symbol
|
116
|
+
uint64 :st_size # Associated symbol size
|
117
|
+
end
|
118
|
+
ELF_sym = {
|
119
|
+
32 => ELF32_sym,
|
120
|
+
64 => ELF64_sym
|
121
|
+
}.freeze
|
122
|
+
|
123
|
+
# Note header.
|
124
|
+
class ELF_Nhdr < ELFStruct
|
125
|
+
endian :big_and_little
|
126
|
+
uint32 :n_namesz # Name size
|
127
|
+
uint32 :n_descsz # Content size
|
128
|
+
uint32 :n_type # Content type
|
129
|
+
end
|
130
|
+
|
131
|
+
# Dynamic tag header.
|
132
|
+
class ELF_Dyn < ELFStruct
|
133
|
+
endian :big_and_little
|
134
|
+
choice :d_tag, selection: :elf_class, choices: { 32 => :int32, 64 => :int64 }
|
135
|
+
# This is an union type named +d_un+ in original source,
|
136
|
+
# simplify it to be +d_val+ here.
|
137
|
+
choice :d_val, **CHOICE_SIZE_T
|
138
|
+
end
|
139
|
+
|
140
|
+
# Rel header in .rel section.
|
141
|
+
class ELF_Rel < ELFStruct
|
142
|
+
endian :big_and_little
|
143
|
+
choice :r_offset, **CHOICE_SIZE_T
|
144
|
+
choice :r_info, **CHOICE_SIZE_T
|
145
|
+
end
|
146
|
+
|
147
|
+
# Rela header in .rela section.
|
148
|
+
class ELF_Rela < ELFStruct
|
149
|
+
endian :big_and_little
|
150
|
+
choice :r_offset, **CHOICE_SIZE_T
|
151
|
+
choice :r_info, **CHOICE_SIZE_T
|
152
|
+
choice :r_addend, selection: :elf_class, choices: { 32 => :int32, 64 => :int64 }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
data/lib/elftools/util.rb
CHANGED
@@ -45,6 +45,48 @@ module ELFTools
|
|
45
45
|
raise ArgumentError, "No constants in #{module_name} named \"#{val}\"" unless mod.const_defined?(val)
|
46
46
|
mod.const_get(val)
|
47
47
|
end
|
48
|
+
|
49
|
+
# Read from stream until reach a null-byte.
|
50
|
+
# @param [File] stream Streaming object
|
51
|
+
# @param [Integer] offset Start from here.
|
52
|
+
# @return [String] Result string will never contain null byte.
|
53
|
+
# @example
|
54
|
+
# Util.cstring(File.open('/bin/cat'), 0)
|
55
|
+
# #=> "\x7FELF\x02\x01\x01"
|
56
|
+
def cstring(stream, offset)
|
57
|
+
stream.pos = offset
|
58
|
+
# read until "\x00"
|
59
|
+
ret = ''
|
60
|
+
loop do
|
61
|
+
c = stream.read(1)
|
62
|
+
return nil if c.nil? # reach EOF
|
63
|
+
break if c == "\x00"
|
64
|
+
ret += c
|
65
|
+
end
|
66
|
+
ret
|
67
|
+
end
|
68
|
+
|
69
|
+
# Select objects from enumerator with +.type+ property
|
70
|
+
# equals to +type+.
|
71
|
+
#
|
72
|
+
# Different from naive +Array#select+ is this method
|
73
|
+
# will yield block whenever find a desired object.
|
74
|
+
#
|
75
|
+
# This method is used to simplify the same logic in methods
|
76
|
+
# {ELFFile#sections_by_type}, {ELFFile#segments_by_type}, etc.
|
77
|
+
# @param [Enumerator] enum An enumerator for further select.
|
78
|
+
# @param [Object] type The type you want.
|
79
|
+
# @return [Array<Object>]
|
80
|
+
# The return value will be objects in +enum+ with attribute
|
81
|
+
# +.type+ equals to +type+.
|
82
|
+
def select_by_type(enum, type)
|
83
|
+
enum.select do |sec|
|
84
|
+
if sec.type == type
|
85
|
+
yield sec if block_given?
|
86
|
+
true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
48
90
|
end
|
49
91
|
extend ClassMethods
|
50
92
|
end
|
data/lib/elftools/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elftools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- david942j
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bindata
|
@@ -114,6 +114,7 @@ files:
|
|
114
114
|
- lib/elftools/sections/dynamic_section.rb
|
115
115
|
- lib/elftools/sections/note_section.rb
|
116
116
|
- lib/elftools/sections/null_section.rb
|
117
|
+
- lib/elftools/sections/relocation_section.rb
|
117
118
|
- lib/elftools/sections/section.rb
|
118
119
|
- lib/elftools/sections/sections.rb
|
119
120
|
- lib/elftools/sections/str_tab_section.rb
|
@@ -123,7 +124,7 @@ files:
|
|
123
124
|
- lib/elftools/segments/note_segment.rb
|
124
125
|
- lib/elftools/segments/segment.rb
|
125
126
|
- lib/elftools/segments/segments.rb
|
126
|
-
- lib/elftools/
|
127
|
+
- lib/elftools/structs.rb
|
127
128
|
- lib/elftools/util.rb
|
128
129
|
- lib/elftools/version.rb
|
129
130
|
homepage: https://github.com/david942j/rbelftools
|
data/lib/elftools/structures.rb
DELETED
@@ -1,133 +0,0 @@
|
|
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
|