elftools 0.2.2 → 1.0.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 +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
|
[![Inline docs](https://inch-ci.org/github/david942j/rbelftools.svg?branch=master)](https://inch-ci.org/github/david942j/rbelftools)
|
7
7
|
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](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: []
|