mirror of
https://github.com/androguard/androguard.git
synced 2024-11-26 22:40:33 +00:00
Merge branch 'master' into sign
This commit is contained in:
commit
029d264d0e
@ -14,7 +14,7 @@ pip install androguard
|
||||
~~~~
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Versions >= 4.0.0 are new releases after a long time, where the project has substantial differences from the previous stable version 3.3.5 from 2019. This means that certain functionalities have been removed. If you notice an issue with your project using tha latest version, please open up an [issue](https://github.com/androguard/androguard/issues).
|
||||
> Versions >= 4.0.0 are new releases after a long time, where the project has substantial differences from the previous stable version 3.3.5 from 2019. This means that certain functionalities have been removed. If you notice an issue with your project using the latest version, please open up an [issue](https://github.com/androguard/androguard/issues).
|
||||
|
||||
## Documentation
|
||||
**Documentation contains outdated information - In progress of updating**
|
||||
@ -59,6 +59,7 @@ In alphabetical order
|
||||
* [MobSF](https://github.com/MobSF/Mobile-Security-Framework-MobSF)
|
||||
* [qiew](https://github.com/mtivadar/qiew)
|
||||
* [Quark-Engine](https://github.com/quark-engine/quark-engine)
|
||||
* [Virustotal](https://virustotal.readme.io/reference/androguard)
|
||||
* [Viper Framework](https://github.com/viper-framework/viper)
|
||||
* ... and many more!
|
||||
|
||||
@ -68,7 +69,7 @@ You are using Androguard and are not listed here? Just create a [ticket](https:/
|
||||
|
||||
### Androguard
|
||||
|
||||
Copyright (C) 2012 - 2023, Anthony Desnos (desnos at t0t0.fr)
|
||||
Copyright (C) 2012 - 2024, Anthony Desnos (desnos at t0t0.fr)
|
||||
All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -1,4 +1,4 @@
|
||||
# The current version of Androguard
|
||||
# Please use only this variable in any scripts,
|
||||
# to keep the version number the same everywhere.
|
||||
__version__ = "4.1.1"
|
||||
__version__ = "4.1.2"
|
||||
|
@ -552,7 +552,6 @@ def cg(
|
||||
from androguard.core.analysis.analysis import ExternalMethod
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import networkx as nx
|
||||
|
||||
a, d, dx = AnalyzeAPK(file_)
|
||||
|
||||
@ -569,8 +568,13 @@ def cg(
|
||||
no_isolated,
|
||||
entry_points
|
||||
)
|
||||
|
||||
|
||||
if show:
|
||||
try:
|
||||
import PyQt5
|
||||
except ImportError:
|
||||
print("PyQt5 is not installed. In most OS you can install it by running 'pip install PyQt5'.\n")
|
||||
exit()
|
||||
pos = nx.spring_layout(callgraph)
|
||||
internal = []
|
||||
external = []
|
||||
|
@ -227,7 +227,7 @@ class DEXBasicBlock:
|
||||
|
||||
def get_method(self) -> dex.EncodedMethod:
|
||||
"""
|
||||
Returns the originiating method
|
||||
Returns the originating method
|
||||
|
||||
:return: the method
|
||||
:rtype: androguard.core.dex.EncodedMethod
|
||||
@ -1462,7 +1462,7 @@ class Analysis:
|
||||
|
||||
logger.info("Adding DEX file version {}".format(vm.version))
|
||||
|
||||
# TODO: This step can easily be multithreaded, as there is no dependecy between the objects at this stage
|
||||
# TODO: This step can easily be multithreaded, as there is no dependency between the objects at this stage
|
||||
tic = time.time()
|
||||
for i, current_class in enumerate(vm.get_classes()):
|
||||
# seed ClassAnalysis objects into classes attribute and add as new class
|
||||
|
@ -31,7 +31,7 @@ from androguard.core.axml import (ARSCParser,
|
||||
TEXT,
|
||||
END_DOCUMENT)
|
||||
|
||||
# External dependecies
|
||||
# External dependencies
|
||||
from lxml.etree import Element
|
||||
import lxml.sax
|
||||
from xml.dom.pulldom import SAX2DOM
|
||||
@ -748,7 +748,7 @@ class APK:
|
||||
|
||||
At the same time, the CRC32 are calculated for the files.
|
||||
|
||||
:rtype: a dictionnary
|
||||
:rtype: a dictionary
|
||||
"""
|
||||
if self._files == {}:
|
||||
# Generate File Types / CRC List
|
||||
@ -1013,7 +1013,12 @@ class APK:
|
||||
):
|
||||
return [xml]
|
||||
return []
|
||||
tags = xml.findall(".//" + tag_name)
|
||||
tags = set()
|
||||
tags.update(xml.findall(".//" + tag_name))
|
||||
|
||||
# https://github.com/androguard/androguard/pull/1053
|
||||
# permission declared using tag <android:uses-permission...
|
||||
tags.update(xml.findall(".//" + NS_ANDROID + tag_name))
|
||||
return [
|
||||
tag for tag in tags if self.is_tag_matched(
|
||||
tag, **attribute_filter
|
||||
@ -1309,7 +1314,7 @@ class APK:
|
||||
filled_permissions = permissions.copy()
|
||||
for permission in filled_permissions:
|
||||
protection_level, label, description = filled_permissions[permission]
|
||||
if ((not label or not description)
|
||||
if ((not label or not description)
|
||||
and permission in self.permission_module_min_sdk):
|
||||
x = self.permission_module_min_sdk[permission]
|
||||
protection_level = self._update_permission_protection_level(
|
||||
|
@ -101,7 +101,7 @@ def complexToFloat(xcomplex) -> float:
|
||||
class StringBlock:
|
||||
"""
|
||||
StringBlock is a CHUNK inside an AXML File: `ResStringPool_header`
|
||||
It contains all strings, which are used by referecing to ID's
|
||||
It contains all strings, which are used by referencing to ID's
|
||||
|
||||
See http://androidxref.com/9.0.0_r3/xref/frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h#436
|
||||
"""
|
||||
@ -694,7 +694,7 @@ class AXMLParser:
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""
|
||||
Return the String assosciated with the tag name
|
||||
Return the String associated with the tag name
|
||||
"""
|
||||
if self.m_name == -1 or (self.m_event != START_TAG and self.m_event != END_TAG):
|
||||
return ''
|
||||
@ -988,7 +988,18 @@ class AXMLPrinter:
|
||||
cur[-1].append(etree.Comment(comment))
|
||||
|
||||
logger.debug("START_TAG: {} (line={})".format(tag, self.axml.m_lineNumber))
|
||||
elem = etree.Element(tag, nsmap=self.axml.nsmap)
|
||||
|
||||
try:
|
||||
elem = etree.Element(tag, nsmap=self.axml.nsmap)
|
||||
except ValueError as e:
|
||||
logger.error(e)
|
||||
# nsmap= {'<!--': 'http://schemas.android.com/apk/res/android'} | pull/1056
|
||||
if 'Invalid namespace prefix' in str(e):
|
||||
corrected_nsmap = self.clean_and_replace_nsmap(self.axml.nsmap, str(e).split("'")[1])
|
||||
elem = etree.Element(tag, nsmap=corrected_nsmap)
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
for i in range(self.axml.getAttributeCount()):
|
||||
uri = self._print_namespace(self.axml.getAttributeNamespace(i))
|
||||
@ -1029,6 +1040,16 @@ class AXMLPrinter:
|
||||
logger.warning("Not all namespace mappings were closed! Malformed AXML?")
|
||||
break
|
||||
|
||||
def clean_and_replace_nsmap(self, nsmap, invalid_prefix):
|
||||
correct_prefix = 'android'
|
||||
corrected_nsmap = {}
|
||||
for prefix, uri in nsmap.items():
|
||||
if prefix.startswith(invalid_prefix):
|
||||
corrected_nsmap[correct_prefix] = uri
|
||||
else:
|
||||
corrected_nsmap[prefix] = uri
|
||||
return corrected_nsmap
|
||||
|
||||
def get_buff(self) -> bytes:
|
||||
"""
|
||||
Returns the raw XML file without prettification applied.
|
||||
|
@ -394,7 +394,7 @@ class Session:
|
||||
|
||||
def get_objects_dex(self) -> Iterator[tuple[str, dex.DEX, Analysis]]:
|
||||
"""
|
||||
Yields all dex objects inclduing their Analysis objects
|
||||
Yields all dex objects including their Analysis objects
|
||||
|
||||
:returns: tuple of (sha256, DEX, Analysis)
|
||||
"""
|
||||
|
@ -218,7 +218,7 @@ class DynamicUI:
|
||||
|
||||
def get_available_blocks(self):
|
||||
blocks: list[Message] = []
|
||||
# Retrieve every unhandled block currently avilable in the queue
|
||||
# Retrieve every unhandled block currently available in the queue
|
||||
try:
|
||||
for _ in range(10):
|
||||
blocks.append(self.input_queue.get_nowait())
|
||||
|
@ -3,7 +3,7 @@
|
||||
import sys
|
||||
from typing import Union, BinaryIO
|
||||
|
||||
# External dependecies
|
||||
# External dependencies
|
||||
# import asn1crypto
|
||||
from asn1crypto.x509 import Name
|
||||
from loguru import logger
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "androguard"
|
||||
description = "Androguard is a full python tool to play with Android files."
|
||||
authors = ["desnos <desnos@t0t0.fr>"]
|
||||
version = "4.1.1"
|
||||
version = "4.1.2"
|
||||
license = "Apache Licence, Version 2.0"
|
||||
readme = "README.md"
|
||||
homepage = "https://github.com/androguard/androguard"
|
||||
@ -27,14 +27,6 @@ loguru = "*"
|
||||
apkInspector = ">=1.1.7"
|
||||
matplotlib = "*"
|
||||
networkx = "*"
|
||||
PyQt5 = "*"
|
||||
# address inconsistencies with pyqt5-qt5 binary release files
|
||||
# > 5.15.2 exclusively contains releases for macosx
|
||||
# 5.15.2 contains release for all, but not arm64 macosx
|
||||
PyQt5-Qt5 = [
|
||||
{version = "*", markers = "sys_platform == 'darwin'"},
|
||||
{version = "5.15.2", markers = "sys_platform != 'darwin'"}
|
||||
]
|
||||
pyyaml = "*"
|
||||
oscrypto = ">=1.3.0"
|
||||
|
||||
|
@ -12,6 +12,5 @@ loguru
|
||||
apkInspector>=1.2.1
|
||||
matplotlib
|
||||
networkx
|
||||
PyQt5
|
||||
pyyaml
|
||||
oscrypto>=1.3.0
|
@ -37,7 +37,7 @@ class AnalysisTest(unittest.TestCase):
|
||||
self.assertEqual(len(list(dx.get_external_methods())), 3116) # difficult to check
|
||||
|
||||
# TODO: the DEX header says 12795 here, but 9676 + 3116 adds up to 12792
|
||||
# JADX corroborates 9676, so I think 3116 is off, and a few unncessary
|
||||
# JADX corroborates 9676, so I think 3116 is off, and a few unnecessary
|
||||
# ExternalMethods are added somewhere
|
||||
self.assertEqual(len(list(dx.get_methods())), 12792) # dex header header->headerItem->methodIdsSize
|
||||
|
||||
|
@ -390,6 +390,8 @@ class APKTest(unittest.TestCase):
|
||||
self.assertTrue(isinstance(activities[0], str), 'activities[0] is not of type str')
|
||||
|
||||
def testAPKIntentFilters(self):
|
||||
from androguard.util import set_log
|
||||
set_log("ERROR")
|
||||
a = APK(os.path.join(test_dir, 'data/APK/a2dp.Vol_137.apk'), testzip=True)
|
||||
activities = a.get_activities()
|
||||
receivers = a.get_receivers()
|
||||
@ -399,34 +401,26 @@ class APKTest(unittest.TestCase):
|
||||
filters = a.get_intent_filters("activity", i)
|
||||
if len(filters) > 0:
|
||||
filter_list.append(filters)
|
||||
self.assertEqual([{'action': ['android.intent.action.MAIN'], 'category': ['android.intent.category.LAUNCHER']}],
|
||||
filter_list)
|
||||
filter_list = []
|
||||
for i in receivers:
|
||||
filters = a.get_intent_filters("receiver", i)
|
||||
if len(filters) > 0:
|
||||
filter_list.append(filters)
|
||||
for expected in [{
|
||||
'action': ['android.intent.action.BOOT_COMPLETED', 'android.intent.action.MY_PACKAGE_REPLACED'],
|
||||
'category': ['android.intent.category.HOME']}, {'action': ['android.appwidget.action.APPWIDGET_UPDATE']}]:
|
||||
assert expected in filter_list
|
||||
filter_list = []
|
||||
for i in services:
|
||||
filters = a.get_intent_filters("service", i)
|
||||
if len(filters) > 0:
|
||||
filter_list.append(filters)
|
||||
|
||||
pairs = zip(filter_list, [{
|
||||
'action': ['android.intent.action.MAIN'],
|
||||
'category': ['android.intent.category.LAUNCHER']
|
||||
}, {
|
||||
'action': [
|
||||
'android.intent.action.BOOT_COMPLETED',
|
||||
'android.intent.action.MY_PACKAGE_REPLACED'
|
||||
],
|
||||
'category': ['android.intent.category.HOME']
|
||||
}, {
|
||||
'action': ['android.appwidget.action.APPWIDGET_UPDATE']
|
||||
}, {
|
||||
'action': ['android.service.notification.NotificationListenerService']
|
||||
}])
|
||||
self.assertFalse(any(x != y for x, y in pairs))
|
||||
self.assertEqual(filter_list, [{'action': ['android.service.notification.NotificationListenerService']}])
|
||||
|
||||
a = APK(os.path.join(test_dir, 'data/APK/com.test.intent_filter.apk'), testzip=True)
|
||||
|
||||
activities = a.get_activities()
|
||||
activities = a.get_activities()
|
||||
receivers = a.get_receivers()
|
||||
services = a.get_services()
|
||||
@ -435,16 +429,7 @@ class APKTest(unittest.TestCase):
|
||||
filters = a.get_intent_filters("activity", i)
|
||||
if len(filters) > 0:
|
||||
filter_list.append(filters)
|
||||
for i in receivers:
|
||||
filters = a.get_intent_filters("receiver", i)
|
||||
if len(filters) > 0:
|
||||
filter_list.append(filters)
|
||||
for i in services:
|
||||
filters = a.get_intent_filters("service", i)
|
||||
if len(filters) > 0:
|
||||
filter_list.append(filters)
|
||||
|
||||
pairs = zip(filter_list, [{
|
||||
for expected in [{
|
||||
'action': ['android.intent.action.VIEW'],
|
||||
'category': [
|
||||
'android.intent.category.APP_BROWSER',
|
||||
@ -461,37 +446,28 @@ class APKTest(unittest.TestCase):
|
||||
}, {
|
||||
'action': ['android.intent.action.MAIN'],
|
||||
'category': ['android.intent.category.LAUNCHER']
|
||||
}, {
|
||||
'action': ['android.intent.action.VIEW'],
|
||||
'category':
|
||||
['android.intent.category.DEFAULT', 'android.intent.category.BROWSABLE'],
|
||||
'data': [{
|
||||
'scheme': 'testhost',
|
||||
'host': 'testscheme',
|
||||
'port': '0301',
|
||||
'path': '/testpath',
|
||||
'pathPattern': 'testpattern',
|
||||
'mimeType': 'text/html'
|
||||
}]
|
||||
}, {
|
||||
'action': ['android.intent.action.RESPOND_VIA_MESSAGE'],
|
||||
'data': [{
|
||||
'scheme': 'testhost',
|
||||
'host': 'testscheme',
|
||||
'port': '0301',
|
||||
'path': '/testpath',
|
||||
'pathPattern': 'testpattern',
|
||||
'mimeType': 'text/html'
|
||||
}, {
|
||||
'scheme': 'testscheme2',
|
||||
'host': 'testhost2',
|
||||
'port': '0301',
|
||||
'path': '/testpath2',
|
||||
'pathPattern': 'testpattern2',
|
||||
'mimeType': 'image/png'
|
||||
}]
|
||||
}])
|
||||
self.assertFalse(any(x != y for x, y in pairs))
|
||||
}]:
|
||||
assert expected in filter_list
|
||||
filter_list = []
|
||||
for i in receivers:
|
||||
filters = a.get_intent_filters("receiver", i)
|
||||
if len(filters) > 0:
|
||||
filter_list.append(filters)
|
||||
self.assertEqual(filter_list, [{'action': ['android.intent.action.VIEW'],
|
||||
'category': ['android.intent.category.DEFAULT',
|
||||
'android.intent.category.BROWSABLE'], 'data': [
|
||||
{'scheme': 'testhost', 'host': 'testscheme', 'port': '0301', 'path': '/testpath',
|
||||
'pathPattern': 'testpattern', 'mimeType': 'text/html'}]}])
|
||||
filter_list = []
|
||||
for i in services:
|
||||
filters = a.get_intent_filters("service", i)
|
||||
if len(filters) > 0:
|
||||
filter_list.append(filters)
|
||||
self.assertEqual(filter_list, [{'action': ['android.intent.action.RESPOND_VIA_MESSAGE'], 'data': [
|
||||
{'scheme': 'testhost', 'host': 'testscheme', 'port': '0301', 'path': '/testpath',
|
||||
'pathPattern': 'testpattern', 'mimeType': 'text/html'},
|
||||
{'scheme': 'testscheme2', 'host': 'testhost2', 'port': '0301', 'path': '/testpath2',
|
||||
'pathPattern': 'testpattern2', 'mimeType': 'image/png'}]}])
|
||||
|
||||
def testEffectiveTargetSdkVersion(self):
|
||||
|
||||
@ -613,7 +589,7 @@ class APKTest(unittest.TestCase):
|
||||
|
||||
def testFeatures(self):
|
||||
a = APK(os.path.join(test_dir, 'data/APK/com.example.android.tvleanback.apk'))
|
||||
self.assertListEqual(list(a.get_features()), ["android.hardware.microphone",
|
||||
self.assertListEqual(sorted(list(a.get_features())), ["android.hardware.microphone",
|
||||
"android.hardware.touchscreen",
|
||||
"android.software.leanback"])
|
||||
self.assertTrue(a.is_androidtv())
|
||||
@ -738,7 +714,7 @@ class APKTest(unittest.TestCase):
|
||||
self.assertEqual(list(a.get_all_attribute_value("activity", "name"))[0], 'com.android.galaxy4.Galaxy4')
|
||||
self.assertEqual(list(a.get_all_attribute_value("activity", "name", format_value=False))[0], '.Galaxy4')
|
||||
|
||||
# Test some formattings
|
||||
# Test some formatting
|
||||
self.assertEqual(a._format_value('foo'), 'com.android.galaxy4.foo')
|
||||
self.assertEqual(a._format_value('.foo'), 'com.android.galaxy4.foo')
|
||||
self.assertEqual(a._format_value('com.android.galaxy4.foo'), 'com.android.galaxy4.foo')
|
||||
|
@ -470,7 +470,7 @@ class InstructionTest(unittest.TestCase):
|
||||
self.assertEqual(ins.get_output(), 'v{}, {}'.format(reg, lit))
|
||||
|
||||
def testInstruction31i(self):
|
||||
"""Test functionaltiy of Instruction31i (const, const-wide/32)"""
|
||||
"""Test functionality of Instruction31i (const, const-wide/32)"""
|
||||
|
||||
# const is often used to load resources...
|
||||
tests = [
|
||||
|
Loading…
Reference in New Issue
Block a user