indis-macho 0.2.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.
@@ -0,0 +1,143 @@
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
+ module MachO
21
+
22
+ class Symbol
23
+ STAB_MASK = 0xe0
24
+ PEXT_MASK = 0x10
25
+ TYPE_MASK = 0x0e
26
+ EXT_MASK = 0x01
27
+ TYPE = {
28
+ 0x0 => :UNDEF,
29
+ 0x2 => :ABS,
30
+ 0xe => :SECT,
31
+ 0xc => :PBUD,
32
+ 0xa => :INDR,
33
+ }
34
+ STAB = {
35
+ 0x20 => :N_GSYM,
36
+ 0x22 => :N_FNAME,
37
+ 0x24 => :N_FUN,
38
+ 0x26 => :N_STSYM,
39
+ 0x28 => :N_LCSYM,
40
+ 0x2e => :N_BNSYM,
41
+ 0x3c => :N_OPT,
42
+ 0x40 => :N_RSYM,
43
+ 0x44 => :N_SLINE,
44
+ 0x4e => :N_ENSYM,
45
+ 0x60 => :N_SSYM,
46
+ 0x64 => :N_SO,
47
+ 0x66 => :N_OSO,
48
+ 0x80 => :N_LSYM,
49
+ 0x82 => :N_BINCL,
50
+ 0x84 => :N_SOL,
51
+ 0x86 => :N_PARAMS,
52
+ 0x88 => :N_VERSION,
53
+ 0x8A => :N_OLEVEL,
54
+ 0xa0 => :N_PSYM,
55
+ 0xa2 => :N_EINCL,
56
+ 0xa4 => :N_ENTRY,
57
+ 0xc0 => :N_LBRAC,
58
+ 0xc2 => :N_EXCL,
59
+ 0xe0 => :N_RBRAC,
60
+ 0xe2 => :N_BCOMM,
61
+ 0xe4 => :N_ECOMM,
62
+ 0xe8 => :N_ECOML,
63
+ 0xfe => :N_LENG,
64
+ }
65
+ REFERENCE_TYPE_MASK = 0xf
66
+ DESC_REFERENCE = {
67
+ 0x0 => :REFERENCE_FLAG_UNDEFINED_NON_LAZY,
68
+ 0x1 => :REFERENCE_FLAG_UNDEFINED_LAZY,
69
+ 0x2 => :REFERENCE_FLAG_DEFINED,
70
+ 0x3 => :REFERENCE_FLAG_PRIVATE_DEFINED,
71
+ 0x4 => :REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY,
72
+ 0x5 => :REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY,
73
+ }
74
+ DESC_ADDITIONAL = {
75
+ 0x08 => :N_ARM_THUMB_DEF,
76
+ 0x10 => :REFERENCED_DYNAMICALLY,
77
+ 0x20 => :N_DESC_DISCARDED_OR_N_NO_DEAD_STRIP, # TODO: resolve mach file type
78
+ 0x40 => :N_WEAK_REF,
79
+ 0x80 => :N_WEAK_DEF,
80
+ }
81
+ LIBRARY_ORDINAL = {
82
+ 0x0 => :SELF_LIBRARY_ORDINAL,
83
+ 0xfe => :DYNAMIC_LOOKUP_ORDINAL,
84
+ 0xff => :EXECUTABLE_ORDINAL,
85
+ }
86
+
87
+ attr_reader :name, :sect, :value
88
+
89
+ def initialize(payload, strtab)
90
+ name_idx, @type_val, @sect, @desc_val, @value = payload.read(12).unpack('VCCSV')
91
+ if name_idx == 0
92
+ @name = ''
93
+ else
94
+ @name = strtab[name_idx..-1].split("\0", 2)[0]
95
+ end
96
+
97
+ #puts "#{printf('%08x', value)} #{@name} sect #{@sect} #{self.type ? self.type : 'UNK 0b'+@type_val.to_s(2)} #{self.stab? ? 'stab ' : ''} #{self.private_extern? ? 'pvt ' : ''} #{self.extern? ? 'extern' : ''}"
98
+ end
99
+
100
+ def type
101
+ if stab?
102
+ STAB[@type_val]
103
+ else
104
+ TYPE[@type_val & TYPE_MASK]
105
+ end
106
+ end
107
+
108
+ def stab?
109
+ @type_val & STAB_MASK != 0
110
+ end
111
+
112
+ def stab
113
+ if stab?
114
+ STAB[@type_val]
115
+ else
116
+ nil
117
+ end
118
+ end
119
+
120
+ def private_extern?
121
+ @type_val & PEXT_MASK == PEXT_MASK
122
+ end
123
+
124
+ def extern?
125
+ @type_val & EXT_MASK == EXT_MASK
126
+ end
127
+
128
+ def desc
129
+ d = [DESC_REFERENCE[@desc_val & REFERENCE_TYPE_MASK]]
130
+ DESC_ADDITIONAL.each_pair do |k, v|
131
+ d << v if @desc_val & k == k
132
+ end
133
+ d
134
+ end
135
+
136
+ def twolevel_library_ordinal
137
+ lo = (@desc_val >> 8) & 0xff
138
+ LIBRARY_ORDINAL[lo] || lo
139
+ end
140
+ end
141
+
142
+ end
143
+ end
@@ -0,0 +1,23 @@
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
+ module MachO
21
+ VERSION = '0.2.0'
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ require 'indis-macho'
2
+
3
+ describe Indis::BinaryFormat do
4
+ it "should autoload Mach-O format" do
5
+ Indis::BinaryFormat.known_formats.should include(Indis::BinaryFormat::MachO)
6
+ end
7
+ end
@@ -0,0 +1,100 @@
1
+ require 'indis-macho'
2
+
3
+ def macho_target_double(*args)
4
+ o = args.length == 1 ? args[0] : {}
5
+
6
+ target = double("Target")
7
+ target.stub(:segments=)
8
+ if o[:segments]
9
+ target.should_receive(:segments){ o[:segments] }.any_number_of_times
10
+ else
11
+ target.should_receive(:segments).any_number_of_times.and_return([])
12
+ end
13
+ target.stub(:symbols=)
14
+ if o[:symbols]
15
+ target.should_receive(:symbols){ o[:symbols] }.any_number_of_times
16
+ else
17
+ target.should_receive(:symbols).any_number_of_times.and_return({})
18
+ end
19
+ target.stub(:vamap=)
20
+ target.stub(:io).and_return(o[:io])
21
+ target
22
+ end
23
+
24
+ describe Indis::BinaryFormat::MachO do
25
+ it "should parse mach-o header" do
26
+ io = StringIO.new(File.open('spec/fixtures/single-object.o', 'rb').read().force_encoding('BINARY'))
27
+ target = macho_target_double(io: io)
28
+
29
+ m = Indis::BinaryFormat::MachO.new(target, io)
30
+
31
+ m.cputype.should == :CPU_TYPE_ARM
32
+ m.cpusubtype.should == :CPU_SUBTYPE_ARM_V4T
33
+ m.filetype.should == :MH_OBJECT
34
+ m.flags.should == [:MH_SUBSECTIONS_VIA_SYMBOLS]
35
+
36
+ io = StringIO.new(File.open('spec/fixtures/app-arm-release.o', 'rb').read().force_encoding('BINARY'))
37
+ target = macho_target_double(io: io)
38
+
39
+ m = Indis::BinaryFormat::MachO.new(target, io)
40
+
41
+ m.cputype.should == :CPU_TYPE_ARM
42
+ m.cpusubtype.should == :CPU_SUBTYPE_ARM_V7
43
+ m.filetype.should == :MH_EXECUTE
44
+ m.flags.should == [:MH_NOUNDEFS, :MH_DYLDLINK, :MH_TWOLEVEL, :MH_PIE]
45
+ end
46
+
47
+ it "should parse mach-o commands" do
48
+ io = StringIO.new(File.open('spec/fixtures/single-object.o', 'rb').read().force_encoding('BINARY'))
49
+ target = macho_target_double(io: io)
50
+
51
+ m = Indis::BinaryFormat::MachO.new(target, io)
52
+
53
+ m.commands.length.should == 3
54
+ c = m.commands.first
55
+ c.cmd.should == :LC_SEGMENT
56
+ c.class.should == Indis::MachO::SegmentCommand
57
+ c.length.should == 464
58
+
59
+ c.segname.should == ''
60
+ c.vmaddr.should == 0x0
61
+ c.vmsize.should == 0x79
62
+ c.fileoff.should == 596
63
+ c.filesize.should == 121
64
+ c.maxprot.should == 0x7
65
+ c.initprot.should == 0x7
66
+ c.nsects.should == 6
67
+ c.flags.should == 0x0
68
+
69
+ c.sections.length.should == 6
70
+ s = c.sections.first
71
+ s.sectname.should == '__text'
72
+ s.segname.should == '__TEXT'
73
+ end
74
+
75
+ it "should parse stub segment from .o file" do
76
+ seg = []
77
+
78
+ io = StringIO.new(File.open('spec/fixtures/single-object.o', 'rb').read().force_encoding('BINARY'))
79
+ target = macho_target_double(io: io, segments: seg)
80
+
81
+ m = Indis::BinaryFormat::MachO.new(target, io)
82
+
83
+ seg.length.should == 1
84
+ seg[0].name.should == '__TEXT'
85
+ seg[0].sections.length.should == 6
86
+ seg[0].sections[0].name.should == '__text'
87
+ end
88
+
89
+ it "should parse symbols" do
90
+ sym = {}
91
+ io = StringIO.new(File.open('spec/fixtures/single-object.o', 'rb').read().force_encoding('BINARY'))
92
+ target = macho_target_double(io: io, symbols: sym)
93
+
94
+ m = Indis::BinaryFormat::MachO.new(target, io)
95
+
96
+ sym.keys.should include("_add")
97
+ sym.keys.should include("_sub")
98
+ sym.keys.should include("_printf")
99
+ end
100
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ RSpec.configure do |config|
5
+ config.treat_symbols_as_metadata_keys_with_true_values = true
6
+ config.run_all_when_everything_filtered = true
7
+ config.filter_run :focus
8
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: indis-macho
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Vladimir Pouzanov
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: indis-core
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Mach-o format processor for indis provides support for loading mach-o
47
+ binaries for analysis
48
+ email:
49
+ - farcaller@gmail.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - .rspec
56
+ - .travis.yml
57
+ - Gemfile
58
+ - Gemfile.ci
59
+ - LICENSE
60
+ - README.md
61
+ - Rakefile
62
+ - indis-macho.gemspec
63
+ - lib/indis-macho.rb
64
+ - lib/indis-macho/command.rb
65
+ - lib/indis-macho/symbol.rb
66
+ - lib/indis-macho/version.rb
67
+ - spec/fixtures/app-arm-release.o
68
+ - spec/fixtures/single-object.o
69
+ - spec/indis-macho/binary_format_spec.rb
70
+ - spec/indis-macho/macho_spec.rb
71
+ - spec/spec_helper.rb
72
+ homepage: http://www.indis.org/
73
+ licenses:
74
+ - GPL-3
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ! '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 1.8.21
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: Mach-o format processor for indis
97
+ test_files:
98
+ - spec/fixtures/app-arm-release.o
99
+ - spec/fixtures/single-object.o
100
+ - spec/indis-macho/binary_format_spec.rb
101
+ - spec/indis-macho/macho_spec.rb
102
+ - spec/spec_helper.rb
103
+ has_rdoc: