indis-macho 0.2.0

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