elftools 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c59b0333aab08b885d294a4d0295d6bac48eb11c
4
+ data.tar.gz: 79ce95770fcf3911db8c7260bc6009d8e3cf2839
5
+ SHA512:
6
+ metadata.gz: f1bf67e49c1d910509199fd1c4276760ecc022a9e88a1045ddd3aeccab62f258a176bc57b059b71eec3b74bde3d652d666b53284a701785df5c79a385dbfc8aa
7
+ data.tar.gz: e5b41464b5b6d092bbb0cf752b73594a8771d5c91840ee471fd0744ed7259d994c25a3a46d2e2d509907aef12b1e0c817a77b1d544f3c5bd01c350e2f3b4cd0a
data/README.md ADDED
@@ -0,0 +1,139 @@
1
+ [![Build Status](https://travis-ci.org/david942j/rbelftools.svg?branch=master)](https://travis-ci.org/david942j/rbelftools)
2
+ [![Code Climate](https://codeclimate.com/github/david942j/rbelftools/badges/gpa.svg)](https://codeclimate.com/github/david942j/rbelftools)
3
+ [![Issue Count](https://codeclimate.com/github/david942j/rbelftools/badges/issue_count.svg)](https://codeclimate.com/github/david942j/rbelftools)
4
+ [![Test Coverage](https://codeclimate.com/github/david942j/rbelftools/badges/coverage.svg)](https://codeclimate.com/github/david942j/rbelftools/coverage)
5
+ [![Inline docs](https://inch-ci.org/github/david942j/rbelftools.svg?branch=master)](https://inch-ci.org/github/david942j/rbelftools)
6
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](http://choosealicense.com/licenses/mit/)
7
+
8
+ # Introduction
9
+
10
+ ELF parser in pure ruby implementation. This work is inspired by [pyelftools](https://github.com/eliben/pyelftools) by [Eli Bendersky](https://github.com/eliben).
11
+
12
+ The motivation to create this repository is want to be a dependency of [pwntools-ruby](https://github.com/peter50216/pwntools-ruby). Since ELF parser is a big work, it should not be implemented directly in pwntools.
13
+
14
+ **rbelftools**'s target is to create a nice ELF parser library in ruby. More features are work in progress.
15
+
16
+ # Install
17
+
18
+ Available on RubyGems.org!
19
+ ```bash
20
+ gem install elftools
21
+ ```
22
+
23
+ # Example Usage
24
+
25
+ ## Start from file object
26
+ ```ruby
27
+ require 'elftools'
28
+ elf = ELFTools::ELFFile.new(File.open('spec/files/amd64.elf'))
29
+ #=> #<ELFTools::ELFFile:0x00560b147f8328 @elf_class=64, @endian=:little, @stream=#<File:spec/files/amd64>>
30
+
31
+ elf.machine
32
+ #=> 'Advanced Micro Devices X86-64'
33
+
34
+ elf.build_id
35
+ #=> '73ab62cb7bc9959ce053c2b711322158708cdc07'
36
+ ```
37
+
38
+ ## Sections
39
+ ```ruby
40
+ elf.section_by_name('.dynstr')
41
+ #=>
42
+ # #<ELFTools::Sections::StrTabSection:0x00560b148cef40
43
+ # @header=
44
+ # {:sh_name=>86,
45
+ # :sh_type=>3,
46
+ # :sh_flags=>2,
47
+ # :sh_addr=>4195224,
48
+ # :sh_offset=>920,
49
+ # :sh_size=>113,
50
+ # :sh_link=>0,
51
+ # :sh_info=>0,
52
+ # :sh_addralign=>1,
53
+ # :sh_entsize=>0},
54
+ # @name=".dynstr">
55
+ ```
56
+ ```ruby
57
+ elf.sections.map(&:name).join(' ')
58
+ #=> " .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"
59
+ ```
60
+ ```ruby
61
+ elf.section_by_name('.note.gnu.build-id').data
62
+ #=> "\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"
63
+ ```
64
+
65
+ ## Symbols
66
+ ```ruby
67
+ symtab_section = elf.section_by_name('.symtab')
68
+ symtab_section.num_symbols
69
+ #=> 75
70
+
71
+ symtab_section.symbol_by_name('puts@@GLIBC_2.2.5')
72
+ #=>
73
+ # #<ELFTools::Symbol:0x00560b14af67a0
74
+ # @header={:st_name=>348, :st_info=>18, :st_other=>0, :st_shndx=>0, :st_value=>0, :st_size=>0},
75
+ # @name="puts@@GLIBC_2.2.5">
76
+
77
+ symbols = symtab_section.symbols # Array of symbols
78
+ symbols.map(&:name).reject(&:empty?).first(5).join(' ')
79
+ #=> "crtstuff.c __JCR_LIST__ deregister_tm_clones register_tm_clones __do_global_dtors_aux"
80
+ ```
81
+
82
+ ## Segments
83
+ ```ruby
84
+ elf.segment_by_type(:note)
85
+ #=>
86
+ # #<ELFTools::Segments::NoteSegment:0x00555beaafe218
87
+ # @header=
88
+ # {:p_type=>4,
89
+ # :p_flags=>4,
90
+ # :p_offset=>624,
91
+ # :p_vaddr=>624,
92
+ # :p_paddr=>624,
93
+ # :p_filesz=>68,
94
+ # :p_memsz=>68,
95
+ # :p_align=>4}>
96
+
97
+ elf.segment_by_type(:interp).interp_name
98
+ #=> "/lib64/ld-linux-x86-64.so.2"
99
+ ```
100
+
101
+ # Why rbelftools
102
+
103
+ 1. Fully documented
104
+
105
+ Always important for an Open-Source project. Online document is [here](http://www.rubydoc.info/github/david942j/rbelftools/master/frames)
106
+ 2. Fully tested
107
+
108
+ Of course.
109
+ 3. Lazy loading on everything
110
+
111
+ To use **rbelftools**, only need to pass the stream object of ELF file.
112
+ **rbelftools** will read the stream object **as least times as possible** when parsing
113
+ the file. Most information will not be fetched until you need it, which makes
114
+ **rbelftools** efficient.
115
+ 4. To be a library
116
+
117
+ **rbelftools** is designed to be a library for furthur usage.
118
+ It will _not_ add any too trivial features.
119
+ For example, to check if NX disabled, you can use
120
+ `elf.segment_by_type(:gnu_stack).executable?` but not `elf.nx?`
121
+ 5. Section and segment parser
122
+
123
+ Providing common sections and segments parser. For example, .symtab, .shstrtab
124
+ .dynamic sections and INTERP, DYNAMIC segments, etc.
125
+
126
+ # Development
127
+ ```bash
128
+ git clone https://github.com/david942j/rbelftools.git
129
+ cd rbelftools
130
+ bundle
131
+ rake
132
+ ```
133
+ Any comments or suggestions are welcome!
134
+
135
+ # Cross Platform
136
+ **rbelftools** can be used on Linux and OSX. Should also work on Windows but not tested.
137
+
138
+ # License
139
+ MIT License
@@ -0,0 +1,182 @@
1
+ module ELFTools
2
+ # Define constants from elf.h.
3
+ # Mostly refer from https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h.
4
+ module Constants
5
+ # ELF magic header
6
+ ELFMAG = "\x7FELF".freeze
7
+
8
+ # Section header types, records in +sh_type+.
9
+ module SHT
10
+ SHT_NULL = 0
11
+ SHT_PROGBITS = 1
12
+ SHT_SYMTAB = 2
13
+ SHT_STRTAB = 3
14
+ SHT_RELA = 4
15
+ SHT_HASH = 5
16
+ SHT_DYNAMIC = 6
17
+ SHT_NOTE = 7
18
+ SHT_NOBITS = 8
19
+ SHT_REL = 9
20
+ SHT_SHLIB = 10
21
+ SHT_DYNSYM = 11
22
+ SHT_NUM = 12
23
+ SHT_LOPROC = 0x70000000
24
+ SHT_HIPROC = 0x7fffffff
25
+ SHT_LOUSER = 0x80000000
26
+ SHT_HIUSER = 0xffffffff
27
+ end
28
+ include SHT
29
+
30
+ # Program header types, records in +p_type+.
31
+ module PT
32
+ PT_NULL = 0
33
+ PT_LOAD = 1
34
+ PT_DYNAMIC = 2
35
+ PT_INTERP = 3
36
+ PT_NOTE = 4
37
+ PT_SHLIB = 5
38
+ PT_PHDR = 6
39
+ PT_TLS = 7 # Thread local storage segment
40
+ PT_LOOS = 0x60000000 # OS-specific
41
+ PT_HIOS = 0x6fffffff # OS-specific
42
+ PT_LOPROC = 0x70000000
43
+ PT_HIPROC = 0x7fffffff
44
+ PT_GNU_EH_FRAME = 0x6474e550
45
+ PT_GNU_STACK = (PT_LOOS + 0x474e551)
46
+ end
47
+ include PT
48
+
49
+ # Dynamic table types, records in +d_tag+.
50
+ module DT
51
+ DT_NULL = 0
52
+ DT_NEEDED = 1
53
+ DT_PLTRELSZ = 2
54
+ DT_PLTGOT = 3
55
+ DT_HASH = 4
56
+ DT_STRTAB = 5
57
+ DT_SYMTAB = 6
58
+ DT_RELA = 7
59
+ DT_RELASZ = 8
60
+ DT_RELAENT = 9
61
+ DT_STRSZ = 10
62
+ DT_SYMENT = 11
63
+ DT_INIT = 12
64
+ DT_FINI = 13
65
+ DT_SONAME = 14
66
+ DT_RPATH = 15
67
+ DT_SYMBOLIC = 16
68
+ DT_REL = 17
69
+ DT_RELSZ = 18
70
+ DT_RELENT = 19
71
+ DT_PLTREL = 20
72
+ DT_DEBUG = 21
73
+ DT_TEXTREL = 22
74
+ DT_JMPREL = 23
75
+ DT_ENCODING = 32
76
+ DT_LOOS = 0x6000000d
77
+ DT_HIOS = 0x6ffff000
78
+ DT_VALRNGLO = 0x6ffffd00
79
+ DT_VALRNGHI = 0x6ffffdff
80
+ DT_ADDRRNGLO = 0x6ffffe00
81
+ DT_ADDRRNGHI = 0x6ffffeff
82
+ DT_VERSYM = 0x6ffffff0
83
+ DT_RELACOUNT = 0x6ffffff9
84
+ DT_RELCOUNT = 0x6ffffffa
85
+ DT_FLAGS_1 = 0x6ffffffb
86
+ DT_VERDEF = 0x6ffffffc
87
+ DT_VERDEFNUM = 0x6ffffffd
88
+ DT_VERNEED = 0x6ffffffe
89
+ DT_VERNEEDNUM = 0x6fffffff
90
+ DT_LOPROC = 0x70000000
91
+ DT_HIPROC = 0x7fffffff
92
+ end
93
+ include DT
94
+
95
+ # These constants define the various ELF target machines.
96
+ module EM
97
+ EM_NONE = 0
98
+ EM_M32 = 1
99
+ EM_SPARC = 2
100
+ EM_386 = 3
101
+ EM_68K = 4
102
+ EM_88K = 5
103
+ EM_486 = 6 # Perhaps disused
104
+ EM_860 = 7
105
+ EM_MIPS = 8 # MIPS R3000 (officially, big-endian only)
106
+ # Next two are historical and binaries and
107
+ # modules of these types will be rejected by Linux.
108
+ EM_MIPS_RS3_LE = 10 # MIPS R3000 little-endian
109
+ EM_MIPS_RS4_BE = 10 # MIPS R4000 big-endian
110
+
111
+ EM_PARISC = 15 # HPPA
112
+ EM_SPARC32PLUS = 18 # Sun's "v8plus"
113
+ EM_PPC = 20 # PowerPC
114
+ EM_PPC64 = 21 # PowerPC64
115
+ EM_SPU = 23 # Cell BE SPU
116
+ EM_ARM = 40 # ARM 32 bit
117
+ EM_SH = 42 # SuperH
118
+ EM_SPARCV9 = 43 # SPARC v9 64-bit
119
+ EM_H8_300 = 46 # Renesas H8/300
120
+ EM_IA_64 = 50 # HP/Intel IA-64
121
+ EM_X86_64 = 62 # AMD x86-64
122
+ EM_S390 = 22 # IBM S/390
123
+ EM_CRIS = 76 # Axis Communications 32-bit embedded processor
124
+ EM_M32R = 88 # Renesas M32R
125
+ EM_MN10300 = 89 # Panasonic/MEI MN10300, AM33
126
+ EM_OPENRISC = 92 # OpenRISC 32-bit embedded processor
127
+ EM_BLACKFIN = 106 # ADI Blackfin Processor
128
+ EM_ALTERA_NIOS2 = 113 # Altera Nios II soft-core processor
129
+ EM_TI_C6000 = 140 # TI C6X DSPs
130
+ EM_AARCH64 = 183 # ARM 64 bit
131
+ EM_TILEPRO = 188 # Tilera TILEPro
132
+ EM_MICROBLAZE = 189 # Xilinx MicroBlaze
133
+ EM_TILEGX = 191 # Tilera TILE-Gx
134
+ EM_BPF = 247 # Linux BPF - in-kernel virtual machine
135
+ EM_FRV = 0x5441 # Fujitsu FR-V
136
+ EM_AVR32 = 0x18ad # Atmel AVR32
137
+
138
+ # This is an interim value that we will use until the committee comes up with a final number.
139
+ EM_ALPHA = 0x9026
140
+
141
+ # Bogus old m32r magic number, used by old tools.
142
+ EM_CYGNUS_M32R = 0x9041
143
+ # This is the old interim value for S/390 architecture
144
+ EM_S390_OLD = 0xA390
145
+ # Also Panasonic/MEI MN10300, AM33
146
+ EM_CYGNUS_MN10300 = 0xbeef
147
+
148
+ # Return the architecture name according to +val+.
149
+ # Used by {ELFTools::ELFFile#machine}.
150
+ #
151
+ # Only supports famous archs.
152
+ # @param [Integer] val Value of +e_machine+.
153
+ # @return [String]
154
+ # Name of architecture.
155
+ # @example
156
+ # mapping(3)
157
+ # #=> 'Intel 80386'
158
+ # mapping(6)
159
+ # #=> 'Intel 80386'
160
+ # mapping(62)
161
+ # #=> 'Advanced Micro Devices X86-64'
162
+ # mapping(1337)
163
+ # #=> '<unknown>: 0x539'
164
+ def self.mapping(val)
165
+ case val
166
+ when EM_NONE then 'None'
167
+ when EM_386, EM_486 then 'Intel 80386'
168
+ when EM_860 then 'Intel 80860'
169
+ when EM_MIPS then 'MIPS R3000'
170
+ when EM_PPC then 'PowerPC'
171
+ when EM_PPC64 then 'PowerPC64'
172
+ when EM_ARM then 'ARM'
173
+ when EM_IA_64 then 'Intel IA-64'
174
+ when EM_AARCH64 then 'AArch64'
175
+ when EM_X86_64 then 'Advanced Micro Devices X86-64'
176
+ else format('<unknown>: 0x%x', val)
177
+ end
178
+ end
179
+ end
180
+ include EM
181
+ end
182
+ end
@@ -0,0 +1,106 @@
1
+ module ELFTools
2
+ # Define common methods for dynamic sections and
3
+ # dynamic segments.
4
+ #
5
+ # Notice: this module can only be included by
6
+ # {ELFTools::Sections::DynamicSection} and
7
+ # {ELFTools::Segments::DynamicSegment} because
8
+ # methods here assume some attributes exist.
9
+ module Dynamic
10
+ # Iterate all tags.
11
+ #
12
+ # Notice: this method assume the following methods
13
+ # already exist:
14
+ # header
15
+ # tag_start
16
+ # @param [Block] block You can give a block.
17
+ # @return [Array<ELFTools::Dynamic::Tag>] Array of tags.
18
+ def each_tags
19
+ arr = []
20
+ 0.step do |i|
21
+ tag = tag_at(i)
22
+ yield tag if block_given?
23
+ arr << tag
24
+ break if tag.header.d_tag == ELFTools::Constants::DT_NULL
25
+ end
26
+ arr
27
+ end
28
+ alias tags each_tags
29
+
30
+ # Get a tag of specific type.
31
+ # @param [Integer, Symbol, String] type
32
+ # Constant value, symbol, or string of type
33
+ # is acceptable. See examples for more information.
34
+ # @return [ELFTools::Dynamic::Tag] The desired tag.
35
+ # @example
36
+ # dynamic = elf.segment_by_type(:dynamic)
37
+ # # type as integer
38
+ # dynamic.tag_by_type(0) # the null tag
39
+ # #=> #<ELFTools::Dynamic::Tag:0x0055b5a5ecad28 @header={:d_tag=>0, :d_val=>0}>
40
+ # dynamic.tag_by_type(ELFTools::Constants::DT_NULL)
41
+ # #=> #<ELFTools::Dynamic::Tag:0x0055b5a5ecad28 @header={:d_tag=>0, :d_val=>0}>
42
+ #
43
+ # # symbol
44
+ # dynamic.tag_by_type(:null)
45
+ # #=> #<ELFTools::Dynamic::Tag:0x0055b5a5ecad28 @header={:d_tag=>0, :d_val=>0}>
46
+ # dynamic.tag_by_type(:pltgot)
47
+ # #=> #<ELFTools::Dynamic::Tag:0x0055d3d2d91b28 @header={:d_tag=>3, :d_val=>6295552}>
48
+ #
49
+ # # string
50
+ # dynamic.tag_by_type('null')
51
+ # #=> #<ELFTools::Dynamic::Tag:0x0055b5a5ecad28 @header={:d_tag=>0, :d_val=>0}>
52
+ # dynamic.tag_by_type('DT_PLTGOT')
53
+ # #=> #<ELFTools::Dynamic::Tag:0x0055d3d2d91b28 @header={:d_tag=>3, :d_val=>6295552}>
54
+ def tag_by_type(type)
55
+ type = Util.to_constant(Constants::DT, type)
56
+ each_tags do |tag|
57
+ return tag if tag.header.d_tag == type
58
+ end
59
+ nil
60
+ end
61
+
62
+ # Get the +n+-th tag.
63
+ #
64
+ # Tags are lazy loaded.
65
+ # Notice: this method assume the following methods
66
+ # already exist:
67
+ # header
68
+ # tag_start
69
+ #
70
+ # Notice: we cannot do bound checking of +n+ here since
71
+ # the only way to get size of tags is calling +tags.size+.
72
+ # @param [Integer] n The index.
73
+ # @return [ELFTools::Dynamic::Tag] The desired tag.
74
+ def tag_at(n)
75
+ return if n < 0
76
+ @tag_at_map ||= {}
77
+ return @tag_at_map[n] if @tag_at_map[n]
78
+ dyn = ELF_Dyn.new(endian: endian)
79
+ dyn.elf_class = header.elf_class
80
+ stream.pos = tag_start + n * dyn.num_bytes
81
+ @tag_at_map[n] = Tag.new(dyn.read(stream), stream)
82
+ end
83
+
84
+ private
85
+
86
+ def endian
87
+ header.class.self_endian
88
+ end
89
+
90
+ # A tag class.
91
+ class Tag
92
+ attr_reader :header # @return [ELFTools::ELF_Dyn] The dynamic tag header.
93
+ attr_reader :stream # @return [File] Streaming object.
94
+
95
+ # Instantiate a {ELFTools::Dynamic::Tag} object.
96
+ # @param [ELF_Dyn] header The dynamic tag header.
97
+ # @param [File] stream Streaming object.
98
+ def initialize(header, stream)
99
+ @header = header
100
+ @stream = stream
101
+ end
102
+ # TODO: Get the name of tags, e.g. SONAME
103
+ # TODO: Handle (non)-PIE ELF correctly.
104
+ end
105
+ end
106
+ end