class ApkAnalyzer::Analyzer
Constants
- ACTION
- ANDROID_MANIFEST_FILE
- CATEGORY
- GL_ES_VERSION
- HEX_FALSE
- HEX_TRUE
- NAME
- REQUIRED
Public Class Methods
new(file_path)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 17 def initialize(file_path) # Deactivating invalid date warnings in zip for apktools gem and apk analyzer code Zip.warn_invalid_date = false @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
Public Instance Methods
collect_cert_info()
click to toggle source
Certificate info. Issuer and dates
# File lib/apk_analyzer/analyzer.rb, line 80 def collect_cert_info # Redirect keytool check error to /dev/null 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 #{@file_path.shellescape}` certificate_content_regexp = /(-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----)/m matched_data = certificate_content_regexp.match(certificate_raw) if matched_data certificate_content = matched_data.captures[0] cert_info = { issuer_raw: nil, cn: nil, ou: nil, o: nil, st: nil, l: nil, c: nil, creation_date: nil, expiration_date: nil } cert_extract_dates(certificate_content, cert_info) cert_extract_issuer(certificate_content, cert_info) else puts 'Failed to find CERT.RSA file' end cert_info end
collect_manifest_info()
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 35 def collect_manifest_info manifest_file_path = find_file(ANDROID_MANIFEST_FILE) raise 'Failed to find Manifest file' if manifest_file_path.nil? begin manifest_xml = Nokogiri::XML(@manifest) rescue => e puts "Failed to parse #{ANDROID_MANIFEST_FILE}" log_expection e end manifest_info = {} begin manifest_info[:path] = manifest_file_path content = {} # application content content[:application_info] = collect_application_info(manifest_xml) # intents content[:intents] = collect_intent_info(manifest_xml) # sdk infos sdk_infos = collect_sdk_info(manifest_xml) content[:uses_sdk] = { minimum_sdk_version: sdk_infos[0], target_sdk_version: sdk_infos[1] } # uses permission uses_permissions = collect_uses_permission_info(manifest_xml) content[:uses_permissions] = uses_permissions # uses features feature_list = collect_uses_feature_info(manifest_xml) content[:uses_features] = feature_list # screen compatibility supported_screens = collect_supported_screens(manifest_xml) content[:supports_screens] = supported_screens manifest_info[:content] = content rescue => e log_expection e raise "Invalid xml found" end manifest_info end
Private Instance Methods
bool_conv(value)
click to toggle source
hex strings come come from apktools/apkxml. It converts true to 0xffffffff and false to 0x0
# File lib/apk_analyzer/analyzer.rb, line 246 def bool_conv(value) value == HEX_FALSE ? false : true end
cert_extract_date(date_str)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 235 def cert_extract_date(date_str) match = /=(.*)$/.match(date_str) match.captures[0] end
cert_extract_dates(certificate_content, result)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 227 def cert_extract_dates(certificate_content, result) #collect dates start_date = `echo "#{certificate_content}" | openssl x509 -noout -in /dev/stdin -startdate -nameopt -esc_msb,utf8` end_date = `echo "#{certificate_content}" | openssl x509 -noout -in /dev/stdin -enddate -nameopt -esc_msb,utf8` result[:creation_date] = cert_extract_date(start_date) result[:expiration_date] = cert_extract_date(end_date) end
cert_extract_issuer(certificate_content, result)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 202 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 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 result[:cn] = cert_extract_issuer_parameterized(raw, 'CN') result[:ou] = cert_extract_issuer_parameterized(raw, 'OU') result[:o] = cert_extract_issuer_parameterized(raw, 'O') result[:st] = cert_extract_issuer_parameterized(raw, 'ST') result[:l] = cert_extract_issuer_parameterized(raw, 'L') result[:c] = cert_extract_issuer_parameterized(raw, 'C') end
cert_extract_issuer_parameterized(subject, param)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 217 def cert_extract_issuer_parameterized(subject, param) # The following regex was previously used to match fields when not # using '-nameopt -esc_msb,utf8'' switch with openssl # match = %r{\/#{Regexp.quote(param)}=([^\/]*)}.match(subject) match = /#{Regexp.quote(param)}=([^=]*)(, [A-Z]+=|$)/.match(subject) return nil if match.nil? match.captures[0] end
collect_application_info(manifest_xml)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 151 def collect_application_info(manifest_xml) application_content = {} application_tag = manifest_xml.xpath('//application') # Collect all attributes within application tag unless application_tag.empty? application_attributes = application_tag.first.attributes application_attributes.each_value do |attr_key| value = attr_key.value value = bool_conv(value) if is_hex_bool?(value) application_content[attr_key.name.to_sym] = value end end # Add application id to previous informations application_id = manifest_xml.xpath('//manifest/@package') application_content[:application_id] = application_id[0].value unless application_id.empty? application_content end
collect_intent_info(manifest_xml)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 172 def collect_intent_info(manifest_xml) intent_filters = manifest_xml.xpath('//intent-filter') intents = [] intent_filters.each do |intent| intent_attributes = {} actions = [] category = nil intent.children.each do |child| next unless child.is_a?(Nokogiri::XML::Element) if child.name == ACTION actions.push child.attributes[NAME].value elsif child.name == CATEGORY category = child.attributes[NAME].value end end intent_attributes[:actions] = actions unless actions.empty? intent_attributes[:category] = category unless category.nil? intents.push intent_attributes unless intent_attributes.empty? end intents end
collect_sdk_info(manifest_xml)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 194 def collect_sdk_info(manifest_xml) sdk_infos = [] minimum_sdk_version = manifest_xml.xpath('//uses-sdk/@android:minSdkVersion') target_sdk_version = manifest_xml.xpath('//uses-sdk/@android:targetSdkVersion') sdk_infos = [minimum_sdk_version, target_sdk_version].map { |elt| sanitize_hex(elt.first.value) unless elt.empty? } sdk_infos end
collect_supported_screens(manifest_xml)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 111 def collect_supported_screens(manifest_xml) supported_screens = [] screen_types = manifest_xml.xpath('//supports-screens').first unless screen_types.nil? screen_types.attributes.each do |screen_type, required_param| supported_screens.push screen_type if required_param.value == HEX_TRUE end end supported_screens end
collect_uses_feature_info(manifest_xml)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 122 def collect_uses_feature_info(manifest_xml) features = manifest_xml.xpath('//uses-feature') feature_list = [] features.each do |feature| feature_element = {} feature.attributes.each_value do |attr| feature_attr_key = attr.name feature_attr_value = attr.value if attr.name == REQUIRED feature_attr_value = bool_conv(feature_attr_value) elsif attr.name == GL_ES_VERSION feature_attr_key = NAME feature_attr_value = opengl_version_conv(attr.value) end feature_element[feature_attr_key.to_sym] = feature_attr_value end feature_list.push feature_element end feature_list end
collect_uses_permission_info(manifest_xml)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 145 def collect_uses_permission_info(manifest_xml) uses_permissions = [] manifest_xml.xpath('//uses-permission/@android:name').each { |permission| uses_permissions.push permission.value } uses_permissions end
find_file(file_name)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 271 def find_file(file_name) begin zipfile = Zip::File.open(@file_path) # Search at the root file_path = zipfile.find_entry(file_name) return file_path.name unless file_path.nil? # Search deeply zipfile.each do |entry| file_path = entry.name if entry.name.match(file_name) break unless file_path.nil? end file_path.nil? ? nil : file_path rescue => e log_expection e ensure zipfile.close end end
is_hex_bool?(hex_string)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 250 def is_hex_bool?(hex_string) hex_string == HEX_TRUE || hex_string == HEX_FALSE end
log_expection(e)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 292 def log_expection e puts e.message puts e.backtrace end
opengl_version_conv(value)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 254 def opengl_version_conv(value) value_copy = value.dup value_copy = value_copy.gsub(/\D|0/, '') value_copy.chars.join('.') value_copy += '.0' if value.chars.last == '0' "Open GL #{value_copy}" end
sanitize_hex(hex)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 240 def sanitize_hex(hex) hex.to_i(16) end
valid_zip?(file)
click to toggle source
# File lib/apk_analyzer/analyzer.rb, line 262 def valid_zip?(file) zip = Zip::File.open(file) true rescue StandardError false ensure zip.close if zip end