ruby_apk 0.4.2 → 0.5.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/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