Merge branch 'master' into sign

This commit is contained in:
erev0s 2024-06-03 22:38:29 +03:00 committed by GitHub
commit 029d264d0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 87 additions and 89 deletions

View File

@ -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");

View File

@ -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"

View File

@ -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 = []

View File

@ -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

View File

@ -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(

View File

@ -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.

View File

@ -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)
"""

View File

@ -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())

View File

@ -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

View File

@ -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"

View File

@ -12,6 +12,5 @@ loguru
apkInspector>=1.2.1
matplotlib
networkx
PyQt5
pyyaml
oscrypto>=1.3.0

View File

@ -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

View File

@ -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')

View File

@ -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 = [