elftools 0.2.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +44 -0
- data/lib/elftools/dynamic.rb +1 -0
- data/lib/elftools/elf_file.rb +42 -3
- data/lib/elftools/lazy_array.rb +4 -4
- data/lib/elftools/note.rb +1 -1
- data/lib/elftools/sections/relocation_section.rb +1 -1
- data/lib/elftools/sections/section.rb +1 -1
- data/lib/elftools/sections/sym_tab_section.rb +1 -1
- data/lib/elftools/structs.rb +48 -5
- data/lib/elftools/version.rb +1 -1
- metadata +29 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9ca4e5e90a3710cbc74557520cfd6efcb442e3b
|
4
|
+
data.tar.gz: 9f745e3609cdaa67103ccf718db973ee8c4b3c74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9edec0296929aa3c6dbc90475d8c0281d6f9a559d681945b4badc1778ad293f340a9c49a0526c4c92cfd03aa546774f3a411d98d66a04390233a29c2780a4151
|
7
|
+
data.tar.gz: cc1df33b85fdaebd9dadf64250fdd1f633fe29f9501e2c68fc41cb711e736564a42c307f4c61a06ce1db24348998b2d6652bb72e15c1ae7375ab9d9e22a40bc2
|
data/README.md
CHANGED
@@ -6,6 +6,9 @@
|
|
6
6
|
[](https://inch-ci.org/github/david942j/rbelftools)
|
7
7
|
[](http://choosealicense.com/licenses/mit/)
|
8
8
|
|
9
|
+
# rbelftools
|
10
|
+
Pure ruby library for parsing and patching ELF files.
|
11
|
+
|
9
12
|
# Introduction
|
10
13
|
|
11
14
|
ELF parser in pure ruby implementation. This work is inspired by [pyelftools](https://github.com/eliben/pyelftools) by [Eli Bendersky](https://github.com/eliben).
|
@@ -21,6 +24,14 @@ Available on RubyGems.org!
|
|
21
24
|
gem install elftools
|
22
25
|
```
|
23
26
|
|
27
|
+
# Features
|
28
|
+
|
29
|
+
- [x] Supports both big and little endian
|
30
|
+
- [x] ELF parser
|
31
|
+
- [x] ELF headers patcher
|
32
|
+
|
33
|
+
See example usage for more details.
|
34
|
+
|
24
35
|
# Example Usage
|
25
36
|
|
26
37
|
## Start from file object
|
@@ -114,6 +125,39 @@ relocations.map { |r| symtab.symbol_at(r.symbol_index).name }
|
|
114
125
|
#=> ["puts", "__stack_chk_fail", "printf", "__libc_start_main", "fgets", "scanf"]
|
115
126
|
```
|
116
127
|
|
128
|
+
## Patch
|
129
|
+
|
130
|
+
Patch ELF is so easy!
|
131
|
+
|
132
|
+
All kinds of headers (i.e. `Ehdr`, `Shdr`, `Phdr`, etc.) can be patched.
|
133
|
+
Patched slots will not be applied on the opened file.
|
134
|
+
Invoke `elf.save(filename)` to save the patched ELF into `filename`.
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
elf = ELFTools::ELFFile.new(File.open('spec/files/amd64.elf'))
|
138
|
+
elf.machine
|
139
|
+
#=> "Advanced Micro Devices X86-64"
|
140
|
+
elf.header.e_machine = 40
|
141
|
+
elf.machine
|
142
|
+
#=> "ARM"
|
143
|
+
|
144
|
+
interp_segment = elf.segment_by_type(:interp)
|
145
|
+
interp_segment.interp_name
|
146
|
+
#=> "/lib64/ld-linux-x86-64.so.2"
|
147
|
+
interp_segment.header.p_filesz
|
148
|
+
#=> 28
|
149
|
+
interp_segment.header.p_filesz = 20
|
150
|
+
interp_segment.interp_name
|
151
|
+
#=> "/lib64/ld-linux-x86"
|
152
|
+
|
153
|
+
# save the patched ELF
|
154
|
+
elf.save('elf.patched')
|
155
|
+
|
156
|
+
# in bash
|
157
|
+
# $ file elf.patched
|
158
|
+
# elf.patched: ELF 64-bit LSB executable, ARM, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86, for GNU...
|
159
|
+
```
|
160
|
+
|
117
161
|
# Why rbelftools
|
118
162
|
|
119
163
|
1. Fully documented
|
data/lib/elftools/dynamic.rb
CHANGED
data/lib/elftools/elf_file.rb
CHANGED
@@ -21,6 +21,8 @@ module ELFTools
|
|
21
21
|
# #=> #<ELFTools::ELFFile:0x00564b106c32a0 @elf_class=64, @endian=:little, @stream=#<File:/bin/cat>>
|
22
22
|
def initialize(stream)
|
23
23
|
@stream = stream
|
24
|
+
# always set binmode if stream is an IO object.
|
25
|
+
@stream.binmode if @stream.respond_to?(:binmode)
|
24
26
|
identify # fetch the most basic information
|
25
27
|
end
|
26
28
|
|
@@ -31,7 +33,7 @@ module ELFTools
|
|
31
33
|
def header
|
32
34
|
return @header if defined?(@header)
|
33
35
|
stream.pos = 0
|
34
|
-
@header = Structs::ELF_Ehdr.new(endian: endian)
|
36
|
+
@header = Structs::ELF_Ehdr.new(endian: endian, offset: stream.pos)
|
35
37
|
@header.elf_class = elf_class
|
36
38
|
@header.read(stream)
|
37
39
|
end
|
@@ -290,8 +292,45 @@ module ELFTools
|
|
290
292
|
end
|
291
293
|
end
|
292
294
|
|
295
|
+
# The patch status.
|
296
|
+
# @return [Hash{Integer => String}]
|
297
|
+
def patches
|
298
|
+
patch = {}
|
299
|
+
loaded_headers.each do |header|
|
300
|
+
header.patches.each do |key, val|
|
301
|
+
patch[key + header.offset] = val
|
302
|
+
end
|
303
|
+
end
|
304
|
+
patch
|
305
|
+
end
|
306
|
+
|
307
|
+
# Apply patches and save as +filename+.
|
308
|
+
#
|
309
|
+
# @param [String] filename
|
310
|
+
# @return [void]
|
311
|
+
def save(filename)
|
312
|
+
stream.pos = 0
|
313
|
+
all = stream.read.force_encoding('ascii-8bit')
|
314
|
+
patches.each do |pos, val|
|
315
|
+
all[pos, val.size] = val
|
316
|
+
end
|
317
|
+
IO.binwrite(filename, all)
|
318
|
+
end
|
319
|
+
|
293
320
|
private
|
294
321
|
|
322
|
+
# bad idea..
|
323
|
+
def loaded_headers
|
324
|
+
explore = lambda do |obj|
|
325
|
+
return obj if obj.is_a?(::ELFTools::Structs::ELFStruct)
|
326
|
+
return obj.map(&explore) if obj.is_a?(Array)
|
327
|
+
obj.instance_variables.map do |s|
|
328
|
+
explore.call(obj.instance_variable_get(s))
|
329
|
+
end
|
330
|
+
end
|
331
|
+
explore.call(self).flatten
|
332
|
+
end
|
333
|
+
|
295
334
|
def identify
|
296
335
|
stream.pos = 0
|
297
336
|
magic = stream.read(4)
|
@@ -312,7 +351,7 @@ module ELFTools
|
|
312
351
|
|
313
352
|
def create_section(n)
|
314
353
|
stream.pos = header.e_shoff + n * header.e_shentsize
|
315
|
-
shdr = Structs::ELF_Shdr.new(endian: endian)
|
354
|
+
shdr = Structs::ELF_Shdr.new(endian: endian, offset: stream.pos)
|
316
355
|
shdr.elf_class = elf_class
|
317
356
|
shdr.read(stream)
|
318
357
|
Sections::Section.create(shdr, stream,
|
@@ -323,7 +362,7 @@ module ELFTools
|
|
323
362
|
|
324
363
|
def create_segment(n)
|
325
364
|
stream.pos = header.e_phoff + n * header.e_phentsize
|
326
|
-
phdr = Structs::ELF_Phdr[elf_class].new(endian: endian)
|
365
|
+
phdr = Structs::ELF_Phdr[elf_class].new(endian: endian, offset: stream.pos)
|
327
366
|
phdr.elf_class = elf_class
|
328
367
|
Segments::Segment.create(phdr.read(stream), stream, offset_from_vma: method(:offset_from_vma))
|
329
368
|
end
|
data/lib/elftools/lazy_array.rb
CHANGED
@@ -8,17 +8,17 @@ module ELFTools
|
|
8
8
|
# @param [Integer] size
|
9
9
|
# The size of array.
|
10
10
|
# @yieldparam [Integer] i
|
11
|
-
# Needs +i+-th element.
|
11
|
+
# Needs the +i+-th element.
|
12
12
|
# @yieldreturn [Object]
|
13
13
|
# Value of the +i+-th element.
|
14
14
|
# @example
|
15
|
-
# arr = LazyArray.new(10) { |i| p i; i * i }
|
15
|
+
# arr = LazyArray.new(10) { |i| p "calc #{i}"; i * i }
|
16
16
|
# p arr[2]
|
17
|
-
# # 2
|
17
|
+
# # "calc 2"
|
18
18
|
# # 4
|
19
19
|
#
|
20
20
|
# p arr[3]
|
21
|
-
# # 3
|
21
|
+
# # "calc 3"
|
22
22
|
# # 9
|
23
23
|
#
|
24
24
|
# p arr[3]
|
data/lib/elftools/note.rb
CHANGED
@@ -59,7 +59,7 @@ module ELFTools
|
|
59
59
|
def create_relocation(n)
|
60
60
|
stream.pos = header.sh_offset + n * header.sh_entsize
|
61
61
|
klass = rela? ? Structs::ELF_Rela : Structs::ELF_Rel
|
62
|
-
rel = klass.new(endian: header.class.self_endian)
|
62
|
+
rel = klass.new(endian: header.class.self_endian, offset: stream.pos)
|
63
63
|
rel.elf_class = header.elf_class
|
64
64
|
rel.read(stream)
|
65
65
|
Relocation.new(rel, stream)
|
@@ -87,7 +87,7 @@ module ELFTools
|
|
87
87
|
|
88
88
|
def create_symbol(n)
|
89
89
|
stream.pos = header.sh_offset + n * header.sh_entsize
|
90
|
-
sym = Structs::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, offset: stream.pos)
|
91
91
|
sym.read(stream)
|
92
92
|
Symbol.new(sym, stream, symstr: method(:symstr))
|
93
93
|
end
|
data/lib/elftools/structs.rb
CHANGED
@@ -2,7 +2,7 @@ require 'bindata'
|
|
2
2
|
module ELFTools
|
3
3
|
# Define ELF related structures in this module.
|
4
4
|
#
|
5
|
-
# Structures are
|
5
|
+
# Structures are fetched from https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h.
|
6
6
|
# Using the bindata gem to make these structures support 32/64 bits and
|
7
7
|
# little/big endian simultaneously.
|
8
8
|
module Structs
|
@@ -14,11 +14,54 @@ module ELFTools
|
|
14
14
|
}.freeze
|
15
15
|
|
16
16
|
attr_accessor :elf_class # @return [Integer] 32 or 64.
|
17
|
+
attr_accessor :offset # @return [Integer] The file offset of this header.
|
17
18
|
|
18
|
-
#
|
19
|
-
# @return [
|
20
|
-
def
|
21
|
-
|
19
|
+
# Records which fields have been patched.
|
20
|
+
# @return [Hash{Integer => Integer}] Patches.
|
21
|
+
def patches
|
22
|
+
@patches ||= {}
|
23
|
+
end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
# Hook constructor, while +BinData::Record+ doesn't allow us to override +#initialize+,
|
27
|
+
# so we hack +new+ here.
|
28
|
+
def new(**kwargs)
|
29
|
+
offset = kwargs.delete(:offset)
|
30
|
+
super.tap do |obj|
|
31
|
+
obj.offset = offset
|
32
|
+
obj.field_names.each do |f|
|
33
|
+
m = "#{f}=".to_sym
|
34
|
+
old_method = obj.singleton_method(m)
|
35
|
+
obj.define_singleton_method(m) do |val|
|
36
|
+
org = obj.send(f)
|
37
|
+
obj.patches[org.abs_offset] = ELFStruct.pack(val, org.num_bytes)
|
38
|
+
old_method.call(val)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Hacking to get endian of current class
|
45
|
+
# @return [Symbol, nil] +:little+ or +:big+.
|
46
|
+
def self_endian
|
47
|
+
bindata_name[-2..-1] == 'ge' ? :big : :little
|
48
|
+
end
|
49
|
+
|
50
|
+
# Pack integer into string.
|
51
|
+
# @param [Integer] val
|
52
|
+
# @param [Integer] bytes
|
53
|
+
# @return [String]
|
54
|
+
def pack(val, bytes)
|
55
|
+
raise ArgumentError, "Not supported assign type #{val.class}" unless val.is_a?(Integer)
|
56
|
+
number = val & ((1 << (8 * bytes)) - 1)
|
57
|
+
out = []
|
58
|
+
bytes.times do
|
59
|
+
out << (number & 0xff)
|
60
|
+
number >>= 8
|
61
|
+
end
|
62
|
+
out = out.pack('C*')
|
63
|
+
self_endian == :little ? out : out.reverse
|
64
|
+
end
|
22
65
|
end
|
23
66
|
end
|
24
67
|
|
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: 1.0.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-
|
11
|
+
date: 2017-10-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bindata
|
@@ -25,33 +25,33 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: codeclimate-test-reporter
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0.6'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0.6'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: pry
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0.
|
47
|
+
version: '0.10'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0.
|
54
|
+
version: '0.10'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,33 +67,47 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '12.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: rspec
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: '3.5'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: '3.5'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: rubocop
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '0.
|
89
|
+
version: '0.47'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '0.
|
96
|
+
version: '0.47'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: simplecov
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.13.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.13.0
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: yard
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,5 +178,5 @@ rubyforge_project:
|
|
164
178
|
rubygems_version: 2.5.2
|
165
179
|
signing_key:
|
166
180
|
specification_version: 4
|
167
|
-
summary: ELFTools - Pure ruby library for parsing ELF files
|
181
|
+
summary: ELFTools - Pure ruby library for parsing and patching ELF files
|
168
182
|
test_files: []
|