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