ruby_apk 0.4.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/.rspec +1 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +38 -0
- data/LICENSE.txt +22 -0
- data/README.md +106 -0
- data/Rakefile +43 -0
- data/VERSION +1 -0
- data/lib/android/apk.rb +155 -0
- data/lib/android/axml_parser.rb +178 -0
- data/lib/android/dex/access_flag.rb +74 -0
- data/lib/android/dex/dex_object.rb +435 -0
- data/lib/android/dex/info.rb +151 -0
- data/lib/android/dex/utils.rb +45 -0
- data/lib/android/dex.rb +92 -0
- data/lib/android/manifest.rb +209 -0
- data/lib/android/resource.rb +135 -0
- data/lib/android/utils.rb +55 -0
- data/lib/ruby_apk.rb +6 -0
- data/ruby_apk.gemspec +93 -0
- data/spec/apk_spec.rb +268 -0
- data/spec/axml_parser_spec.rb +46 -0
- data/spec/data/sample.apk +0 -0
- data/spec/data/sample_AndroidManifest.xml +0 -0
- data/spec/data/sample_classes.dex +0 -0
- data/spec/data/sample_resources.arsc +0 -0
- data/spec/data/sample_resources_utf16.arsc +0 -0
- data/spec/dex/access_flag_spec.rb +42 -0
- data/spec/dex/dex_object_spec.rb +103 -0
- data/spec/dex/info_spec.rb +121 -0
- data/spec/dex/utils_spec.rb +56 -0
- data/spec/dex_spec.rb +59 -0
- data/spec/manifest_spec.rb +179 -0
- data/spec/resource_spec.rb +87 -0
- data/spec/ruby_apk_spec.rb +4 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/utils_spec.rb +90 -0
- metadata +198 -0
data/lib/android/dex.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require_relative 'dex/dex_object'
|
2
|
+
require_relative 'dex/info'
|
3
|
+
require_relative 'dex/access_flag'
|
4
|
+
require_relative 'dex/utils'
|
5
|
+
|
6
|
+
module Android
|
7
|
+
# parsed dex object
|
8
|
+
# @see http://source.android.com/tech/dalvik/dex-format.html
|
9
|
+
# @attr_reader strings [Array<String>] strings in dex file.
|
10
|
+
class Dex
|
11
|
+
# @return [Dex::Header] dex header information
|
12
|
+
attr_reader :header
|
13
|
+
alias :h :header
|
14
|
+
|
15
|
+
# @return [String] dex binary data
|
16
|
+
attr_reader :data
|
17
|
+
# @return [Array<Dex::ClassInfo>] array of class information
|
18
|
+
attr_reader :classes
|
19
|
+
|
20
|
+
attr_reader :field_ids, :method_ids, :proto_ids
|
21
|
+
# @param [String] data dex binary data
|
22
|
+
def initialize(data)
|
23
|
+
@data = data
|
24
|
+
@data.force_encoding(Encoding::ASCII_8BIT)
|
25
|
+
@classes = []
|
26
|
+
parse()
|
27
|
+
end
|
28
|
+
|
29
|
+
def strings
|
30
|
+
@strings ||= @string_data_items.map{|item| item.to_s }
|
31
|
+
end
|
32
|
+
|
33
|
+
def inspect
|
34
|
+
"<Android::Dex @classes => #{@classes.size}, datasize => #{@data.size}>"
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
# @private
|
39
|
+
TYPE_DESCRIPTOR = {
|
40
|
+
'V' => 'void',
|
41
|
+
'Z' => 'boolean',
|
42
|
+
'B' => 'byte',
|
43
|
+
'S' => 'short',
|
44
|
+
'C' => 'short',
|
45
|
+
'I' => 'int',
|
46
|
+
'J' => 'long',
|
47
|
+
'F' => 'float',
|
48
|
+
'D' => 'double'
|
49
|
+
}
|
50
|
+
|
51
|
+
|
52
|
+
def type_resolve(typeid)
|
53
|
+
type = strings[@type_ids[typeid]]
|
54
|
+
if type.start_with? '['
|
55
|
+
type = type[1..type.size]
|
56
|
+
return TYPE_DESCRIPTOR.fetch(type, type) + "[]" # TODO: recursive
|
57
|
+
else
|
58
|
+
return TYPE_DESCRIPTOR.fetch(type, type)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
private
|
64
|
+
def parse
|
65
|
+
@header = DexObject::Header.new(@data)
|
66
|
+
@map_list = DexObject::MapList.new(@data, h[:map_off])
|
67
|
+
|
68
|
+
# parse strings
|
69
|
+
@string_ids = DexObject::StringIdItem.new(@data, h[:string_ids_off], h[:string_ids_size])
|
70
|
+
@string_data_items = []
|
71
|
+
@string_ids[:string_data_off].each { |off| @string_data_items << DexObject::StringDataItem.new(@data, off) }
|
72
|
+
|
73
|
+
@type_ids = DexObject::TypeIdItem.new(@data, h[:type_ids_off], h[:type_ids_size])
|
74
|
+
@proto_ids = ids_list_array(DexObject::ProtoIdItem, h[:proto_ids_off], h[:proto_ids_size])
|
75
|
+
@field_ids = ids_list_array(DexObject::FieldIdItem, h[:field_ids_off], h[:field_ids_size])
|
76
|
+
@method_ids = ids_list_array(DexObject::MethodIdItem, h[:method_ids_off], h[:method_ids_size])
|
77
|
+
@class_defs = ids_list_array(DexObject::ClassDefItem, h[:class_defs_off], h[:class_defs_size])
|
78
|
+
|
79
|
+
@classes = []
|
80
|
+
@class_defs.each do |cls_def|
|
81
|
+
@classes << ClassInfo.new(cls_def, self)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def ids_list_array(cls, offset, size)
|
86
|
+
ret_array = []
|
87
|
+
size.times { |i| ret_array << cls.new(@data, offset + cls.size * i) }
|
88
|
+
ret_array
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
module Android
|
4
|
+
# parsed AndroidManifest.xml class
|
5
|
+
# @see http://developer.android.com/guide/topics/manifest/manifest-intro.html
|
6
|
+
class Manifest
|
7
|
+
APPLICATION_TAG = '/manifest/application'
|
8
|
+
|
9
|
+
# <activity>, <service>, <receiver> or <provider> element in <application> element of the manifest file.
|
10
|
+
class Component
|
11
|
+
# component types
|
12
|
+
TYPES = ['service', 'activity', 'receiver', 'provider']
|
13
|
+
|
14
|
+
# the element is valid Component element or not
|
15
|
+
# @param [REXML::Element] elem xml element
|
16
|
+
# @return [Boolean]
|
17
|
+
def self.valid?(elem)
|
18
|
+
TYPES.include?(elem.name.downcase)
|
19
|
+
rescue => e
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [String] type string in TYPES
|
24
|
+
attr_reader :type
|
25
|
+
# @return [String] component name
|
26
|
+
attr_reader :name
|
27
|
+
# @return [Array<Manifest::IntentFilter>]
|
28
|
+
attr_reader :intent_filters
|
29
|
+
# @return [Array<Manifest::Meta>]
|
30
|
+
attr_reader :metas
|
31
|
+
|
32
|
+
# @param [REXML::Element] elem target element
|
33
|
+
# @raise [ArgumentError] when elem is invalid.
|
34
|
+
def initialize(elem)
|
35
|
+
raise ArgumentError unless Component.valid?(elem)
|
36
|
+
@type = elem.name
|
37
|
+
@name = elem.attributes['name']
|
38
|
+
@intent_filters = []
|
39
|
+
unless elem.elements['intent-filter'].nil?
|
40
|
+
elem.elements['intent-filter'].each do |e|
|
41
|
+
@intent_filters << IntentFilter.parse(e)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
@metas = []
|
45
|
+
elem.each_element('meta-data') do |e|
|
46
|
+
@metas << Meta.new(e)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# intent-filter element in components
|
52
|
+
module IntentFilter
|
53
|
+
# parse inside of intent-filter element
|
54
|
+
# @param [REXML::Element] elem target element
|
55
|
+
# @return [IntentFilter::Action, IntentFilter::Category, IntentFilter::Data]
|
56
|
+
# intent-filter element
|
57
|
+
def self.parse(elem)
|
58
|
+
case elem.name
|
59
|
+
when 'action'
|
60
|
+
Action.new(elem)
|
61
|
+
when 'category'
|
62
|
+
Category.new(elem)
|
63
|
+
when 'data'
|
64
|
+
Data.new(elem)
|
65
|
+
else
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# intent-filter action class
|
71
|
+
class Action
|
72
|
+
# @return [String] action name of intent-filter
|
73
|
+
attr_reader :name
|
74
|
+
# @return [String] action type of intent-filter
|
75
|
+
attr_reader :type
|
76
|
+
|
77
|
+
def initialize(elem)
|
78
|
+
@type = 'action'
|
79
|
+
@name = elem.attributes['name']
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# intent-filter category class
|
84
|
+
class Category
|
85
|
+
# @return [String] category name of intent-filter
|
86
|
+
attr_reader :name
|
87
|
+
# @return [String] category type of intent-filter
|
88
|
+
attr_reader :type
|
89
|
+
|
90
|
+
def initialize(elem)
|
91
|
+
@type = 'category'
|
92
|
+
@name = elem.attributes['name']
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# intent-filter data class
|
97
|
+
class Data
|
98
|
+
# @return [String]
|
99
|
+
attr_reader :type
|
100
|
+
# @return [String]
|
101
|
+
attr_reader :host
|
102
|
+
# @return [String]
|
103
|
+
attr_reader :mime_type
|
104
|
+
# @return [String]
|
105
|
+
attr_reader :path
|
106
|
+
# @return [String]
|
107
|
+
attr_reader :path_pattern
|
108
|
+
# @return [String]
|
109
|
+
attr_reader :path_prefix
|
110
|
+
# @return [String]
|
111
|
+
attr_reader :port
|
112
|
+
# @return [String]
|
113
|
+
attr_reader :scheme
|
114
|
+
|
115
|
+
def initialize(elem)
|
116
|
+
@type = 'data'
|
117
|
+
@host = elem.attributes['host']
|
118
|
+
@mime_type = elem.attributes['mimeType']
|
119
|
+
@path = elem.attributes['path']
|
120
|
+
@path_pattern = elem.attributes['pathPattern']
|
121
|
+
@path_prefix = elem.attributes['pathPrefix']
|
122
|
+
@port = elem.attributes['port']
|
123
|
+
@scheme = elem.attributes['scheme']
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# meta information class
|
129
|
+
class Meta
|
130
|
+
# @return [String]
|
131
|
+
attr_reader :name
|
132
|
+
# @return [String]
|
133
|
+
attr_reader :resource
|
134
|
+
# @return [String]
|
135
|
+
attr_reader :value
|
136
|
+
def initialize(elem)
|
137
|
+
@name = elem.attributes['name']
|
138
|
+
@resource = elem.attributes['resource']
|
139
|
+
@value = elem.attributes['value']
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
#################################
|
144
|
+
# Manifest class definitions
|
145
|
+
#################################
|
146
|
+
|
147
|
+
# @param [String] data binary data of AndroidManifest.xml
|
148
|
+
def initialize(data)
|
149
|
+
parser = AXMLParser.new(data)
|
150
|
+
@doc = parser.parse
|
151
|
+
end
|
152
|
+
|
153
|
+
# used permission array
|
154
|
+
# @return [Array<String>] permission names
|
155
|
+
# @note return empty array when the manifest includes no use-parmission element
|
156
|
+
def use_permissions
|
157
|
+
perms = []
|
158
|
+
@doc.each_element('/manifest/uses-permission') do |elem|
|
159
|
+
perms << elem.attributes['name']
|
160
|
+
end
|
161
|
+
perms.uniq
|
162
|
+
end
|
163
|
+
|
164
|
+
# @return [Array<Android::Manifest::Component>] all components in apk
|
165
|
+
# @note return empty array when the manifest include no components
|
166
|
+
def components
|
167
|
+
components = []
|
168
|
+
unless @doc.elements['/manifest/application'].nil?
|
169
|
+
@doc.elements['/manifest/application'].each do |elem|
|
170
|
+
components << Component.new(elem) if Component.valid?(elem)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
components
|
174
|
+
end
|
175
|
+
|
176
|
+
# application package name
|
177
|
+
# @return [String]
|
178
|
+
def package_name
|
179
|
+
@doc.root.attributes['package']
|
180
|
+
end
|
181
|
+
|
182
|
+
# application version code
|
183
|
+
# @return [Integer]
|
184
|
+
def version_code
|
185
|
+
@doc.root.attributes['versionCode'].to_i
|
186
|
+
end
|
187
|
+
|
188
|
+
# application version name
|
189
|
+
# @return [String]
|
190
|
+
def version_name
|
191
|
+
@doc.root.attributes['versionName']
|
192
|
+
end
|
193
|
+
|
194
|
+
# @return [Integer] minSdkVersion in uses element
|
195
|
+
def min_sdk_ver
|
196
|
+
@doc.elements['/manifest/uses-sdk'].attributes['minSdkVersion'].to_i
|
197
|
+
end
|
198
|
+
|
199
|
+
# return xml as string format
|
200
|
+
# @param [Integer] indent size(bytes)
|
201
|
+
# @return [String] raw xml string
|
202
|
+
def to_xml(indent=4)
|
203
|
+
xml =''
|
204
|
+
formatter = REXML::Formatters::Pretty.new(indent)
|
205
|
+
formatter.write(@doc.root, xml)
|
206
|
+
xml
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'stringio'
|
3
|
+
require 'csv'
|
4
|
+
require 'ruby_apk'
|
5
|
+
|
6
|
+
module Android
|
7
|
+
# based on Android OS source code
|
8
|
+
# /frameworks/base/include/utils/ResourceTypes.h
|
9
|
+
class Resource
|
10
|
+
class ChunkHeader
|
11
|
+
attr_reader :type, :header_size, :size
|
12
|
+
def initialize(data, offset)
|
13
|
+
@data = data
|
14
|
+
@offset = offset
|
15
|
+
@data_io = StringIO.new(@data, 'rb')
|
16
|
+
@data_io.seek(offset)
|
17
|
+
parse
|
18
|
+
end
|
19
|
+
private
|
20
|
+
def parse
|
21
|
+
@type = read_int16
|
22
|
+
@header_size = read_int16
|
23
|
+
@size = read_int32
|
24
|
+
end
|
25
|
+
def read_int32
|
26
|
+
@data_io.read(4).unpack('V')[0]
|
27
|
+
end
|
28
|
+
def read_int16
|
29
|
+
@data_io.read(2).unpack('v')[0]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class ResTableHeader < ChunkHeader
|
34
|
+
attr_reader :package_count
|
35
|
+
def parse
|
36
|
+
super
|
37
|
+
@package_count = read_int32
|
38
|
+
end
|
39
|
+
end
|
40
|
+
class ResStringPool < ChunkHeader
|
41
|
+
SORTED_FLAG = 1 << 0
|
42
|
+
UTF8_FLAG = 1 << 8
|
43
|
+
|
44
|
+
attr_reader :strings
|
45
|
+
private
|
46
|
+
def parse
|
47
|
+
super
|
48
|
+
@string_count = read_int32
|
49
|
+
@style_count = read_int32
|
50
|
+
@flags = read_int32
|
51
|
+
@string_start = read_int32
|
52
|
+
@style_start = read_int32
|
53
|
+
@strings = []
|
54
|
+
@string_count.times do
|
55
|
+
offset = @offset + @string_start + read_int32
|
56
|
+
if (@flags & UTF8_FLAG != 0)
|
57
|
+
# read length twice(utf16 length and utf8 length)
|
58
|
+
# const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const
|
59
|
+
u16len, o16 = ResStringPool.utf8_len(@data[offset, 2])
|
60
|
+
u8len, o8 = ResStringPool.utf8_len(@data[offset+o16, 2])
|
61
|
+
str = @data[offset+o16+o8, u8len]
|
62
|
+
@strings << str.force_encoding(Encoding::UTF_8)
|
63
|
+
else
|
64
|
+
u16len, o16 = ResStringPool.utf16_len(@data[offset, 4])
|
65
|
+
str = @data[offset+o16, u16len*2]
|
66
|
+
str.force_encoding(Encoding::UTF_16LE)
|
67
|
+
@strings << str.encode(Encoding::UTF_8)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# @note refer to /frameworks/base/libs/androidfw/ResourceTypes.cpp
|
73
|
+
# static inline size_t decodeLength(const uint8_t** str)
|
74
|
+
# @param [String] data parse target
|
75
|
+
# @return[Integer, Integer] string length and parsed length
|
76
|
+
def self.utf8_len(data)
|
77
|
+
first, second = data.unpack('CC')
|
78
|
+
if (first & 0x80) != 0
|
79
|
+
return (((first & 0x7F) << 8) + second), 2
|
80
|
+
else
|
81
|
+
return first, 1
|
82
|
+
end
|
83
|
+
end
|
84
|
+
# @note refer to /frameworks/base/libs/androidfw/ResourceTypes.cpp
|
85
|
+
# static inline size_t decodeLength(const char16_t** str)
|
86
|
+
# @param [String] data parse target
|
87
|
+
# @return[Integer, Integer] string length and parsed length
|
88
|
+
def self.utf16_len(data)
|
89
|
+
first, second = data.unpack('vv')
|
90
|
+
if (first & 0x8000) != 0
|
91
|
+
return (((first & 0x7FFF) << 16) + second), 4
|
92
|
+
else
|
93
|
+
return first, 2
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
######################################################################
|
99
|
+
def initialize(data)
|
100
|
+
data.force_encoding(Encoding::ASCII_8BIT)
|
101
|
+
@data = data
|
102
|
+
parse()
|
103
|
+
end
|
104
|
+
|
105
|
+
def strings
|
106
|
+
@string_pool.strings
|
107
|
+
end
|
108
|
+
def package_count
|
109
|
+
@res_table.package_count
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
def parse
|
114
|
+
offset = 0
|
115
|
+
|
116
|
+
while offset < @data.size
|
117
|
+
type = @data[offset, 2].unpack('v')[0]
|
118
|
+
case type
|
119
|
+
when 0x0001 # RES_STRING_POOL_TYPE
|
120
|
+
@string_pool = ResStringPool.new(@data, offset)
|
121
|
+
offset += @string_pool.size
|
122
|
+
when 0x0002 # RES_TABLE_TYPE
|
123
|
+
@res_table = ResTableHeader.new(@data, offset)
|
124
|
+
offset += @res_table.header_size
|
125
|
+
when 0x0200, 0x0201, 0x0202
|
126
|
+
# not implemented yet.
|
127
|
+
chunk = ChunkHeader.new(@data, offset)
|
128
|
+
offset += chunk.size
|
129
|
+
else
|
130
|
+
raise "chunk type error: type:%#04x" % type
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
|
2
|
+
module Android
|
3
|
+
# Utility methods
|
4
|
+
module Utils
|
5
|
+
# path is apk file or not.
|
6
|
+
# @param [String] path target file path
|
7
|
+
# @return [Boolean]
|
8
|
+
def self.apk?(path)
|
9
|
+
begin
|
10
|
+
apk = Apk.new(path)
|
11
|
+
return true
|
12
|
+
rescue => e
|
13
|
+
return false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# data is elf file or not.
|
18
|
+
# @param [String] data target data
|
19
|
+
# @return [Boolean]
|
20
|
+
def self.elf?(data)
|
21
|
+
data[0..3] == "\x7f\x45\x4c\x46"
|
22
|
+
rescue => e
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
# data is cert file or not.
|
27
|
+
# @param [String] data target data
|
28
|
+
# @return [Boolean]
|
29
|
+
def self.cert?(data)
|
30
|
+
data[0..1] == "\x30\x82"
|
31
|
+
rescue => e
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
# data is dex file or not.
|
36
|
+
# @param [String] data target data
|
37
|
+
# @return [Boolean]
|
38
|
+
def self.dex?(data)
|
39
|
+
data[0..7] == "\x64\x65\x78\x0a\x30\x33\x35\x00" # "dex\n035\0"
|
40
|
+
rescue => e
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
44
|
+
# data is valid dex file or not.
|
45
|
+
# @param [String] data target data
|
46
|
+
# @return [Boolean]
|
47
|
+
def self.valid_dex?(data)
|
48
|
+
Android::Dex.new(data)
|
49
|
+
true
|
50
|
+
rescue => e
|
51
|
+
false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
data/lib/ruby_apk.rb
ADDED
data/ruby_apk.gemspec
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "ruby_apk"
|
8
|
+
s.version = "0.4.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["SecureBrain"]
|
12
|
+
s.date = "2012-10-28"
|
13
|
+
s.description = "static analysis tool for android apk"
|
14
|
+
s.email = "info@securebrain.co.jp"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".rspec",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.md",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"lib/android/apk.rb",
|
28
|
+
"lib/android/axml_parser.rb",
|
29
|
+
"lib/android/dex.rb",
|
30
|
+
"lib/android/dex/access_flag.rb",
|
31
|
+
"lib/android/dex/dex_object.rb",
|
32
|
+
"lib/android/dex/info.rb",
|
33
|
+
"lib/android/dex/utils.rb",
|
34
|
+
"lib/android/manifest.rb",
|
35
|
+
"lib/android/resource.rb",
|
36
|
+
"lib/android/utils.rb",
|
37
|
+
"lib/ruby_apk.rb",
|
38
|
+
"ruby_apk.gemspec",
|
39
|
+
"spec/apk_spec.rb",
|
40
|
+
"spec/axml_parser_spec.rb",
|
41
|
+
"spec/data/sample.apk",
|
42
|
+
"spec/data/sample_AndroidManifest.xml",
|
43
|
+
"spec/data/sample_classes.dex",
|
44
|
+
"spec/data/sample_resources.arsc",
|
45
|
+
"spec/data/sample_resources_utf16.arsc",
|
46
|
+
"spec/dex/access_flag_spec.rb",
|
47
|
+
"spec/dex/dex_object_spec.rb",
|
48
|
+
"spec/dex/info_spec.rb",
|
49
|
+
"spec/dex/utils_spec.rb",
|
50
|
+
"spec/dex_spec.rb",
|
51
|
+
"spec/manifest_spec.rb",
|
52
|
+
"spec/resource_spec.rb",
|
53
|
+
"spec/ruby_apk_spec.rb",
|
54
|
+
"spec/spec_helper.rb",
|
55
|
+
"spec/utils_spec.rb"
|
56
|
+
]
|
57
|
+
s.homepage = "http://www.securebrain.co.jp/"
|
58
|
+
s.licenses = ["MIT"]
|
59
|
+
s.require_paths = ["lib"]
|
60
|
+
s.rubygems_version = "1.8.24"
|
61
|
+
s.summary = "static analysis tool for android apk"
|
62
|
+
|
63
|
+
if s.respond_to? :specification_version then
|
64
|
+
s.specification_version = 3
|
65
|
+
|
66
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
67
|
+
s.add_runtime_dependency(%q<rubyzip>, [">= 0"])
|
68
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.11.0"])
|
69
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.1.5"])
|
70
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
71
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
72
|
+
s.add_development_dependency(%q<redcarpet>, [">= 0"])
|
73
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
74
|
+
else
|
75
|
+
s.add_dependency(%q<rubyzip>, [">= 0"])
|
76
|
+
s.add_dependency(%q<rspec>, ["~> 2.11.0"])
|
77
|
+
s.add_dependency(%q<bundler>, ["~> 1.1.5"])
|
78
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
79
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
80
|
+
s.add_dependency(%q<redcarpet>, [">= 0"])
|
81
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
82
|
+
end
|
83
|
+
else
|
84
|
+
s.add_dependency(%q<rubyzip>, [">= 0"])
|
85
|
+
s.add_dependency(%q<rspec>, ["~> 2.11.0"])
|
86
|
+
s.add_dependency(%q<bundler>, ["~> 1.1.5"])
|
87
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
88
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
89
|
+
s.add_dependency(%q<redcarpet>, [">= 0"])
|
90
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|