indis-core 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.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +3 -0
- data/Gemfile.ci +5 -0
- data/LICENSE +674 -0
- data/README.md +5 -0
- data/Rakefile +9 -0
- data/indis-core.gemspec +20 -0
- data/lib/indis-core.rb +22 -0
- data/lib/indis-core/binary_format.rb +61 -0
- data/lib/indis-core/data_entity.rb +64 -0
- data/lib/indis-core/entity.rb +43 -0
- data/lib/indis-core/section.rb +44 -0
- data/lib/indis-core/segment.rb +75 -0
- data/lib/indis-core/symbol.rb +33 -0
- data/lib/indis-core/target.rb +68 -0
- data/lib/indis-core/version.rb +21 -0
- data/lib/indis-core/vmmap.rb +141 -0
- data/spec/fixtures/single-object.o +0 -0
- data/spec/indis-core/binary_format_spec.rb +7 -0
- data/spec/indis-core/data_entity_spec.rb +13 -0
- data/spec/indis-core/section_spec.rb +14 -0
- data/spec/indis-core/segment_spec.rb +19 -0
- data/spec/indis-core/symbol_spec.rb +5 -0
- data/spec/indis-core/target_spec.rb +18 -0
- data/spec/indis-core/vmmap_spec.rb +168 -0
- data/spec/spec_helper.rb +8 -0
- metadata +100 -0
data/README.md
ADDED
data/Rakefile
ADDED
data/indis-core.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/indis-core/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Vladimir Pouzanov"]
|
6
|
+
gem.email = ["farcaller@gmail.com"]
|
7
|
+
gem.description = "Core components of indis, the intelligent disassembler framework"
|
8
|
+
gem.summary = "Core indis components"
|
9
|
+
gem.homepage = "http://www.indis.org/"
|
10
|
+
gem.license = "GPL-3"
|
11
|
+
|
12
|
+
gem.files = `git ls-files`.split($\)
|
13
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
14
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
15
|
+
gem.name = "indis-core"
|
16
|
+
gem.require_paths = ["lib"]
|
17
|
+
gem.version = Indis::VERSION
|
18
|
+
|
19
|
+
gem.add_development_dependency 'rspec'
|
20
|
+
end
|
data/lib/indis-core.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
##############################################################################
|
2
|
+
# Indis framework #
|
3
|
+
# Copyright (C) 2012 Vladimir "Farcaller" Pouzanov <farcaller@gmail.com> #
|
4
|
+
# #
|
5
|
+
# This program is free software: you can redistribute it and/or modify #
|
6
|
+
# it under the terms of the GNU General Public License as published by #
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or #
|
8
|
+
# (at your option) any later version. #
|
9
|
+
# #
|
10
|
+
# This program is distributed in the hope that it will be useful, #
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
13
|
+
# GNU General Public License for more details. #
|
14
|
+
# #
|
15
|
+
# You should have received a copy of the GNU General Public License #
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
17
|
+
##############################################################################
|
18
|
+
|
19
|
+
require 'indis-core/version'
|
20
|
+
|
21
|
+
module Indis
|
22
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
##############################################################################
|
2
|
+
# Indis framework #
|
3
|
+
# Copyright (C) 2012 Vladimir "Farcaller" Pouzanov <farcaller@gmail.com> #
|
4
|
+
# #
|
5
|
+
# This program is free software: you can redistribute it and/or modify #
|
6
|
+
# it under the terms of the GNU General Public License as published by #
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or #
|
8
|
+
# (at your option) any later version. #
|
9
|
+
# #
|
10
|
+
# This program is distributed in the hope that it will be useful, #
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
13
|
+
# GNU General Public License for more details. #
|
14
|
+
# #
|
15
|
+
# You should have received a copy of the GNU General Public License #
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
17
|
+
##############################################################################
|
18
|
+
|
19
|
+
module Indis
|
20
|
+
|
21
|
+
# BinaryFormat manages a set of known binary formats and provides support
|
22
|
+
# for guessing the correct format for target binary.
|
23
|
+
module BinaryFormat
|
24
|
+
|
25
|
+
# Returns a list of all known binary formats.
|
26
|
+
#
|
27
|
+
# @return [Array] all known binary formats.
|
28
|
+
def self.known_formats
|
29
|
+
fmt = []
|
30
|
+
self.constants.each do |c|
|
31
|
+
e = const_get(c)
|
32
|
+
fmt << e if e.is_a?(Class) && e.superclass == Format
|
33
|
+
end
|
34
|
+
fmt
|
35
|
+
end
|
36
|
+
|
37
|
+
# Base class for any binary format.
|
38
|
+
class Format
|
39
|
+
# Basic constructor takes care of storing the target and io stream.
|
40
|
+
def initialize(target, io)
|
41
|
+
@target = target
|
42
|
+
@io = io
|
43
|
+
end
|
44
|
+
|
45
|
+
# @abstract Returns the format magic bytes.
|
46
|
+
#
|
47
|
+
# @return [Fixnum] Magic bytes that are checked against the first bytes in binary.
|
48
|
+
def self.magic
|
49
|
+
raise RuntimeError
|
50
|
+
end
|
51
|
+
|
52
|
+
# @abstract Returns the human-readable format name.
|
53
|
+
#
|
54
|
+
# @return [String] Human-readable format name.
|
55
|
+
def self.name
|
56
|
+
raise RuntimeError
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
##############################################################################
|
2
|
+
# Indis framework #
|
3
|
+
# Copyright (C) 2012 Vladimir "Farcaller" Pouzanov <farcaller@gmail.com> #
|
4
|
+
# #
|
5
|
+
# This program is free software: you can redistribute it and/or modify #
|
6
|
+
# it under the terms of the GNU General Public License as published by #
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or #
|
8
|
+
# (at your option) any later version. #
|
9
|
+
# #
|
10
|
+
# This program is distributed in the hope that it will be useful, #
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
13
|
+
# GNU General Public License for more details. #
|
14
|
+
# #
|
15
|
+
# You should have received a copy of the GNU General Public License #
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
17
|
+
##############################################################################
|
18
|
+
|
19
|
+
require 'indis-core/entity'
|
20
|
+
|
21
|
+
module Indis
|
22
|
+
|
23
|
+
# DataEntity represens "data bytes" that are directly used e.g. in a load
|
24
|
+
# to register instruction. There are four sizes of data: 8, 16, 32 and 64
|
25
|
+
# bits long, namely +byte+, +word+, +dword+ and +qword+
|
26
|
+
class DataEntity < Entity
|
27
|
+
KIND = {
|
28
|
+
1 => :byte,
|
29
|
+
2 => :word,
|
30
|
+
4 => :dword,
|
31
|
+
8 => :qword,
|
32
|
+
}
|
33
|
+
|
34
|
+
NAMED_TYPE = {
|
35
|
+
byte: 'DCB',
|
36
|
+
word: 'DCW',
|
37
|
+
dword: 'DCD',
|
38
|
+
qword: 'DCQ'
|
39
|
+
}
|
40
|
+
|
41
|
+
attr_reader :value # @return [Fixnum] the value of entity
|
42
|
+
|
43
|
+
# @param [Fixnum] ofs virtual address
|
44
|
+
# @param [Fixnum] size entity size in bytes
|
45
|
+
# @param [Indus::VMMap] vmmap map of the target to load value from
|
46
|
+
# @raise [AttributeError] if the size is not one of the known values
|
47
|
+
def initialize(ofs, size, vmmap)
|
48
|
+
raise ArgumentError, "Unaligned size" unless KIND[size]
|
49
|
+
super ofs
|
50
|
+
@size = size
|
51
|
+
@value = vmmap.bytes_at(ofs, size).reverse_each.reduce(0) { |v, i| (v << 8) + i }
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [KIND] entity kind
|
55
|
+
def kind
|
56
|
+
KIND[@size]
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_s
|
60
|
+
"#{NAMED_TYPE[kind]}\t#{sprintf("%0#{@size*2}X", @value)}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
##############################################################################
|
2
|
+
# Indis framework #
|
3
|
+
# Copyright (C) 2012 Vladimir "Farcaller" Pouzanov <farcaller@gmail.com> #
|
4
|
+
# #
|
5
|
+
# This program is free software: you can redistribute it and/or modify #
|
6
|
+
# it under the terms of the GNU General Public License as published by #
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or #
|
8
|
+
# (at your option) any later version. #
|
9
|
+
# #
|
10
|
+
# This program is distributed in the hope that it will be useful, #
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
13
|
+
# GNU General Public License for more details. #
|
14
|
+
# #
|
15
|
+
# You should have received a copy of the GNU General Public License #
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
17
|
+
##############################################################################
|
18
|
+
|
19
|
+
module Indis
|
20
|
+
|
21
|
+
# Entity represens an object mapped to some given bytes in the traget's
|
22
|
+
# virtual address space
|
23
|
+
class Entity
|
24
|
+
attr_reader :vmaddr # @return [Fixnum] virtaul address of the entity
|
25
|
+
|
26
|
+
# @abstract You must provide @size value in a subclass
|
27
|
+
# @return [Fixnum] entity size in bytes
|
28
|
+
attr_reader :size
|
29
|
+
|
30
|
+
attr_reader :tags # @return [Hash] cross-references and additional attributes of entity
|
31
|
+
|
32
|
+
def initialize(ofs)
|
33
|
+
@vmaddr = ofs
|
34
|
+
@tags = {}
|
35
|
+
end
|
36
|
+
|
37
|
+
def unmap
|
38
|
+
# TODO: Reverse unmap tags
|
39
|
+
@tags = nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
##############################################################################
|
2
|
+
# Indis framework #
|
3
|
+
# Copyright (C) 2012 Vladimir "Farcaller" Pouzanov <farcaller@gmail.com> #
|
4
|
+
# #
|
5
|
+
# This program is free software: you can redistribute it and/or modify #
|
6
|
+
# it under the terms of the GNU General Public License as published by #
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or #
|
8
|
+
# (at your option) any later version. #
|
9
|
+
# #
|
10
|
+
# This program is distributed in the hope that it will be useful, #
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
13
|
+
# GNU General Public License for more details. #
|
14
|
+
# #
|
15
|
+
# You should have received a copy of the GNU General Public License #
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
17
|
+
##############################################################################
|
18
|
+
|
19
|
+
module Indis
|
20
|
+
|
21
|
+
class Section
|
22
|
+
attr_reader :name, :segment
|
23
|
+
attr_reader :vmaddr, :vmsize, :bytes
|
24
|
+
|
25
|
+
def initialize(seg, name, vmaddr, vmsize, iooff)
|
26
|
+
@segment = seg
|
27
|
+
@name = name
|
28
|
+
@vmaddr = vmaddr
|
29
|
+
@vmsize = vmsize
|
30
|
+
@iooff = iooff
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_vmrange
|
34
|
+
@vmaddr..(@vmaddr+@vmsize)
|
35
|
+
end
|
36
|
+
|
37
|
+
def bytes
|
38
|
+
s = @iooff - @segment.iooff
|
39
|
+
e = s + @vmsize
|
40
|
+
@segment.bytes[s...e]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
##############################################################################
|
2
|
+
# Indis framework #
|
3
|
+
# Copyright (C) 2012 Vladimir "Farcaller" Pouzanov <farcaller@gmail.com> #
|
4
|
+
# #
|
5
|
+
# This program is free software: you can redistribute it and/or modify #
|
6
|
+
# it under the terms of the GNU General Public License as published by #
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or #
|
8
|
+
# (at your option) any later version. #
|
9
|
+
# #
|
10
|
+
# This program is distributed in the hope that it will be useful, #
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
13
|
+
# GNU General Public License for more details. #
|
14
|
+
# #
|
15
|
+
# You should have received a copy of the GNU General Public License #
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
17
|
+
##############################################################################
|
18
|
+
|
19
|
+
module Indis
|
20
|
+
|
21
|
+
# A segment describes one given segment contained in the target binary.
|
22
|
+
# Segment's virtual size might be different from physical (stored in file),
|
23
|
+
# in this case the data is padded with zeroes
|
24
|
+
#
|
25
|
+
# @note this class is heavily based on Mach-O
|
26
|
+
class Segment
|
27
|
+
# Contains a list of current segment sections
|
28
|
+
# @return [Array]
|
29
|
+
attr_reader :sections
|
30
|
+
|
31
|
+
attr_reader :target # @return [Indis::Target] owning target
|
32
|
+
|
33
|
+
attr_reader :name # @return [String] segment name
|
34
|
+
|
35
|
+
attr_reader :vmaddr # @return [Fixnum] starting virtual address
|
36
|
+
|
37
|
+
attr_reader :vmsize # @return [Fixnum] segment size
|
38
|
+
|
39
|
+
# The whole (zero-padded if required) bytes string for a segment
|
40
|
+
# @return [String]
|
41
|
+
attr_reader :bytes
|
42
|
+
|
43
|
+
attr_reader :iooff # @return [Fixnum] offset from the beginning of of file to segment data
|
44
|
+
|
45
|
+
# @param [Indis::Target] target the target containing a segment
|
46
|
+
# @param [String] name segment name
|
47
|
+
# @param [Fixnum] vmaddr starting virtual address
|
48
|
+
# @param [Fixnum] vmsize size (in bytes)
|
49
|
+
# @param [Fixnum] iooff offset from the beginning of of file to segment data
|
50
|
+
# @param [String] bytes known data bytes (loaded from binary)
|
51
|
+
def initialize(target, name, vmaddr, vmsize, iooff, bytes)
|
52
|
+
@target = target
|
53
|
+
@name = name
|
54
|
+
@sections = []
|
55
|
+
|
56
|
+
@vmaddr = vmaddr
|
57
|
+
@vmsize = vmsize
|
58
|
+
@iooff = iooff
|
59
|
+
@bytes = pad_bytes(bytes)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Constructs a Range of virtual addresses used by segment
|
63
|
+
#
|
64
|
+
# @return [Range] the range of all addresses
|
65
|
+
def to_vmrange
|
66
|
+
@vmaddr..(@vmaddr+@vmsize)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
def pad_bytes(bytes)
|
71
|
+
bytes + ("\x0" * (@vmsize - bytes.length))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
##############################################################################
|
2
|
+
# Indis framework #
|
3
|
+
# Copyright (C) 2012 Vladimir "Farcaller" Pouzanov <farcaller@gmail.com> #
|
4
|
+
# #
|
5
|
+
# This program is free software: you can redistribute it and/or modify #
|
6
|
+
# it under the terms of the GNU General Public License as published by #
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or #
|
8
|
+
# (at your option) any later version. #
|
9
|
+
# #
|
10
|
+
# This program is distributed in the hope that it will be useful, #
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
13
|
+
# GNU General Public License for more details. #
|
14
|
+
# #
|
15
|
+
# You should have received a copy of the GNU General Public License #
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
17
|
+
##############################################################################
|
18
|
+
|
19
|
+
module Indis
|
20
|
+
|
21
|
+
class Symbol
|
22
|
+
attr_reader :name, :section, :format_sym, :image, :vmaddr
|
23
|
+
|
24
|
+
def initialize(name, section, image, vmaddr, format_sym)
|
25
|
+
@name = name
|
26
|
+
@section = section
|
27
|
+
@image = image
|
28
|
+
@format_sym = format_sym
|
29
|
+
@vmaddr = vmaddr
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
##############################################################################
|
2
|
+
# Indis framework #
|
3
|
+
# Copyright (C) 2012 Vladimir "Farcaller" Pouzanov <farcaller@gmail.com> #
|
4
|
+
# #
|
5
|
+
# This program is free software: you can redistribute it and/or modify #
|
6
|
+
# it under the terms of the GNU General Public License as published by #
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or #
|
8
|
+
# (at your option) any later version. #
|
9
|
+
# #
|
10
|
+
# This program is distributed in the hope that it will be useful, #
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
13
|
+
# GNU General Public License for more details. #
|
14
|
+
# #
|
15
|
+
# You should have received a copy of the GNU General Public License #
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
17
|
+
##############################################################################
|
18
|
+
|
19
|
+
require 'indis-core/binary_format'
|
20
|
+
require 'indis-core/vmmap'
|
21
|
+
|
22
|
+
module Indis
|
23
|
+
|
24
|
+
# The main entry point for indis. Target describes a given binary, performs
|
25
|
+
# format matching, loading the binary into memory and primary processing.
|
26
|
+
class Target
|
27
|
+
attr_reader :io # @return [IO] IO object for target binary
|
28
|
+
|
29
|
+
attr_reader :format # @return [Indis::BinaryFormat::Format] binary format of the target
|
30
|
+
|
31
|
+
attr_reader :vmmap # @return [Indis::VMMap] virtual memory map
|
32
|
+
|
33
|
+
attr_accessor :segments # @return [Array] list of all processed {Indis::Segment segments}
|
34
|
+
|
35
|
+
attr_accessor :symbols # @return [Array] list of all processed {Indis::Symbol symbols}
|
36
|
+
|
37
|
+
# @param [String] filename target binary file name
|
38
|
+
# @raise [AttributeError] if the file does not exist
|
39
|
+
# @raise [RuntimeError] if there is no known format for magic or there are several matching formats
|
40
|
+
def initialize(filename)
|
41
|
+
raise AttributeError, "File does not exist" unless FileTest.file?(filename)
|
42
|
+
@filename = filename
|
43
|
+
@io = StringIO.new(File.open(filename).read().force_encoding('BINARY'))
|
44
|
+
|
45
|
+
magic = @io.read(4).unpack('V')[0]
|
46
|
+
@io.seek(-4, IO::SEEK_CUR)
|
47
|
+
|
48
|
+
fmts = BinaryFormat.known_formats.map { |f| f if f.magic == magic }.compact
|
49
|
+
|
50
|
+
raise RuntimeError, "Unknown format for magic #{magic.to_s(16)}" if fmts.length == 0
|
51
|
+
raise RuntimeError, "Several possible formats: #{fmts}" if fmts.length > 1
|
52
|
+
|
53
|
+
@format = fmts.first.new(self, @io)
|
54
|
+
|
55
|
+
@vmmap = VMMap.new(self)
|
56
|
+
end
|
57
|
+
|
58
|
+
# A target can consist of several other targets (e.g. fat mach-o). In such
|
59
|
+
# a case the target is +meta+. It does not have any segments, sections or vmmap,
|
60
|
+
# but it has one or several subtargets.
|
61
|
+
# @todo implement meta targets
|
62
|
+
# @return True if the target is a meta target
|
63
|
+
def meta?
|
64
|
+
@subtargets && @subtargets.length > 0
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|