androguard/tests/test_arsc.py

236 lines
7.3 KiB
Python

# -*- coding: utf-8 -*-
import os
import unittest
from lxml import etree
test_dir = os.path.dirname(os.path.abspath(__file__))
from operator import itemgetter
from androguard.core import apk, axml
TEST_APP_NAME = "TestsAndroguardApplication"
TEST_ICONS = {
120: "res/drawable-ldpi/icon.png",
160: "res/drawable-mdpi/icon.png",
240: "res/drawable-hdpi/icon.png",
65536: "res/drawable-hdpi/icon.png",
}
TEST_CONFIGS = {
"layout": [axml.ARSCResTableConfig.default_config()],
"string": [axml.ARSCResTableConfig.default_config()],
"drawable": [
axml.ARSCResTableConfig(sdkVersion=4, density=120),
axml.ARSCResTableConfig(sdkVersion=4, density=160),
axml.ARSCResTableConfig(sdkVersion=4, density=240),
],
}
class ARSCTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
with open(
os.path.join(test_dir, 'data/APK/TestActivity.apk'), "rb"
) as fd:
cls.apk = apk.APK(fd.read(), True)
def testARSC(self):
arsc = self.apk.get_android_resources()
self.assertTrue(arsc)
def testAppName(self):
app_name = self.apk.get_app_name()
self.assertEqual(
app_name,
TEST_APP_NAME,
"Couldn't deduce application/activity label",
)
def testAppIcon(self):
for wanted_density, correct_path in TEST_ICONS.items():
app_icon_path = self.apk.get_app_icon(wanted_density)
self.assertEqual(
app_icon_path,
correct_path,
"Incorrect icon path for requested density",
)
def testStrings(self):
arsc = self.apk.get_android_resources()
p = arsc.get_packages_names()[0]
l = "\x00\x00"
e = etree.fromstring(arsc.get_string_resources(p, l))
self.assertEqual(
e.find("string[@name='hello']").text,
'Hello World, TestActivity! kikoololmodif',
)
self.assertEqual(
e.find("string[@name='app_name']").text,
'TestsAndroguardApplication',
)
def testResourceNames(self):
"""
Test if the resource name translation works
"""
arsc = self.apk.get_android_resources()
self.assertEqual(
arsc.get_resource_xml_name(0x7F040001),
"@tests.androguard:string/app_name",
)
self.assertEqual(
arsc.get_resource_xml_name(0x7F020000),
"@tests.androguard:drawable/icon",
)
self.assertEqual(
arsc.get_resource_xml_name(0x7F040001, 'tests.androguard'),
"@string/app_name",
)
self.assertEqual(
arsc.get_resource_xml_name(0x7F020000, 'tests.androguard'),
"@drawable/icon",
)
# Also test non existing resources
self.assertIsNone(arsc.get_resource_xml_name(0xFFFFFFFF))
self.assertEqual(arsc.get_id('sdf', 0x7F040001), (None, None, None))
self.assertEqual(
arsc.get_id('tests.androguard', 0xFFFFFFFF), (None, None, None)
)
def testDifferentStringLocales(self):
"""
Test if the resolving of different string locales works
"""
a = apk.APK(os.path.join(test_dir, 'data/APK/a2dp.Vol_137.apk'))
arsc = a.get_android_resources()
p = arsc.get_packages_names()[0]
self.assertEqual(
sorted(["\x00\x00", "da", "de", "el", "fr", "ja", "ru"]),
sorted(arsc.get_locales(p)),
)
item = "SMSDelayText"
strings = {
"\x00\x00": "Delay for reading text message",
"da": "Forsinkelse for læsning af tekst besked",
"de": "Verzögerung vor dem Lesen einer SMS",
"el": "Χρονοκαθυστέρηση ανάγνωσης μηνυμάτων SMS",
"fr": "Délai pour lire un SMS",
"ja": "テキストメッセージ読み上げの遅延",
"ru": "Задержка зачитывания SMS",
}
for k, v in strings.items():
e = etree.fromstring(arsc.get_string_resources(p, k))
self.assertEqual(e.find("string[@name='{}']".format(item)).text, v)
def testTypeConfigs(self):
arsc = self.apk.get_android_resources()
configs = arsc.get_type_configs(None)
for res_type, test_configs in list(TEST_CONFIGS.items()):
config_set = set(test_configs)
self.assertIn(
res_type, configs, "resource type %s was not found" % res_type
)
for config in configs[res_type]:
print(config.get_config_name_friendly())
self.assertIn(
config, config_set, "config %r was not expected" % config
)
config_set.remove(config)
self.assertEqual(
len(config_set), 0, "configs were not found: %s" % config_set
)
unexpected_types = set(TEST_CONFIGS.keys()) - set(configs.keys())
self.assertEqual(
len(unexpected_types),
0,
"received unexpected resource types: %s" % unexpected_types,
)
def testFallback(self):
a = apk.APK(
os.path.join(test_dir, 'data/APK/com.teleca.jamendo_35.apk')
)
# Should use the fallback
self.assertEqual(a.get_app_name(), "Jamendo")
res_parser = a.get_android_resources()
res_id = int(a.get_attribute_value('application', 'label')[1:], 16)
# Default Mode, no config
self.assertEqual(len(res_parser.get_res_configs(res_id)), 2)
# With default config, but fallback
self.assertEqual(
len(
res_parser.get_res_configs(
res_id, axml.ARSCResTableConfig.default_config()
)
),
1,
)
# With default config but no fallback
self.assertEqual(
len(
res_parser.get_res_configs(
res_id,
axml.ARSCResTableConfig.default_config(),
fallback=False,
)
),
0,
)
# Also test on resolver:
self.assertListEqual(
list(
map(itemgetter(1), res_parser.get_resolved_res_configs(res_id))
),
["Jamendo", "Jamendo"],
)
self.assertListEqual(
list(
map(
itemgetter(1),
res_parser.get_resolved_res_configs(
res_id, axml.ARSCResTableConfig.default_config()
),
)
),
["Jamendo"],
)
def testIDParsing(self):
parser = axml.ARSCParser.parse_id
self.assertEqual(parser('@DEADBEEF'), (0xDEADBEEF, None))
self.assertEqual(parser('@android:DEADBEEF'), (0xDEADBEEF, 'android'))
self.assertEqual(parser('@foobar:01020304'), (0x01020304, 'foobar'))
with self.assertRaises(ValueError):
parser('@whatisthis')
with self.assertRaises(ValueError):
parser('#almost')
with self.assertRaises(ValueError):
parser('@android:NONOTHEX')
with self.assertRaises(ValueError):
parser('@android:00')
if __name__ == '__main__':
unittest.main()