ruby_apk 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,4 +1,7 @@
1
1
  # ChangeLog
2
+ ## 0.5.0
3
+ * implement Android::Resource#find, #res_readable_id, #res_hex_id methods
4
+
2
5
  ## 0.4.2
3
6
  * fix bugs(#2, #3)
4
7
  * divide change log from readme
data/README.md CHANGED
@@ -5,7 +5,7 @@ Android Apk static analysis library for Ruby.
5
5
  - ruby(>=1.9.x)
6
6
 
7
7
  ## Install
8
- $ gem install ruby_apk
8
+ $ gem install ruby_apk
9
9
 
10
10
  ## Usage
11
11
  ### Initialize
@@ -72,6 +72,22 @@ end
72
72
  rsc = Android::Resource.new(rsc_data)
73
73
  ```
74
74
 
75
+ ### Resolve resource id
76
+ This feature supports only srting resources for now.
77
+
78
+ ```ruby
79
+ apk = Android::Apk.new('sample.apk')
80
+ # assigns readable resource id
81
+ puts rsc.find('@string/app_name') # => 'application name'
82
+
83
+ # assigns hex resource id
84
+ puts rsc.find('@0x7f040000') # => 'application name'
85
+
86
+ # you can set lang attribute.
87
+ puts rsc.find('@0x7f040000', :lang => 'ja')
88
+ ```
89
+
90
+
75
91
  ### Dex
76
92
  #### Extract dex information
77
93
  ```ruby
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.2
1
+ 0.5.0
@@ -1,26 +1,22 @@
1
1
  # encoding: utf-8
2
2
  require 'stringio'
3
- require 'csv'
4
- require 'ruby_apk'
5
3
 
6
4
  module Android
7
5
  # based on Android OS source code
8
6
  # /frameworks/base/include/utils/ResourceTypes.h
7
+ # @see http://justanapplication.wordpress.com/category/android/android-resources/
9
8
  class Resource
10
- class ChunkHeader
11
- attr_reader :type, :header_size, :size
9
+ class Chunk
12
10
  def initialize(data, offset)
13
11
  @data = data
14
12
  @offset = offset
13
+ exec_parse
14
+ end
15
+ def exec_parse
15
16
  @data_io = StringIO.new(@data, 'rb')
16
- @data_io.seek(offset)
17
+ @data_io.seek(@offset)
17
18
  parse
18
- end
19
- private
20
- def parse
21
- @type = read_int16
22
- @header_size = read_int16
23
- @size = read_int32
19
+ @data_io.close
24
20
  end
25
21
  def read_int32
26
22
  @data_io.read(4).unpack('V')[0]
@@ -28,6 +24,22 @@ module Android
28
24
  def read_int16
29
25
  @data_io.read(2).unpack('v')[0]
30
26
  end
27
+ def read_int8
28
+ @data_io.read(1).ord
29
+ end
30
+ def current_position
31
+ @data_io.pos
32
+ end
33
+ end
34
+
35
+ class ChunkHeader < Chunk
36
+ attr_reader :type, :header_size, :size
37
+ private
38
+ def parse
39
+ @type = read_int16
40
+ @header_size = read_int16
41
+ @size = read_int32
42
+ end
31
43
  end
32
44
 
33
45
  class ResTableHeader < ChunkHeader
@@ -37,6 +49,7 @@ module Android
37
49
  @package_count = read_int32
38
50
  end
39
51
  end
52
+
40
53
  class ResStringPool < ChunkHeader
41
54
  SORTED_FLAG = 1 << 0
42
55
  UTF8_FLAG = 1 << 8
@@ -95,37 +108,407 @@ module Android
95
108
  end
96
109
  end
97
110
 
111
+ class ResTablePackage < ChunkHeader
112
+ attr_reader :name
113
+
114
+ def global_string_pool=(pool)
115
+ @global_string_pool = pool
116
+ extract_res_strings
117
+ end
118
+
119
+ # find resource by resource id
120
+ # @param [String] res_id (like '@0x7f010001' or '@string/key')
121
+ # @param [Hash] opts option
122
+ # @option opts [String] :lang language code like 'ja', 'cn'...
123
+ # @option opts [String] :contry cantry code like 'jp'...
124
+ # @raise [ArgumentError] invalid id format
125
+ # @note
126
+ # This method only support string resource for now.
127
+ # @note
128
+ # Always return nil if assign not string type res id.
129
+ #
130
+ def find(res_id, opts={})
131
+ hex_id = strid2int(res_id)
132
+ tid = ((hex_id&0xff0000) >>16)
133
+ key = hex_id&0xffff
134
+
135
+ if type(tid) == 'string'
136
+ return find_res_string(key, opts)
137
+ else
138
+ nil
139
+ end
140
+ end
141
+
142
+ def find_res_string(key, opts={})
143
+ unless opts[:lang].nil?
144
+ string = @res_strings_lang[opts[:lang]]
145
+ end
146
+ unless opts[:contry].nil?
147
+ string = @res_strings_contry[opts[:contry]]
148
+ end
149
+ string = @res_strings_default if string.nil?
150
+ raise NotFoundError unless string.has_key? key
151
+ return string[key]
152
+ end
153
+ private :find_res_string
154
+
155
+ # convert string resource id to fixnum
156
+ # @param [String] res_id (like '@0x7f010001' or '@string/key')
157
+ # @return [Fixnum] integer id (like 0x7f010001)
158
+ # @raise [ArgumentError] invalid format
159
+ def strid2int(res_id)
160
+ case res_id
161
+ when /^@?0x[0-9a-fA-F]{8}$/
162
+ return res_id.sub(/^@/,'').to_i(16)
163
+ when /^@?\w+\/\w+/
164
+ return res_hex_id(res_id).sub(/^@/,'').to_i(16)
165
+ else
166
+ raise ArgumentError
167
+ end
168
+ end
169
+
170
+ def res_readable_id(hex_id)
171
+ if hex_id.kind_of? String
172
+ hex_id = hex_id.sub(/^@/,'').to_i(16)
173
+ end
174
+ tid = ((hex_id&0xff0000) >>16)
175
+ key = hex_id&0xffff
176
+ raise NotFoundError if !@types.has_key?(tid) || @types[tid][0][key].nil?
177
+ keyid= @types[tid][0][key].key # ugh!
178
+ "@#{type(tid)}/#{key(keyid)}"
179
+ end
180
+ def res_hex_id(readable_id, opt={})
181
+ dummy, typestr, keystr = readable_id.match(/^@?(\w+)\/(\w+)$/).to_a
182
+ tid = type_id(typestr)
183
+ raise NotFoundError unless @types.has_key?(tid)
184
+ keyid = @types[tid][0].keys[keystr]
185
+ raise NotFoundError if keyid.nil?
186
+ "@0x7f%02x%04x" % [tid, keyid]
187
+ end
188
+
189
+ def type_strings
190
+ @type_strings.strings
191
+ end
192
+ def type(id)
193
+ type_strings[id-1]
194
+ end
195
+ def type_id(str)
196
+ raise NotFoundError unless type_strings.include? str
197
+ type_strings.index(str) + 1
198
+ end
199
+ def key_strings
200
+ @key_strings.strings
201
+ end
202
+ def key(id)
203
+ key_strings[id]
204
+ end
205
+ def key_id(str)
206
+ raise NotFoundError unless key_strings.include? str
207
+ key_strings.index(str)
208
+ end
209
+
210
+ def parse
211
+ super
212
+ @id = read_int32
213
+ @name = @data_io.read(256).force_encoding(Encoding::UTF_16LE)
214
+ @name.encode!(Encoding::UTF_8).strip!
215
+ type_strings_offset = read_int32
216
+ @type_strings = ResStringPool.new(@data, @offset + type_strings_offset)
217
+ @last_public_type = read_int32
218
+ key_strings_offset = read_int32
219
+ @key_strings = ResStringPool.new(@data, @offset + key_strings_offset)
220
+ @last_public_key = read_int32
221
+
222
+ offset = @offset + key_strings_offset + @key_strings.size
223
+
224
+ @types = {}
225
+ @specs = {}
226
+ while offset < (@offset + @size)
227
+ type = @data[offset, 2].unpack('v')[0]
228
+ case type
229
+ when 0x0201 # RES_TABLE_TYPE_TYPE
230
+ type = ResTableType.new(@data, offset, self)
231
+ offset += type.size
232
+ @types[type.id] = [] if @types[type.id].nil?
233
+ @types[type.id] << type
234
+ when 0x0202 # RES_TABLE_TYPE_SPEC_TYPE`
235
+ spec = ResTableTypeSpec.new(@data, offset)
236
+ offset += spec.size
237
+ @specs[spec.id] = [] if @specs[spec.id].nil?
238
+ @specs[spec.id] << spec
239
+ else
240
+ raise "chunk type error: type:%#04x" % type
241
+ end
242
+ end
243
+ end
244
+ private :parse
245
+
246
+ def extract_res_strings
247
+ @res_strings_lang = {}
248
+ @res_strings_contry = {}
249
+ begin
250
+ type = type_id('string')
251
+ rescue NotFoundError
252
+ return
253
+ end
254
+ @types[type_id('string')].each do |type|
255
+ str_hash = {}
256
+ type.entry_count.times do |i|
257
+ entry = type[i]
258
+ if entry.nil?
259
+ str_hash[i] = nil
260
+ else
261
+ str_hash[i] = @global_string_pool.strings[type[i].val.data]
262
+ end
263
+ end
264
+ lang = type.config.locale_lang
265
+ contry = type.config.locale_contry
266
+ if lang.nil? && contry.nil?
267
+ @res_strings_default = str_hash
268
+ else
269
+ @res_strings_lang[lang] = str_hash unless lang.nil?
270
+ @res_strings_contry[contry] = str_hash unless contry.nil?
271
+ end
272
+ end
273
+ end
274
+ private :extract_res_strings
275
+
276
+ def inspect
277
+ "<ResTablePackage offset:%#08x, size:%#x, name:\"%s\">" % [@offset, @size, @name]
278
+ end
279
+ end
280
+
281
+ class ResTableType < ChunkHeader
282
+ attr_reader :id, :entry_count, :entry_start, :config
283
+ attr_reader :keys
284
+
285
+ def initialize(data, offset, pkg)
286
+ @pkg = pkg
287
+ super(data, offset)
288
+ end
289
+ # @param [String] index key name
290
+ # @param [Fixnum] index key index
291
+ # @return [ResTableEntry]
292
+ # @return [ResTableMapEntry]
293
+ # @return nil if entry index is NO_ENTRY(0xFFFFFFFF)
294
+ def [](index)
295
+ @entries[index]
296
+ end
297
+
298
+ def parse
299
+ super
300
+ @id = read_int8
301
+ res0 = read_int8 # must be 0.(maybe 4byte align)
302
+ res1 = read_int16 # must be 0.(maybe 4byte align)
303
+ @entry_count = read_int32
304
+ @entry_start = read_int32
305
+ @config = ResTableConfig.new(@data, current_position)
306
+ @data_io.seek(@config.size, IO::SEEK_CUR)
307
+
308
+ @entries = []
309
+ @keys = {}
310
+ @entry_count.times do |i|
311
+ entry_index = read_int32
312
+ if entry_index == ResTableEntry::NO_ENTRY
313
+ @entries << nil
314
+ else
315
+ entry = ResTableEntry.read_entry(@data, @offset + @entry_start + entry_index)
316
+ @entries << entry
317
+ @keys[@pkg.key(entry.key)] = i
318
+ end
319
+ end
320
+ end
321
+ private :parse
322
+
323
+
324
+ def inspect
325
+ "<ResTableType offset:0x#{@offset.to_s(16)}, id:#{@id}, " +
326
+ "count:#{@entry_count}, start:0x#{@entry_start.to_s(16)}>"
327
+ end
328
+ end
329
+
330
+ class ResTableConfig < Chunk
331
+ attr_reader :size, :imei, :locale_lang, :locale_contry, :input
332
+ attr_reader :screen_input, :version, :screen_config
333
+ def parse
334
+ @size = read_int32
335
+ @imei = read_int32
336
+ la = @data_io.read(2)
337
+ @locale_lang = la unless la == "\x00\x00"
338
+ cn = @data_io.read(2)
339
+ @locale_contry = cn unless cn == "\x00\x00"
340
+ @screen_type = read_int32
341
+ @input = read_int32
342
+ @screen_input = read_int32
343
+ @version = read_int32
344
+ @screen_config = read_int32
345
+ end
346
+ def inspect
347
+ "<ResTableConfig size:#{@size}, imei:#{@imei}, la:'#{@locale_lang}' cn:'#{@locale_contry}'"
348
+ end
349
+ end
350
+
351
+ class ResTableTypeSpec < ChunkHeader
352
+ attr_reader :id, :entry_count
353
+
354
+ def parse
355
+ super
356
+ @id = read_int8
357
+ res0 = read_int8 # must be 0.(maybe 4byte align)
358
+ res1 = read_int16 # must be 0.(maybe 4byte align)
359
+ @entry_count = read_int32
360
+ end
361
+ private :parse
362
+
363
+ def inspect
364
+ "<ResTableTypeSpec id:#{@id} entry count:#{@entry_count}>"
365
+ end
366
+ end
367
+ class ResTableEntry < Chunk
368
+ NO_ENTRY = 0xFFFFFFFF
369
+
370
+ # @return [ResTableEntry] if not set FLAG_COMPLEX
371
+ # @return [ResTableMapEntry] if not set FLAG_COMPLEX
372
+ def self.read_entry(data, offset)
373
+ flag = data[offset + 2, 2].unpack('v')[0]
374
+ if flag & ResTableEntry::FLAG_COMPLEX == 0
375
+ ResTableEntry.new(data, offset)
376
+ else
377
+ ResTableMapEntry.new(data, offset)
378
+ end
379
+ end
380
+
381
+ # If set, this is a complex entry, holding a set of name/value
382
+ # mappings. It is followed by an array of ResTable_map structures.
383
+ FLAG_COMPLEX = 0x01
384
+ # If set, this resource has been declared public, so libraries
385
+ # are allowed to reference it.
386
+ FLAG_PUBLIC = 0x02
387
+
388
+ attr_reader :size, :key, :val
389
+ def parse
390
+ @size = read_int16
391
+ @flag = read_int16
392
+ @key = read_int32 # RefStringPool_key
393
+ @val = ResValue.new(@data, current_position)
394
+ end
395
+ private :parse
396
+
397
+ def inspect
398
+ "<ResTableEntry @size=#{@size}, @key=#{@key} @flag=#{@flag}>"
399
+ end
400
+ end
401
+ class ResTableMapEntry < ResTableEntry
402
+ attr_reader :parent, :count
403
+ def parse
404
+ super
405
+ # resource identifier of the parent mapping, 0 if there is none.
406
+ @parent = read_int32
407
+ # number of name/value pairs that follw for FLAG_COMPLEX
408
+ @count = read_int32
409
+ # TODO: implement read ResTableMap object
410
+ end
411
+ private :parse
412
+ end
413
+ class ResTableMap < Chunk
414
+ def size
415
+ @val.size + 4
416
+ end
417
+ def parse
418
+ @name = read_int32
419
+ @val = ResValue.new(@data, current_position)
420
+ end
421
+ end
422
+
423
+ class ResValue < Chunk
424
+ attr_reader :size, :data_type, :data
425
+ def parse
426
+ @size = read_int16
427
+ res0 = read_int8 # Always set 0.
428
+ @data_type = read_int8
429
+ @data = read_int32
430
+ end
431
+ private :parse
432
+ end
433
+
98
434
  ######################################################################
435
+ # @returns [Hash] { name(String) => value(ResTablePackage) }
436
+ attr_reader :packages
437
+
99
438
  def initialize(data)
100
439
  data.force_encoding(Encoding::ASCII_8BIT)
101
440
  @data = data
102
441
  parse()
103
442
  end
104
443
 
444
+
445
+ # @return [Array<String>] all strings defined in arsc.
105
446
  def strings
106
447
  @string_pool.strings
107
448
  end
449
+
450
+ # @return [Fixnum] number of packages
108
451
  def package_count
109
452
  @res_table.package_count
110
453
  end
111
454
 
455
+ # This method only support string resource for now.
456
+ # find resource by resource id
457
+ # @param [String] res_id (like '@0x7f010001' or '@string/key')
458
+ # @param [Hash] opts option
459
+ # @option opts [String] :lang language code like 'ja', 'cn'...
460
+ # @option opts [String] :contry cantry code like 'jp'...
461
+ # @raise [ArgumentError] invalid id format
462
+ # @note
463
+ # This method only support string resource for now.
464
+ # @note
465
+ # Always return nil if assign not string type res id.
466
+ # @since 0.5.0
467
+ def find(rsc_id, opt={})
468
+ first_pkg.find(rsc_id, opt)
469
+ end
470
+
471
+ # @param [String] hex_id hexoctet format resource id('@0x7f010001')
472
+ # @return [String] readable resource id ('@string/key')
473
+ # @since 0.5.0
474
+ def res_readable_id(hex_id)
475
+ first_pkg.res_readable_id(hex_id)
476
+ end
477
+
478
+ # convert readable resource id to hex id
479
+ # @param [String] readable_id readable resource id ('@string/key')
480
+ # @return [String] hexoctet format resource id('@0x7f010001')
481
+ # @since 0.5.0
482
+ def res_hex_id(readable_id)
483
+ first_pkg.res_hex_id(readable_id)
484
+ end
485
+
112
486
  private
487
+ def first_pkg
488
+ @packages.first[1]
489
+ end
113
490
  def parse
114
491
  offset = 0
115
492
 
116
493
  while offset < @data.size
117
494
  type = @data[offset, 2].unpack('v')[0]
495
+ #print "[%#08x] " % offset
496
+ @packages = {}
118
497
  case type
119
498
  when 0x0001 # RES_STRING_POOL_TYPE
120
499
  @string_pool = ResStringPool.new(@data, offset)
121
500
  offset += @string_pool.size
501
+ #puts "RES_STRING_POOL_TYPE %#x, %#x" % [@string_pool.size, offset]
122
502
  when 0x0002 # RES_TABLE_TYPE
503
+ #puts "RES_TABLE_TYPE"
123
504
  @res_table = ResTableHeader.new(@data, offset)
124
505
  offset += @res_table.header_size
125
- when 0x0200, 0x0201, 0x0202
126
- # not implemented yet.
127
- chunk = ChunkHeader.new(@data, offset)
128
- offset += chunk.size
506
+ when 0x0200 # RES_TABLE_PACKAGE_TYPE
507
+ #puts "RES_TABLE_PACKAGE_TYPE"
508
+ pkg = ResTablePackage.new(@data, offset)
509
+ pkg.global_string_pool = @string_pool
510
+ offset += pkg.size
511
+ @packages[pkg.name] = pkg
129
512
  else
130
513
  raise "chunk type error: type:%#04x" % type
131
514
  end
data/ruby_apk.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "ruby_apk"
8
- s.version = "0.4.2"
8
+ s.version = "0.5.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["SecureBrain"]
12
- s.date = "2012-12-19"
12
+ s.date = "2013-01-04"
13
13
  s.description = "static analysis tool for android apk"
14
14
  s.email = "info@securebrain.co.jp"
15
15
  s.extra_rdoc_files = [
@@ -44,6 +44,7 @@ Gem::Specification.new do |s|
44
44
  "spec/data/sample_classes.dex",
45
45
  "spec/data/sample_resources.arsc",
46
46
  "spec/data/sample_resources_utf16.arsc",
47
+ "spec/data/str_resources.arsc",
47
48
  "spec/dex/access_flag_spec.rb",
48
49
  "spec/dex/dex_object_spec.rb",
49
50
  "spec/dex/info_spec.rb",
@@ -58,7 +59,7 @@ Gem::Specification.new do |s|
58
59
  s.homepage = "https://github.com/SecureBrain/ruby_apk/"
59
60
  s.licenses = ["MIT"]
60
61
  s.require_paths = ["lib"]
61
- s.rubygems_version = "1.8.24"
62
+ s.rubygems_version = "1.8.23"
62
63
  s.summary = "static analysis tool for android apk"
63
64
 
64
65
  if s.respond_to? :specification_version then
Binary file
@@ -83,5 +83,74 @@ describe Android::Resource do
83
83
  end
84
84
  end
85
85
  end
86
+
87
+ context 'with str_resources.arsc data' do
88
+ let(:res_data) { File.read(File.expand_path(File.dirname(__FILE__) + '/data/str_resources.arsc')) }
89
+ subject { resource }
90
+ describe '#packages' do
91
+ subject {resource.packages}
92
+ it { should be_instance_of Hash}
93
+ it { subject.size.should eq 1 }
94
+ end
95
+ describe 'ResTablePackage' do
96
+ subject { resource.packages.first[1] }
97
+ it { subject.type(1).should eq 'attr' }
98
+ it { subject.type(4).should eq 'string' }
99
+ it { subject.name.should eq 'com.example.sample.ruby_apk' }
100
+ end
101
+ describe '#find' do
102
+ it '@0x7f040000 should return "sample.ruby_apk"' do
103
+ subject.find('@0x7f040000').should eq 'sample application'
104
+ end
105
+ it '@string/app_name should return "sample.ruby_apk"' do
106
+ subject.find('@string/app_name').should eq 'sample application'
107
+ end
108
+ it '@string/hello_world should return "Hello world!"' do
109
+ subject.find('@string/hello_world').should eq 'Hello world!'
110
+ end
111
+ it '@string/app_name should return "sample.ruby_apk"' do
112
+ subject.find('@string/app_name').should eq 'sample application'
113
+ end
114
+ it '@string/app_name with {:lang => "ja"} should return "サンプルアプリ"' do
115
+ subject.find('@string/app_name', :lang => 'ja').should eq 'サンプルアプリ'
116
+ end
117
+ it '@string/hello_world with {:lang => "ja"} should return nil' do
118
+ subject.find('@string/hello_world', :lang => 'ja').should be_nil
119
+ end
120
+ context 'assigns not exist string resource id' do
121
+ it { expect {subject.find('@string/not_exist') }.to raise_error Android::NotFoundError }
122
+ it { expect {subject.find('@0x7f040033') }.to raise_error Android::NotFoundError }
123
+ end
124
+ context 'assigns not string resource id' do
125
+ it { subject.find('@layout/activity_main').should be_nil }
126
+ end
127
+ context 'assigns invalid format id' do
128
+ it '"@xxyyxxyy" should raise ArgumentError' do
129
+ expect{ subject.find('@xxyyxxyy') }.to raise_error(ArgumentError)
130
+ end
131
+ it '"@0xff112233445566" should raise ArgumentError' do
132
+ expect{ subject.find('@0xff112233445566') }.to raise_error(ArgumentError)
133
+ end
134
+ end
135
+ end
136
+ describe '#res_readable_id' do
137
+ it { subject.res_readable_id('@0x7f040000').should eq '@string/app_name' }
138
+ context 'assigns invalid type' do
139
+ it { expect{subject.res_readable_id('@0x7f0f0000')}.to raise_error Android::NotFoundError }
140
+ end
141
+ context 'assigns invalid key' do
142
+ it { expect{subject.res_readable_id('@0x7f040033')}.to raise_error Android::NotFoundError }
143
+ end
144
+ end
145
+ describe '#res_hex_id' do
146
+ it { subject.res_hex_id('@string/app_name').should eq '@0x7f040000' }
147
+ context 'assigns invalid type' do
148
+ it { expect{subject.res_readable_id('@not_exist/xxxx')}.to raise_error Android::NotFoundError }
149
+ end
150
+ context 'assigns invalid key' do
151
+ it { expect{subject.res_readable_id('@string/not_exist')}.to raise_error Android::NotFoundError }
152
+ end
153
+ end
154
+ end
86
155
  end
87
156
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_apk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-19 00:00:00.000000000 Z
12
+ date: 2013-01-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rubyzip
@@ -158,6 +158,7 @@ files:
158
158
  - spec/data/sample_classes.dex
159
159
  - spec/data/sample_resources.arsc
160
160
  - spec/data/sample_resources_utf16.arsc
161
+ - spec/data/str_resources.arsc
161
162
  - spec/dex/access_flag_spec.rb
162
163
  - spec/dex/dex_object_spec.rb
163
164
  - spec/dex/info_spec.rb
@@ -183,7 +184,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
183
184
  version: '0'
184
185
  segments:
185
186
  - 0
186
- hash: -1781742787123284542
187
+ hash: 671022741244016522
187
188
  required_rubygems_version: !ruby/object:Gem::Requirement
188
189
  none: false
189
190
  requirements:
@@ -192,7 +193,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
192
193
  version: '0'
193
194
  requirements: []
194
195
  rubyforge_project:
195
- rubygems_version: 1.8.24
196
+ rubygems_version: 1.8.23
196
197
  signing_key:
197
198
  specification_version: 3
198
199
  summary: static analysis tool for android apk