elftools 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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