android_parser 2.5.1 → 2.6.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.
- checksums.yaml +4 -4
- data/.github/workflows/create_release.yml +15 -0
- data/Gemfile +4 -5
- data/README.md +1 -1
- data/android_parser.gemspec +1 -1
- data/lib/android/apk.rb +49 -11
- data/lib/android/dex/info.rb +23 -6
- data/lib/android/manifest.rb +101 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a455cf668b1e1fbae27b208a872c304ac7ff60987034c529aa93c676b3e4348
|
4
|
+
data.tar.gz: 5089aec3a63dec1766ae3c981442c2a6fa9500f1351d0e989552e84047309443
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4e6104d2a29750902a79871b5a3e2750ea3ea3a8fdcde709870950ca93ddeaa2299f481c393be9556e21378a88c843afe8b9123b338c7a7e7c204a2800c6b99
|
7
|
+
data.tar.gz: 9474805fe13212f5193817cf0bc22b33163211232ec01cf4ad60b65eb4713992d701f3883f87241eb2d8b532d00397d6c641c167b19db4b8d71fc314bf87d352
|
@@ -0,0 +1,15 @@
|
|
1
|
+
name: Create Release
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
tags:
|
5
|
+
- "v*"
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
create:
|
9
|
+
runs-on: ubuntu-latest
|
10
|
+
steps:
|
11
|
+
- name: Create Release
|
12
|
+
uses: softprops/action-gh-release@v1
|
13
|
+
if: startsWith(github.ref, 'refs/tags/')
|
14
|
+
env:
|
15
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
data/Gemfile
CHANGED
@@ -3,8 +3,7 @@ source 'http://rubygems.org'
|
|
3
3
|
# Specify your gem's dependencies in android_parser.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# end
|
6
|
+
group :development do
|
7
|
+
gem 'awesome_print'
|
8
|
+
gem 'debug'
|
9
|
+
end
|
data/README.md
CHANGED
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.6.0'
|
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
|
data/lib/android/apk.rb
CHANGED
@@ -189,7 +189,7 @@ module Android
|
|
189
189
|
# @return [nil] when label is not found
|
190
190
|
# @deprecated move to {Android::Manifest#label}
|
191
191
|
# @since 0.6.0
|
192
|
-
def label(lang=nil)
|
192
|
+
def label(lang = nil)
|
193
193
|
@manifest.label
|
194
194
|
end
|
195
195
|
|
@@ -200,25 +200,63 @@ module Android
|
|
200
200
|
@layouts ||= Layout.collect_layouts(self) # lazy parse
|
201
201
|
end
|
202
202
|
|
203
|
-
# apk's signature information
|
203
|
+
# apk's v1 signature information
|
204
204
|
# @return [Hash{ String => OpenSSL::PKCS7 } ] key: sign file path, value: signature
|
205
205
|
# @since 0.7.0
|
206
206
|
def signs
|
207
|
-
signs
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
207
|
+
@signs ||= lambda {
|
208
|
+
signs = {}
|
209
|
+
self.each_file do |path, data|
|
210
|
+
# find META-INF/xxx.{RSA|DSA|EC}
|
211
|
+
next unless path =~ /^META-INF\// && data.unpack("CC") == [0x30, 0x82]
|
212
|
+
|
213
|
+
signs[path] = OpenSSL::PKCS7.new(data)
|
214
|
+
end
|
215
|
+
signs
|
216
|
+
}.call
|
214
217
|
end
|
215
218
|
|
216
|
-
# certificate info which is used for signing
|
219
|
+
# v1 certificate info which is used for signing
|
217
220
|
# @return [Hash{String => OpenSSL::X509::Certificate }] key: sign file path, value: first certficate in the sign file
|
218
221
|
# @since 0.7.0
|
219
222
|
def certificates
|
220
|
-
|
223
|
+
@certificates ||= Hash[self.signs.map{|path, sign| [path, sign.certificates.first] }]
|
224
|
+
end
|
225
|
+
|
226
|
+
# detect if use kotlin language (may be third-party sdk or not)
|
227
|
+
# @return [Boolean]
|
228
|
+
# @since 2.6.0
|
229
|
+
def kotlin?
|
230
|
+
@kotlin ||= kotlin_file? || kotlin_classes?
|
221
231
|
end
|
232
|
+
|
233
|
+
private
|
234
|
+
|
235
|
+
def kotlin_file?
|
236
|
+
KOTLIN_FILES.any? do |file|
|
237
|
+
begin
|
238
|
+
entry(file)
|
239
|
+
rescue
|
240
|
+
next
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def kotlin_classes?
|
246
|
+
@dex.classes.any? do |class_info|
|
247
|
+
class_info.type.start_with?('kotlin.') ||
|
248
|
+
class_info.type.start_with?('kotlinx.')
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
KOTLIN_FILES = [
|
253
|
+
'kotlin-tooling-metadata.json',
|
254
|
+
'kotlin/kotlin.kotlin_builtins',
|
255
|
+
'META-INF/kotlinx_coroutines_android.version',
|
256
|
+
'META-INF/kotlinx_coroutines_core.version',
|
257
|
+
'META-INF/services/kotlinx.coroutines.CoroutineExceptionHandler',
|
258
|
+
'META-INF/services/kotlinx.coroutines.internal.MainDispatcherFactory'
|
259
|
+
].freeze
|
222
260
|
end
|
223
261
|
end
|
224
262
|
|
data/lib/android/dex/info.rb
CHANGED
@@ -29,16 +29,32 @@ module Android
|
|
29
29
|
# @return [DexObject::ClassDefItem]
|
30
30
|
attr_reader :class_def
|
31
31
|
|
32
|
+
# return full namespace of class
|
33
|
+
#
|
34
|
+
# sample value like `Landroidx/activity/Cancellable;`
|
35
|
+
#
|
36
|
+
# @return [String] class name
|
32
37
|
def name
|
33
|
-
@dex.type_resolve(@class_def[:class_idx])
|
38
|
+
@name ||= @dex.type_resolve(@class_def[:class_idx])
|
34
39
|
end
|
40
|
+
|
35
41
|
def super_class
|
36
|
-
if @class_def[:superclass_idx] != NO_INDEX
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
42
|
+
@super_class ||= if @class_def[:superclass_idx] != NO_INDEX
|
43
|
+
@dex.type_resolve(@class_def[:superclass_idx])
|
44
|
+
else
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Convert name to human readable string
|
50
|
+
#
|
51
|
+
# From `Landroidx/activity/Cancellable;` to `androidx.activity.Cancellable`
|
52
|
+
#
|
53
|
+
# @return [String] human readable name
|
54
|
+
def type
|
55
|
+
@type ||= name.gsub('/', '.')[1..-2]
|
41
56
|
end
|
57
|
+
|
42
58
|
# @param [Dex::ClassDefItem] class_def
|
43
59
|
# @param [Dex] dex dex class instance
|
44
60
|
def initialize(class_def, dex)
|
@@ -62,6 +78,7 @@ module Android
|
|
62
78
|
end
|
63
79
|
|
64
80
|
private
|
81
|
+
|
65
82
|
def cls2info(arr, cls, idx_key)
|
66
83
|
idx = 0
|
67
84
|
ret = []
|
data/lib/android/manifest.rb
CHANGED
@@ -10,7 +10,7 @@ module Android
|
|
10
10
|
# <activity>, <service>, <receiver> or <provider> element in <application> element of the manifest file.
|
11
11
|
class Component
|
12
12
|
# component types
|
13
|
-
TYPES = ['activity', 'activity-alias', 'service', 'receiver', 'provider', 'application']
|
13
|
+
TYPES = ['activity', 'activity-alias', 'service', 'receiver', 'provider', 'application', 'queries']
|
14
14
|
|
15
15
|
# the element is valid Component element or not
|
16
16
|
# @param [REXML::Element] elem xml element
|
@@ -322,6 +322,99 @@ module Android
|
|
322
322
|
end
|
323
323
|
end
|
324
324
|
|
325
|
+
# <intent>, <service>, <receiver> or <provider> element in <application> element of the manifest file.
|
326
|
+
class QueriesComponent
|
327
|
+
# component types
|
328
|
+
TYPES = ['package', 'intent', 'provider']
|
329
|
+
|
330
|
+
# @return [String] type string in TYPES
|
331
|
+
attr_reader :type
|
332
|
+
# @return [Manifest::Queries::Package]
|
333
|
+
attr_reader :packages
|
334
|
+
# @return [Array<Manifest::Queries::Intent>]
|
335
|
+
attr_reader :intents
|
336
|
+
# @return [Array<Manifest::Queries::Provider>]
|
337
|
+
attr_reader :providers
|
338
|
+
# @return [REXML::Element]
|
339
|
+
attr_reader :elem
|
340
|
+
|
341
|
+
# @param [REXML::Element] elem target element
|
342
|
+
# @raise [ArgumentError] when elem is invalid.
|
343
|
+
def initialize(elem)
|
344
|
+
raise ArgumentError unless Component.valid?(elem)
|
345
|
+
|
346
|
+
@elem = elem
|
347
|
+
@type = elem.name
|
348
|
+
@packages = parse_packages
|
349
|
+
@intents = parse_intents
|
350
|
+
@providers = parse_providers
|
351
|
+
end
|
352
|
+
|
353
|
+
private
|
354
|
+
|
355
|
+
def parse_packages
|
356
|
+
packages = []
|
357
|
+
return packages if @elem.elements['package'].nil?
|
358
|
+
|
359
|
+
@elem.each_element('package') do |package|
|
360
|
+
packages << Android::Manifest::Queries::Package.new(package)
|
361
|
+
end
|
362
|
+
|
363
|
+
packages
|
364
|
+
end
|
365
|
+
|
366
|
+
def parse_intents
|
367
|
+
intents = []
|
368
|
+
return intents if @elem.elements['intent'].nil?
|
369
|
+
|
370
|
+
@elem.each_element('intent') do |intent|
|
371
|
+
next if intent&.elements&.empty?
|
372
|
+
next unless Android::Manifest::Queries::Intent.valid?(intent)
|
373
|
+
|
374
|
+
intent = Android::Manifest::Queries::Intent.new(intent)
|
375
|
+
intents << intent unless intent.empty?
|
376
|
+
end
|
377
|
+
|
378
|
+
intents
|
379
|
+
end
|
380
|
+
|
381
|
+
def parse_providers
|
382
|
+
providers = []
|
383
|
+
return providers if @elem.elements['provider'].nil?
|
384
|
+
|
385
|
+
@elem.each_element('provider') do |provider|
|
386
|
+
providers << Android::Manifest::Queries::Provider.new(provider)
|
387
|
+
end
|
388
|
+
|
389
|
+
providers
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
class Queries < QueriesComponent
|
394
|
+
class Intent < Android::Manifest::IntentFilter; end
|
395
|
+
class Provider < Android::Manifest::Provider
|
396
|
+
def authorities
|
397
|
+
@authorities ||= @elem.attributes['authorities']
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
class Package
|
402
|
+
# @return [String] action name of queries
|
403
|
+
attr_reader :name
|
404
|
+
# @return [String] action type of intent-filter
|
405
|
+
attr_reader :type
|
406
|
+
|
407
|
+
def initialize(elem)
|
408
|
+
@type = 'package'
|
409
|
+
@name = elem.attributes['name']
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
def self.valid?(elem)
|
414
|
+
elem&.name == 'queries'
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
325
418
|
#################################
|
326
419
|
# Manifest class definitions
|
327
420
|
#################################
|
@@ -370,6 +463,13 @@ module Android
|
|
370
463
|
components
|
371
464
|
end
|
372
465
|
|
466
|
+
# Returns the manifest's queries element or nil, if there isn't any.
|
467
|
+
# @return [Android::Manifest::Queries] the manifest's application element
|
468
|
+
def queries
|
469
|
+
element = @doc.elements['/manifest/queries']
|
470
|
+
Queries.new(element) if Queries.valid?(element)
|
471
|
+
end
|
472
|
+
|
373
473
|
# @return [Array<Android::Manifest::Activity&ActivityAlias>] all activities in the apk
|
374
474
|
# @note return empty array when the manifest include no activities
|
375
475
|
def activities
|
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.6.0
|
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: 2023-03-
|
12
|
+
date: 2023-03-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rubyzip
|
@@ -139,6 +139,7 @@ extra_rdoc_files: []
|
|
139
139
|
files:
|
140
140
|
- ".github/dependabot.yml"
|
141
141
|
- ".github/workflows/ci.yml"
|
142
|
+
- ".github/workflows/create_release.yml"
|
142
143
|
- ".gitignore"
|
143
144
|
- ".rspec"
|
144
145
|
- CHANGELOG.md
|