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