android_parser 2.6.0 → 2.7.0.beta1
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +2 -2
- data/.github/workflows/create_release.yml +1 -1
- data/CHANGELOG.md +14 -0
- data/android_parser.gemspec +3 -6
- data/docs/resources-arsc-reference.png +0 -0
- data/lib/android/apk.rb +42 -19
- data/lib/android/configuration.rb +13 -0
- data/lib/android/manifest.rb +4 -4
- data/lib/android/null_logger.rb +65 -0
- data/lib/android/resource.rb +100 -29
- data/lib/android/utils.rb +6 -7
- data/lib/android_parser.rb +6 -0
- metadata +7 -5
- data/lib/ruby_apk.rb +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe3ead9fd9c4393e0cf3fb39fae3f4ab4a99e68e30cbceae79a8861d451f3266
|
4
|
+
data.tar.gz: 2d80687dc01a1541d4f0b049d499e448eeee4f7413834fd603b5c7560b40048b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 98d6c6286ec3c02a7939e94bdeff0d0316db3997df828af79bb5b585e4f698ac36cb63c419d66da0da97ba48595e33e3d405e8296de95161cb3a9d8003931233
|
7
|
+
data.tar.gz: bd31cc2455cf30dcb001cc838fa48608910a20aaaa7b9e74f020c4c6fb3ea07f19cc6d0e598813bccfcfec30765f1775968cf4cba0e436a58bc6ddb2548eba00
|
data/.github/workflows/ci.yml
CHANGED
@@ -10,12 +10,12 @@ jobs:
|
|
10
10
|
strategy:
|
11
11
|
fail-fast: false
|
12
12
|
matrix:
|
13
|
-
ruby: [ ruby-
|
13
|
+
ruby: [ ruby-3.1, ruby-3.2, ruby-3.3 ]
|
14
14
|
os: [ ubuntu-latest, macos-latest ]
|
15
15
|
runs-on: ${{ matrix.os }}
|
16
16
|
|
17
17
|
steps:
|
18
|
-
- uses: actions/checkout@
|
18
|
+
- uses: actions/checkout@v4
|
19
19
|
|
20
20
|
- uses: ruby/setup-ruby@v1
|
21
21
|
with:
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# ChangeLog
|
2
2
|
|
3
|
+
## 2.7.0.beta1
|
4
|
+
|
5
|
+
* Dropped Ruby 2.5~3.0 support, now requires Ruby 3.1+.
|
6
|
+
* Bugfix: [#7] parse library type to fix unknown chunk type 0x0203.
|
7
|
+
* Bugfix: fix 3bits of lang and country in locales.
|
8
|
+
* add fetch locales support.
|
9
|
+
* add architectures support.
|
10
|
+
* add detect universal apk.
|
11
|
+
|
12
|
+
## 2.6.0
|
13
|
+
|
14
|
+
* [#2] add queries component support.
|
15
|
+
* [#3] add detect enable kotlin code.
|
16
|
+
|
3
17
|
## 2.5.1
|
4
18
|
|
5
19
|
* Bugfix: Handle string resources referencing other resources.
|
data/android_parser.gemspec
CHANGED
@@ -5,7 +5,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = 'android_parser'
|
8
|
-
spec.version = '2.
|
8
|
+
spec.version = '2.7.0.beta1'
|
9
9
|
spec.authors = ['SecureBrain', 'icyleaf']
|
10
10
|
spec.email = ['info@securebrain.co.jp', 'icyleaf.cn@gmail.com']
|
11
11
|
spec.platform = Gem::Platform::RUBY
|
@@ -15,13 +15,10 @@ Gem::Specification.new do |spec|
|
|
15
15
|
spec.license = 'MIT'
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
17
|
spec.require_paths = ['lib']
|
18
|
-
spec.required_ruby_version = '>=
|
18
|
+
spec.required_ruby_version = '>= 3.1'
|
19
19
|
|
20
20
|
spec.add_dependency 'rubyzip', '>= 1.0', '< 3.0'
|
21
|
-
|
22
|
-
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0') then
|
23
|
-
spec.add_dependency 'rexml', '> 3.0'
|
24
|
-
end
|
21
|
+
spec.add_dependency 'rexml', '> 3.0' # requires for Ruby 3.0+
|
25
22
|
|
26
23
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
27
24
|
spec.add_development_dependency 'rspec-its', '>= 1.2.0'
|
Binary file
|
data/lib/android/apk.rb
CHANGED
@@ -12,7 +12,6 @@ module Android
|
|
12
12
|
|
13
13
|
# apk object class
|
14
14
|
class Apk
|
15
|
-
|
16
15
|
# @return [String] apk file path
|
17
16
|
attr_reader :path
|
18
17
|
# @return [Android::Manifest] manifest instance
|
@@ -43,32 +42,30 @@ module Android
|
|
43
42
|
|
44
43
|
@path = filepath
|
45
44
|
raise NotFoundError, "'#{filepath}'" unless File.exist? @path
|
46
|
-
begin
|
47
|
-
@zip = Zip::File.open(@path)
|
48
|
-
rescue Zip::Error => e
|
49
|
-
raise NotApkFileError, e.message
|
50
|
-
end
|
51
45
|
|
52
46
|
@bindata = File.open(@path, 'rb') {|f| f.read }
|
53
47
|
@bindata.force_encoding(Encoding::ASCII_8BIT)
|
54
|
-
raise NotApkFileError,
|
48
|
+
raise NotApkFileError, 'manifest file is not found.' if zip.find_entry(MANIFEST).nil?
|
49
|
+
|
55
50
|
begin
|
56
51
|
@resource = Android::Resource.new(self.file(RESOURCE))
|
57
52
|
rescue => e
|
58
|
-
|
59
|
-
|
53
|
+
logger.error "failed to parse resource: #{e} with backtrace"
|
54
|
+
logger.error e.backtrace
|
60
55
|
end
|
56
|
+
|
61
57
|
begin
|
62
58
|
@manifest = Android::Manifest.new(self.file(MANIFEST), @resource)
|
63
59
|
rescue => e
|
64
|
-
|
65
|
-
|
60
|
+
logger.error "failed to parse manifest:#{e} with backtrace"
|
61
|
+
logger.error e.backtrace
|
66
62
|
end
|
63
|
+
|
67
64
|
begin
|
68
65
|
@dex = Android::Dex.new(self.file(DEX))
|
69
66
|
rescue => e
|
70
|
-
|
71
|
-
|
67
|
+
logger.error "failed to parse dex:#{e} with backtrace"
|
68
|
+
logger.error e.backtrace
|
72
69
|
end
|
73
70
|
end
|
74
71
|
|
@@ -105,9 +102,9 @@ module Android
|
|
105
102
|
# @yieldparam [String] name file name in apk
|
106
103
|
# @yieldparam [String] data file data in apk
|
107
104
|
def each_file
|
108
|
-
|
105
|
+
zip.each do |entry|
|
109
106
|
next unless entry.file?
|
110
|
-
yield entry.name,
|
107
|
+
yield entry.name, zip.read(entry)
|
111
108
|
end
|
112
109
|
end
|
113
110
|
|
@@ -116,13 +113,13 @@ module Android
|
|
116
113
|
# @return [String] binary data
|
117
114
|
# @raise [NotFoundError] when 'name' doesn't exist in the apk
|
118
115
|
def file(name) # get data by entry name(path)
|
119
|
-
|
116
|
+
zip.read(entry(name))
|
120
117
|
end
|
121
118
|
|
122
119
|
# @yield [entry]
|
123
120
|
# @yieldparam [Zip::Entry] entry zip entry
|
124
121
|
def each_entry
|
125
|
-
|
122
|
+
zip.each do |entry|
|
126
123
|
next unless entry.file?
|
127
124
|
yield entry
|
128
125
|
end
|
@@ -133,7 +130,7 @@ module Android
|
|
133
130
|
# @return [Zip::Entry] zip entry object
|
134
131
|
# @raise [NotFoundError] when 'name' doesn't exist in the apk
|
135
132
|
def entry(name)
|
136
|
-
entry =
|
133
|
+
entry = zip.find_entry(name)
|
137
134
|
raise NotFoundError, "'#{name}'" if entry.nil?
|
138
135
|
return entry
|
139
136
|
end
|
@@ -223,6 +220,23 @@ module Android
|
|
223
220
|
@certificates ||= Hash[self.signs.map{|path, sign| [path, sign.certificates.first] }]
|
224
221
|
end
|
225
222
|
|
223
|
+
# Return all architectures (all most for universal apk)
|
224
|
+
# @return [Array<String>]
|
225
|
+
# @since 2.7.0
|
226
|
+
def archs
|
227
|
+
@archs ||= zip.glob('lib/**/*').each_with_object([]) do |entry, obj|
|
228
|
+
arch = entry.name.split('/')[1]
|
229
|
+
obj << arch unless obj.include?(arch)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# detect if contains multi-platforms (native machine code)
|
234
|
+
# @return [Boolean]
|
235
|
+
# @since 2.7.0
|
236
|
+
def universal?
|
237
|
+
archs.size > 1
|
238
|
+
end
|
239
|
+
|
226
240
|
# detect if use kotlin language (may be third-party sdk or not)
|
227
241
|
# @return [Boolean]
|
228
242
|
# @since 2.6.0
|
@@ -257,6 +271,15 @@ module Android
|
|
257
271
|
'META-INF/services/kotlinx.coroutines.CoroutineExceptionHandler',
|
258
272
|
'META-INF/services/kotlinx.coroutines.internal.MainDispatcherFactory'
|
259
273
|
].freeze
|
274
|
+
|
275
|
+
def zip
|
276
|
+
@zip ||= Zip::File.open(@path)
|
277
|
+
rescue Zip::Error => e
|
278
|
+
raise NotApkFileError, e.message
|
279
|
+
end
|
280
|
+
|
281
|
+
def logger
|
282
|
+
Android.logger
|
283
|
+
end
|
260
284
|
end
|
261
285
|
end
|
262
|
-
|
data/lib/android/manifest.rb
CHANGED
@@ -17,7 +17,7 @@ module Android
|
|
17
17
|
# @return [Boolean]
|
18
18
|
def self.valid?(elem)
|
19
19
|
TYPES.include?(elem.name.downcase)
|
20
|
-
rescue
|
20
|
+
rescue
|
21
21
|
false
|
22
22
|
end
|
23
23
|
|
@@ -83,7 +83,7 @@ module Android
|
|
83
83
|
# @return [Boolean]
|
84
84
|
def self.valid?(elem)
|
85
85
|
['activity', 'activity-alias'].include?(elem.name.downcase)
|
86
|
-
rescue
|
86
|
+
rescue
|
87
87
|
false
|
88
88
|
end
|
89
89
|
|
@@ -145,7 +145,7 @@ module Android
|
|
145
145
|
filter&.elements&.any? do |elem|
|
146
146
|
TYPES.include?(elem&.name&.downcase)
|
147
147
|
end
|
148
|
-
rescue
|
148
|
+
rescue
|
149
149
|
false
|
150
150
|
end
|
151
151
|
|
@@ -418,7 +418,7 @@ module Android
|
|
418
418
|
#################################
|
419
419
|
# Manifest class definitions
|
420
420
|
#################################
|
421
|
-
|
421
|
+
|
422
422
|
# @return [REXML::Document] manifest xml
|
423
423
|
attr_reader :doc
|
424
424
|
|
@@ -0,0 +1,65 @@
|
|
1
|
+
|
2
|
+
module Android
|
3
|
+
class NullLogger
|
4
|
+
# @param _args Anything that we want to ignore
|
5
|
+
# @return [nil]
|
6
|
+
def unknown(*_args)
|
7
|
+
nil
|
8
|
+
end
|
9
|
+
|
10
|
+
# @param _args Anything that we want to ignore
|
11
|
+
# @return [nil]
|
12
|
+
def fatal(*_args)
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [FALSE]
|
17
|
+
def fatal?
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param _args Anything that we want to ignore
|
22
|
+
# @return [nil]
|
23
|
+
def error(*_args)
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [FALSE]
|
28
|
+
def error?
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param _args Anything that we want to ignore
|
33
|
+
# @return [nil]
|
34
|
+
def warn(*_args)
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [FALSE]
|
39
|
+
def warn?
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param _args Anything that we want to ignore
|
44
|
+
# @return [nil]
|
45
|
+
def info(*_args)
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [FALSE]
|
50
|
+
def info?
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
# @param _args Anything that we want to ignore
|
55
|
+
# @return [nil]
|
56
|
+
def debug(*_args)
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [FALSE]
|
61
|
+
def debug?
|
62
|
+
false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/android/resource.rb
CHANGED
@@ -6,6 +6,8 @@ module Android
|
|
6
6
|
# /frameworks/base/include/utils/ResourceTypes.h
|
7
7
|
# @see http://justanapplication.wordpress.com/category/android/android-resources/
|
8
8
|
class Resource
|
9
|
+
class UnknownChunkType < StandardError; end
|
10
|
+
|
9
11
|
class Chunk
|
10
12
|
def initialize(data, offset)
|
11
13
|
data.force_encoding(Encoding::ASCII_8BIT)
|
@@ -198,7 +200,7 @@ module Android
|
|
198
200
|
end
|
199
201
|
|
200
202
|
class ResTablePackage < ChunkHeader
|
201
|
-
attr_reader :name
|
203
|
+
attr_reader :name, :locales
|
202
204
|
|
203
205
|
def global_string_pool=(pool)
|
204
206
|
@global_string_pool = pool
|
@@ -279,7 +281,7 @@ module Android
|
|
279
281
|
"@#{type(tid)}/#{key(keyid)}"
|
280
282
|
end
|
281
283
|
def res_hex_id(readable_id, opt={})
|
282
|
-
|
284
|
+
_dummy, typestr, keystr = readable_id.match(/^@?(\w+)\/(\w+)$/).to_a
|
283
285
|
tid = type_id(typestr)
|
284
286
|
raise NotFoundError unless @types.has_key?(tid)
|
285
287
|
keyid = @types[tid][0].keys[keystr]
|
@@ -324,21 +326,26 @@ module Android
|
|
324
326
|
|
325
327
|
@types = {}
|
326
328
|
@specs = {}
|
329
|
+
@libraries = []
|
327
330
|
while offset < (@offset + @size)
|
328
331
|
type = @data[offset, 2].unpack('v')[0]
|
329
332
|
case type
|
330
|
-
when
|
333
|
+
when TYPE_TYPE
|
331
334
|
type = ResTableType.new(@data, offset, self)
|
332
335
|
offset += type.size
|
333
336
|
@types[type.id] = [] if @types[type.id].nil?
|
334
337
|
@types[type.id] << type
|
335
|
-
when
|
338
|
+
when TYPE_SPEC_TYPE
|
336
339
|
spec = ResTableTypeSpec.new(@data, offset)
|
337
340
|
offset += spec.size
|
338
341
|
@specs[spec.id] = [] if @specs[spec.id].nil?
|
339
342
|
@specs[spec.id] << spec
|
343
|
+
when TYPE_LIBRARY
|
344
|
+
library = ResTableLibraryType.new(@data, offset)
|
345
|
+
offset += library.size
|
346
|
+
@libraries.concat(library.libraries)
|
340
347
|
else
|
341
|
-
raise "chunk type error: type:%#04x" % type
|
348
|
+
raise UnknownChunkType, "chunk type error: type:%#04x" % type
|
342
349
|
end
|
343
350
|
end
|
344
351
|
end
|
@@ -348,7 +355,7 @@ module Android
|
|
348
355
|
@res_strings_lang = {}
|
349
356
|
@res_strings_contry = {}
|
350
357
|
begin
|
351
|
-
|
358
|
+
_type = type_id('string')
|
352
359
|
rescue NotFoundError
|
353
360
|
return
|
354
361
|
end
|
@@ -367,6 +374,8 @@ module Android
|
|
367
374
|
@res_strings_contry[contry] = str_hash unless contry.nil?
|
368
375
|
end
|
369
376
|
end
|
377
|
+
|
378
|
+
@locales = @res_strings_lang.keys
|
370
379
|
end
|
371
380
|
private :extract_res_strings
|
372
381
|
|
@@ -409,8 +418,8 @@ module Android
|
|
409
418
|
def parse
|
410
419
|
super
|
411
420
|
@id = read_int8
|
412
|
-
|
413
|
-
|
421
|
+
_res0 = read_int8 # must be 0.(maybe 4byte align)
|
422
|
+
_res1 = read_int16 # must be 0.(maybe 4byte align)
|
414
423
|
@entry_count = read_int32
|
415
424
|
@entry_start = read_int32
|
416
425
|
@config = ResTableConfig.new(@data, current_position)
|
@@ -444,10 +453,8 @@ module Android
|
|
444
453
|
def parse
|
445
454
|
@size = read_int32
|
446
455
|
@imei = read_int32
|
447
|
-
|
448
|
-
@
|
449
|
-
cn = @data_io.read(2)
|
450
|
-
@locale_contry = cn unless cn == "\x00\x00"
|
456
|
+
@locale_lang = unpack_locale(@data_io.read(1), @data_io.read(1), 'a')
|
457
|
+
@locale_contry = unpack_locale(@data_io.read(1), @data_io.read(1), '0')
|
451
458
|
@screen_type = read_int32
|
452
459
|
@input = read_int32
|
453
460
|
@screen_input = read_int32
|
@@ -457,6 +464,27 @@ module Android
|
|
457
464
|
def inspect
|
458
465
|
"<ResTableConfig size:#{@size}, imei:#{@imei}, la:'#{@locale_lang}' cn:'#{@locale_contry}'"
|
459
466
|
end
|
467
|
+
|
468
|
+
private
|
469
|
+
|
470
|
+
# Convert from https://github.com/LoyieKing/Apktool/blob/e0e6cfd03f10d710e666c29456659688fb5aeea2/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java#L474
|
471
|
+
def unpack_locale(in0, in1, base)
|
472
|
+
return if in0 == "\x00" && in1 == "\x00"
|
473
|
+
|
474
|
+
in0_value = in0.ord
|
475
|
+
in1_value = in1.ord
|
476
|
+
# check high bit, if so we have a packed 3 letter code
|
477
|
+
return [(in0_value).chr, (in1_value).chr].join('') unless ((in0_value >> 7) & 1) == 1
|
478
|
+
|
479
|
+
first = in1_value & 0x1F
|
480
|
+
second = ((in1_value & 0xE0) >> 5) + ((in0_value & 0x03) << 3)
|
481
|
+
third = (in0_value & 0x7C) >> 2
|
482
|
+
|
483
|
+
base_value = base.ord
|
484
|
+
# since this function handles languages & regions, we add the value(s) to the base char
|
485
|
+
# which is usually 'a' or '0' depending on language or region.
|
486
|
+
[(first + base_value).chr, (second + base_value).chr, (third + base_value).chr].join('')
|
487
|
+
end
|
460
488
|
end
|
461
489
|
|
462
490
|
class ResTableTypeSpec < ChunkHeader
|
@@ -465,8 +493,8 @@ module Android
|
|
465
493
|
def parse
|
466
494
|
super
|
467
495
|
@id = read_int8
|
468
|
-
|
469
|
-
|
496
|
+
_res0 = read_int8 # must be 0.(maybe 4byte align)
|
497
|
+
_res1 = read_int16 # must be 0.(maybe 4byte align)
|
470
498
|
@entry_count = read_int32
|
471
499
|
end
|
472
500
|
private :parse
|
@@ -531,29 +559,54 @@ module Android
|
|
531
559
|
end
|
532
560
|
end
|
533
561
|
|
562
|
+
class ResTableLibraryType < ChunkHeader
|
563
|
+
attr_reader :libraries
|
564
|
+
|
565
|
+
def parse
|
566
|
+
super
|
567
|
+
@libraries = []
|
568
|
+
library_count = read_int32
|
569
|
+
library_count.times do
|
570
|
+
package_id = read_int32
|
571
|
+
package_name = @data_io.read(128).force_encoding(Encoding::UTF_16LE).strip
|
572
|
+
@libraries << ResTableLibraryValue.new(package_id, package_name)
|
573
|
+
end
|
574
|
+
end
|
575
|
+
private :parse
|
576
|
+
|
577
|
+
def inspect
|
578
|
+
"<ResTableLibraryType library_count:#{@libraries.size}>"
|
579
|
+
end
|
580
|
+
|
581
|
+
ResTableLibraryValue = Struct.new(:package_id, :package_name)
|
582
|
+
end
|
583
|
+
|
534
584
|
class ResValue < Chunk
|
535
585
|
TYPE_REFERENCE = 0x01
|
536
586
|
|
537
587
|
attr_reader :size, :data_type, :data
|
538
588
|
def parse
|
539
589
|
@size = read_int16
|
540
|
-
|
590
|
+
_res0 = read_int8 # Always set 0.
|
541
591
|
@data_type = read_int8
|
542
592
|
@data = read_int32
|
543
593
|
end
|
544
594
|
private :parse
|
545
595
|
end
|
546
596
|
|
547
|
-
|
597
|
+
#################################
|
598
|
+
# Resource class definitions
|
599
|
+
#################################
|
600
|
+
|
548
601
|
# @returns [Hash] { name(String) => value(ResTablePackage) }
|
549
602
|
attr_reader :packages
|
550
603
|
|
551
604
|
def initialize(data)
|
552
605
|
data.force_encoding(Encoding::ASCII_8BIT)
|
553
606
|
@data = data
|
554
|
-
parse()
|
555
|
-
end
|
556
607
|
|
608
|
+
parse
|
609
|
+
end
|
557
610
|
|
558
611
|
# @return [Array<String>] all strings defined in arsc.
|
559
612
|
def strings
|
@@ -565,7 +618,7 @@ module Android
|
|
565
618
|
@res_table.package_count
|
566
619
|
end
|
567
620
|
|
568
|
-
#
|
621
|
+
# This method only support string resource for now.
|
569
622
|
# find resource by resource id
|
570
623
|
# @param [String] res_id (like '@0x7f010001' or '@string/key')
|
571
624
|
# @param [Hash] opts option
|
@@ -596,6 +649,13 @@ module Android
|
|
596
649
|
first_pkg.res_hex_id(readable_id)
|
597
650
|
end
|
598
651
|
|
652
|
+
# Return resources locales
|
653
|
+
# @return [Array<String>] all strings of locales.
|
654
|
+
# @since 2.7.0
|
655
|
+
def locales
|
656
|
+
first_pkg.locales
|
657
|
+
end
|
658
|
+
|
599
659
|
def first_pkg
|
600
660
|
@packages.first[1]
|
601
661
|
end
|
@@ -607,27 +667,38 @@ module Android
|
|
607
667
|
|
608
668
|
while offset < @data.size
|
609
669
|
type = @data[offset, 2].unpack('v')[0]
|
610
|
-
|
670
|
+
logger.debug "[%#08x] " % offset
|
611
671
|
@packages = {}
|
612
672
|
case type
|
613
|
-
when
|
614
|
-
@string_pool = ResStringPool.new(@data, offset)
|
615
|
-
offset += @string_pool.size
|
616
|
-
#puts "RES_STRING_POOL_TYPE %#x, %#x" % [@string_pool.size, offset]
|
617
|
-
when 0x0002 # RES_TABLE_TYPE
|
618
|
-
#puts "RES_TABLE_TYPE"
|
673
|
+
when TYPE_TABLE
|
619
674
|
@res_table = ResTableHeader.new(@data, offset)
|
620
675
|
offset += @res_table.header_size
|
621
|
-
|
622
|
-
|
676
|
+
logger.debug "RES_TABLE_TYPE"
|
677
|
+
when TYPE_STRING_POOL_TYPE
|
678
|
+
@string_pool = ResStringPool.new(@data, offset)
|
679
|
+
offset += @string_pool.size
|
680
|
+
logger.debug("RES_STRING_POOL_TYPE %#x, %#x" % [@string_pool.size, offset])
|
681
|
+
when TYPE_PACKAGE
|
623
682
|
pkg = ResTablePackage.new(@data, offset)
|
624
683
|
pkg.global_string_pool = @string_pool
|
625
684
|
offset += pkg.size
|
626
685
|
@packages[pkg.name] = pkg
|
686
|
+
logger.debug "RES_TABLE_PACKAGE_TYPE"
|
627
687
|
else
|
628
|
-
raise "chunk type error: type:%#04x" % type
|
688
|
+
raise UnknownChunkType, "chunk type error: type:%#04x" % type
|
629
689
|
end
|
630
690
|
end
|
631
691
|
end
|
692
|
+
|
693
|
+
def logger
|
694
|
+
Android.logger
|
695
|
+
end
|
696
|
+
|
697
|
+
TYPE_STRING_POOL_TYPE = 0x0001 # RES_STRING_POOL_TYPE
|
698
|
+
TYPE_TABLE = 0x0002 # RES_TABLE_TYPE
|
699
|
+
TYPE_PACKAGE = 0x0200 # RES_TABLE_PACKAGE_TYPE
|
700
|
+
TYPE_TYPE = 0x0201 # RES_TABLE_TYPE_TYPE
|
701
|
+
TYPE_SPEC_TYPE = 0x0202 # RES_TABLE_TYPE_SPEC_TYPE
|
702
|
+
TYPE_LIBRARY = 0x0203 # RES_TABLE_LIBRARY_TYPE
|
632
703
|
end
|
633
704
|
end
|
data/lib/android/utils.rb
CHANGED
@@ -8,9 +8,9 @@ module Android
|
|
8
8
|
# @return [Boolean]
|
9
9
|
def self.apk?(path)
|
10
10
|
begin
|
11
|
-
|
11
|
+
Apk.new(path)
|
12
12
|
return true
|
13
|
-
rescue
|
13
|
+
rescue
|
14
14
|
return false
|
15
15
|
end
|
16
16
|
end
|
@@ -20,7 +20,7 @@ module Android
|
|
20
20
|
# @return [Boolean]
|
21
21
|
def self.elf?(data)
|
22
22
|
data[0..3] == "\x7f\x45\x4c\x46"
|
23
|
-
rescue
|
23
|
+
rescue
|
24
24
|
false
|
25
25
|
end
|
26
26
|
|
@@ -29,7 +29,7 @@ module Android
|
|
29
29
|
# @return [Boolean]
|
30
30
|
def self.cert?(data)
|
31
31
|
data[0..1] == "\x30\x82"
|
32
|
-
rescue
|
32
|
+
rescue
|
33
33
|
false
|
34
34
|
end
|
35
35
|
|
@@ -38,7 +38,7 @@ module Android
|
|
38
38
|
# @return [Boolean]
|
39
39
|
def self.dex?(data)
|
40
40
|
data[0..7] == "\x64\x65\x78\x0a\x30\x33\x35\x00" # "dex\n035\0"
|
41
|
-
rescue
|
41
|
+
rescue
|
42
42
|
false
|
43
43
|
end
|
44
44
|
|
@@ -48,9 +48,8 @@ module Android
|
|
48
48
|
def self.valid_dex?(data)
|
49
49
|
Android::Dex.new(data)
|
50
50
|
true
|
51
|
-
rescue
|
51
|
+
rescue
|
52
52
|
false
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
56
|
-
|
data/lib/android_parser.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'android/null_logger'
|
4
|
+
require_relative 'android/configuration'
|
3
5
|
require_relative 'android/apk'
|
4
6
|
require_relative 'android/manifest'
|
5
7
|
require_relative 'android/axml_parser'
|
@@ -8,3 +10,7 @@ require_relative 'android/dex'
|
|
8
10
|
require_relative 'android/resource'
|
9
11
|
require_relative 'android/utils'
|
10
12
|
require_relative 'android/layout'
|
13
|
+
|
14
|
+
module Android
|
15
|
+
extend Configuration
|
16
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: android_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.7.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- SecureBrain
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2024-06-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rubyzip
|
@@ -148,9 +148,11 @@ files:
|
|
148
148
|
- README.md
|
149
149
|
- Rakefile
|
150
150
|
- android_parser.gemspec
|
151
|
+
- docs/resources-arsc-reference.png
|
151
152
|
- lib/android/apk.rb
|
152
153
|
- lib/android/axml_parser.rb
|
153
154
|
- lib/android/axml_writer.rb
|
155
|
+
- lib/android/configuration.rb
|
154
156
|
- lib/android/dex.rb
|
155
157
|
- lib/android/dex/access_flag.rb
|
156
158
|
- lib/android/dex/dex_object.rb
|
@@ -158,10 +160,10 @@ files:
|
|
158
160
|
- lib/android/dex/utils.rb
|
159
161
|
- lib/android/layout.rb
|
160
162
|
- lib/android/manifest.rb
|
163
|
+
- lib/android/null_logger.rb
|
161
164
|
- lib/android/resource.rb
|
162
165
|
- lib/android/utils.rb
|
163
166
|
- lib/android_parser.rb
|
164
|
-
- lib/ruby_apk.rb
|
165
167
|
homepage: https://github.com/icyleaf/android_parser
|
166
168
|
licenses:
|
167
169
|
- MIT
|
@@ -174,14 +176,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
174
176
|
requirements:
|
175
177
|
- - ">="
|
176
178
|
- !ruby/object:Gem::Version
|
177
|
-
version: '
|
179
|
+
version: '3.1'
|
178
180
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
179
181
|
requirements:
|
180
182
|
- - ">="
|
181
183
|
- !ruby/object:Gem::Version
|
182
184
|
version: '0'
|
183
185
|
requirements: []
|
184
|
-
rubygems_version: 3.
|
186
|
+
rubygems_version: 3.5.9
|
185
187
|
signing_key:
|
186
188
|
specification_version: 4
|
187
189
|
summary: Static analysis tool for android apk since 2021
|
data/lib/ruby_apk.rb
DELETED