diff --git a/README.md b/README.md index 5c1e33f..1c00b15 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # Apk Analyzer -The aim of this gem is to extract some data from android apk files. Analysis results are printed in json. It can be used with CLI +The aim of this gem is to extract some data from android apk or aab files. Analysis results are printed in json. It can be used with CLI ## Installation @@ -31,18 +31,18 @@ $ gem install apk_analyzer In a terminal use Apk analyzer like this: ```shell -$ apk_analyzer --manifest --cert-info --file /path/to/apk +$ apk_analyzer --manifest --cert-info --file /path/to/file ``` Script above will collect and print: * Android manifest informations -* Apk certificate informations if it have been signed +* Certificate informations if it have been signed **Result** ```json { "manifest_info": { - "path_in_apk": "AndroidManifest.xml", + "path": "AndroidManifest.xml", "content": { "application_info": { "theme": "13", @@ -128,7 +128,7 @@ Script above will collect and print: require 'apk_analyzer' # Instantiate analyzer -apk_analyzer = ApkAnalyzer::Analyzer.new(File.expand_path('path/to/apk')) +apk_analyzer = ApkAnalyzer::Analyzer.new(File.expand_path('path/to/file')) # Then collect data manifest_info = apk_analyzer.collect_manifest_info diff --git a/bin/apk_analyzer b/bin/apk_analyzer index e4c755b..3345996 100755 --- a/bin/apk_analyzer +++ b/bin/apk_analyzer @@ -6,7 +6,7 @@ require 'optparse' require 'json' options = { - apk_path: nil, + file_path: nil, manifest: false, cert_info: false, all: false @@ -18,8 +18,8 @@ apk_data = { } opts_parser = OptionParser.new do |opts| - opts.on('-f', '--file=FILE_PATH', 'Apk file path') do |file_path| - options[:apk_path] = file_path + opts.on('-f', '--file=FILE_PATH', 'File path') do |file_path| + options[:file_path] = file_path end opts.on('-m', '--manifest', 'Prints Manifest.xml information') do @@ -30,7 +30,7 @@ opts_parser = OptionParser.new do |opts| options[:cert_info] = true end - opts.on('-a', '--all', 'Prints available data on APK') do + opts.on('-a', '--all', 'Prints available data') do options[:all] = true end @@ -45,8 +45,8 @@ exit_code = 0 opts_parser.parse! -raise 'File not specified' if options[:apk_path].nil? -apk_analyzer = ApkAnalyzer::Analyzer.new(File.expand_path(options[:apk_path])) +raise 'File not specified' if options[:file_path].nil? +apk_analyzer = ApkAnalyzer::Analyzer.new(File.expand_path(options[:file_path])) apk_data = {} begin apk_data[:manifest_info] = apk_analyzer.collect_manifest_info if options[:manifest] || options[:all] diff --git a/lib/apk_analyzer/analyzer.rb b/lib/apk_analyzer/analyzer.rb index 1a2380e..d41987b 100644 --- a/lib/apk_analyzer/analyzer.rb +++ b/lib/apk_analyzer/analyzer.rb @@ -14,19 +14,29 @@ class Analyzer ANDROID_MANIFEST_FILE = 'AndroidManifest.xml' - def initialize(apk_path) + def initialize(file_path) # Deactivating invalid date warnings in zip for apktools gem and apk analyzer code Zip.warn_invalid_date = false - @apk_path = apk_path - raise 'File is not a valid apk file' unless valid_zip?(apk_path) - @apk_xml = ApkXml.new(apk_path) + @file_path = file_path + raise 'File is not a valid file' unless valid_zip?(file_path) + case File.extname(file_path) + when ".apk" + @manifest = ApkXml.new(file_path).parse_xml('AndroidManifest.xml', true, true) + when ".aab" + String bundle_tool_location = %x[ #{"which bundletool"} ] + raise 'Bundletool is not installed & available in your path' if bundle_tool_location.nil? or bundle_tool_location.length == 0 + cmd = "bundletool dump manifest --bundle #{file_path}" + @manifest = %x[ #{cmd} ] + else + raise 'unknown platform technology' + end end def collect_manifest_info - manifest_file_path = find_file_in_apk(ANDROID_MANIFEST_FILE) - raise 'Failed to find Manifest file in apk' if manifest_file_path.nil? + manifest_file_path = find_file(ANDROID_MANIFEST_FILE) + raise 'Failed to find Manifest file' if manifest_file_path.nil? begin - manifest_xml = Nokogiri::XML(@apk_xml.parse_xml('AndroidManifest.xml', true, true)) + manifest_xml = Nokogiri::XML(@manifest) rescue => e puts "Failed to parse #{ANDROID_MANIFEST_FILE}" log_expection e @@ -34,7 +44,7 @@ def collect_manifest_info manifest_info = {} begin - manifest_info[:path_in_apk] = manifest_file_path + manifest_info[:path] = manifest_file_path content = {} # application content content[:application_info] = collect_application_info(manifest_xml) @@ -72,7 +82,7 @@ def collect_cert_info os_has_keytool = system('keytool 2>/dev/null') raise 'keytool dependency not satisfied. Make sure that JAVA keytool utility is installed' unless os_has_keytool cert_info = {} - certificate_raw = `keytool -printcert -rfc -jarfile #{@apk_path.shellescape}` + certificate_raw = `keytool -printcert -rfc -jarfile #{@file_path.shellescape}` certificate_content_regexp = /(-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----)/m matched_data = certificate_content_regexp.match(certificate_raw) if matched_data @@ -91,7 +101,7 @@ def collect_cert_info cert_extract_dates(certificate_content, cert_info) cert_extract_issuer(certificate_content, cert_info) else - puts 'Failed to find CERT.RSA file in APK' + puts 'Failed to find CERT.RSA file' end cert_info end @@ -190,8 +200,9 @@ def collect_sdk_info(manifest_xml) end def cert_extract_issuer(certificate_content, result) + print(certificate_content) subject = `echo "#{certificate_content}" | openssl x509 -noout -in /dev/stdin -subject -nameopt -esc_msb,utf8` - # All APK certificate fields are not manadatory. At least one is needed.So to remove trailing carrier return + # All certificate fields are not manadatory. At least one is needed.So to remove trailing carrier return # character, we apply gsub method on the raw subject, and we use it after. raw = subject.gsub(/\n/,'') result[:issuer_raw] = raw @@ -257,25 +268,24 @@ def valid_zip?(file) zip.close if zip end - def find_file_in_apk(file_name) + def find_file(file_name) begin - file_path_in_apk = nil - apk_zipfile = Zip::File.open(@apk_path) + zipfile = Zip::File.open(@file_path) # Search at the root - file_path_in_apk = apk_zipfile.find_entry(file_name) - return file_path_in_apk.name unless file_path_in_apk.nil? + file_path = zipfile.find_entry(file_name) + return file_path.name unless file_path.nil? # Search deeply - apk_zipfile.each do |entry| - file_path_in_apk = entry.name if entry.name.match(file_name) - break unless file_path_in_apk.nil? + zipfile.each do |entry| + file_path = entry.name if entry.name.match(file_name) + break unless file_path.nil? end - file_path_in_apk.nil? ? nil : file_path_in_apk + file_path.nil? ? nil : file_path rescue => e log_expection e ensure - apk_zipfile.close + zipfile.close end end diff --git a/lib/apk_analyzer/version.rb b/lib/apk_analyzer/version.rb index a14dc08..bb7234f 100644 --- a/lib/apk_analyzer/version.rb +++ b/lib/apk_analyzer/version.rb @@ -1,3 +1,3 @@ module ApkAnalyzer - VERSION = '1.0.2' + VERSION = '1.0.3' end