androguard/main.py

460 lines
17 KiB
Python
Raw Normal View History

# core modules
import os
import re
import shutil
import sys
# 3rd party modules
from lxml import etree
2022-07-04 06:51:52 +00:00
from loguru import logger
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters.terminal import TerminalFormatter
# internal modules
from androguard.core import androconf
from androguard.core import apk
from androguard.core.axml import AXMLPrinter
2022-07-04 06:51:52 +00:00
from androguard.util import readFile
from androguard.ui import DynamicUI
2019-01-02 18:46:29 +00:00
def androaxml_main(inp, outp=None, resource=None):
ret_type = androconf.is_android(inp)
if ret_type == "APK":
a = apk.APK(inp)
2019-01-02 18:46:29 +00:00
if resource:
if resource not in a.files:
logger.error("The APK does not contain a file called '{}'".format(resource), file=sys.stderr)
2019-01-02 18:46:29 +00:00
sys.exit(1)
axml = AXMLPrinter(a.get_file(resource)).get_xml_obj()
else:
axml = a.get_android_manifest_xml()
elif ".xml" in inp:
2022-07-04 06:51:52 +00:00
axml = AXMLPrinter(readFile(inp)).get_xml_obj()
else:
logger.error("Unknown file type")
2019-01-02 18:46:29 +00:00
sys.exit(1)
buff = etree.tostring(axml, pretty_print=True, encoding="utf-8")
if outp:
with open(outp, "wb") as fd:
fd.write(buff)
else:
sys.stdout.write(highlight(buff.decode("UTF-8"), get_lexer_by_name("xml"), TerminalFormatter()))
def androarsc_main(arscobj, outp=None, package=None, typ=None, locale=None):
package = package or arscobj.get_packages_names()[0]
ttype = typ or "public"
locale = locale or '\x00\x00'
# TODO: be able to dump all locales of a specific type
# TODO: be able to recreate the structure of files when developing, eg a
# res folder with all the XML files
if not hasattr(arscobj, "get_{}_resources".format(ttype)):
print("No decoder found for type: '{}'! Please open a bug report."
.format(ttype),
file=sys.stderr)
sys.exit(1)
x = getattr(arscobj, "get_" + ttype + "_resources")(package, locale)
buff = etree.tostring(etree.fromstring(x),
pretty_print=True,
encoding="UTF-8")
if outp:
with open(outp, "wb") as fd:
fd.write(buff)
else:
2019-01-03 09:37:59 +00:00
sys.stdout.write(highlight(buff.decode("UTF-8"), get_lexer_by_name("xml"), TerminalFormatter()))
def export_apps_to_format(filename,
s,
output,
methods_filter=None,
jar=None,
decompiler_type=None,
form=None):
from androguard.misc import clean_file_name
from androguard.core.dex import DEX
from androguard.core.bytecode import method2dot, method2format
from androguard.decompiler import decompiler
print("Dump information {} in {}".format(filename, output))
if not os.path.exists(output):
print("Create directory %s" % output)
os.makedirs(output)
else:
print("Clean directory %s" % output)
androconf.rrmdir(output)
os.makedirs(output)
methods_filter_expr = None
if methods_filter:
methods_filter_expr = re.compile(methods_filter)
dump_classes = []
for _, vm, vmx in s.get_objects_dex():
print("Decompilation ...", end=' ')
sys.stdout.flush()
if decompiler_type == "dex2jad":
vm.set_decompiler(decompiler.DecompilerDex2Jad(vm,
androconf.CONF["BIN_DEX2JAR"],
androconf.CONF["BIN_JAD"],
androconf.CONF["TMP_DIRECTORY"]))
elif decompiler_type == "dex2winejad":
vm.set_decompiler(decompiler.DecompilerDex2WineJad(vm,
androconf.CONF["BIN_DEX2JAR"],
androconf.CONF["BIN_WINEJAD"],
androconf.CONF["TMP_DIRECTORY"]))
elif decompiler_type == "ded":
vm.set_decompiler(decompiler.DecompilerDed(vm,
androconf.CONF["BIN_DED"],
androconf.CONF["TMP_DIRECTORY"]))
elif decompiler_type == "dex2fernflower":
vm.set_decompiler(decompiler.DecompilerDex2Fernflower(vm,
androconf.CONF["BIN_DEX2JAR"],
androconf.CONF["BIN_FERNFLOWER"],
androconf.CONF["OPTIONS_FERNFLOWER"],
androconf.CONF["TMP_DIRECTORY"]))
print("End")
if jar:
print("jar ...", end=' ')
filenamejar = decompiler.Dex2Jar(vm,
androconf.CONF["BIN_DEX2JAR"],
androconf.CONF["TMP_DIRECTORY"]).get_jar()
shutil.move(filenamejar, os.path.join(output, "classes.jar"))
print("End")
for method in vm.get_methods():
if methods_filter_expr:
msig = "{}{}{}".format(method.get_class_name(), method.get_name(),
method.get_descriptor())
if not methods_filter_expr.search(msig):
continue
# Current Folder to write to
filename_class = valid_class_name(str(method.get_class_name()))
filename_class = os.path.join(output, filename_class)
create_directory(filename_class)
print("Dump {} {} {} ...".format(method.get_class_name(),
method.get_name(),
method.get_descriptor()), end=' ')
filename = clean_file_name(os.path.join(filename_class, method.get_short_string()))
buff = method2dot(vmx.get_method(method))
# Write Graph of method
if form:
print("%s ..." % form, end=' ')
method2format(filename + "." + form, form, None, buff)
# Write the Java file for the whole class
if str(method.get_class_name()) not in dump_classes:
print("source codes ...", end=' ')
current_class = vm.get_class(method.get_class_name())
current_filename_class = valid_class_name(str(current_class.get_name()))
current_filename_class = os.path.join(output, current_filename_class + ".java")
with open(current_filename_class, "w") as fd:
fd.write(current_class.get_source())
dump_classes.append(method.get_class_name())
# Write SMALI like code
print("bytecodes ...", end=' ')
bytecode_buff = DEX.get_bytecodes_method(vm, vmx, method)
with open(filename + ".ag", "w") as fd:
fd.write(bytecode_buff)
print()
def valid_class_name(class_name):
if class_name[-1] == ";":
class_name = class_name[1:-1]
return os.path.join(*class_name.split("/"))
def create_directory(pathdir):
if not os.path.exists(pathdir):
os.makedirs(pathdir)
def androlyze_main(session, filename):
"""
Start an interactive shell
:param session: Session file to load
:param filename: File to analyze, can be APK or DEX (or ODEX)
"""
from colorama import Fore
import colorama
import atexit
2022-07-04 06:51:52 +00:00
from IPython.terminal.embed import embed
2022-07-04 06:51:52 +00:00
from traitlets.config import Config
from androguard.core.androconf import ANDROGUARD_VERSION, CONF
2022-07-19 07:10:50 +00:00
from androguard.session import Session
2022-07-04 06:51:52 +00:00
from androguard.core import dex, apk
from androguard.core.analysis.analysis import Analysis
2022-07-18 12:39:23 +00:00
from androguard.pentest import Pentest
from androguard.ui import DynamicUI
from androguard.misc import AnalyzeAPK
colorama.init()
if session:
logger.info("Restoring session '{}'...".format(session))
s = CONF['SESSION'] = Load(session)
logger.info("Successfully restored {}".format(s))
# TODO Restore a, d, dx etc...
else:
s = CONF["SESSION"] = Session(export_ipython=True)
if filename:
("Loading apk {}...".format(os.path.basename(filename)))
logger.info("Please be patient, this might take a while.")
filetype = androconf.is_android(filename)
logger.info("Found the provided file is of type '{}'".format(filetype))
if filetype not in ['DEX', 'DEY', 'APK']:
logger.error(Fore.RED + "This file type is not supported by androlyze for auto loading right now!" + Fore.RESET, file=sys.stderr)
logger.error("But your file is still available:")
logger.error(">>> filename")
logger.error(repr(filename))
print()
else:
with open(filename, "rb") as fp:
raw = fp.read()
2022-07-18 12:39:23 +00:00
h = s.add(filename, raw)
logger.info("Added file to session: SHA256::{}".format(h))
if filetype == 'APK':
logger.info("Loaded APK file...")
a, d, dx = s.get_objects_apk(digest=h)
2022-07-18 12:39:23 +00:00
print(">>> filename")
print(filename)
print(">>> a")
print(a)
print(">>> d")
print(d)
print(">>> dx")
print(dx)
print()
elif filetype in ['DEX', 'DEY']:
logger.info("Loaded DEX file...")
for h_, d, dx in s.get_objects_dex():
if h == h_:
break
print(">>> d")
print(d)
print(">>> dx")
print(dx)
print()
def shutdown_hook():
"""Save the session on exit, if wanted"""
if not s.isOpen():
return
try:
res = input("Do you want to save the session? (y/[n])?").lower()
except (EOFError, KeyboardInterrupt):
pass
else:
if res == "y":
# TODO: if we already started from a session, probably we want to save it under the same name...
# TODO: be able to take any filename you want
fname = s.save()
print("Saved Session to file: '{}'".format(fname))
cfg = Config()
_version_string = "Androguard version {}".format(ANDROGUARD_VERSION)
ipshell = embed(config=cfg, banner1="{} started".format(_version_string))
atexit.register(shutdown_hook)
ipshell()
def androsign_main(args_apk, args_hash, args_all, show):
from androguard.core.apk import APK
from androguard.util import get_certificate_name_string
import hashlib
2018-12-14 16:09:03 +00:00
import binascii
import traceback
from colorama import Fore, Style
2018-12-14 16:09:03 +00:00
from asn1crypto import x509, keys
# Keep the list of hash functions in sync with cli/entry_points.py:sign
hashfunctions = dict(md5=hashlib.md5,
sha1=hashlib.sha1,
sha256=hashlib.sha256,
sha512=hashlib.sha512,
)
if args_hash.lower() not in hashfunctions:
print("Hash function {} not supported!"
.format(args_hash.lower()), file=sys.stderr)
print("Use one of {}"
.format(", ".join(hashfunctions.keys())), file=sys.stderr)
sys.exit(1)
for path in args_apk:
try:
a = APK(path)
print("{}, package: '{}'".format(os.path.basename(path), a.get_package()))
print("Is signed v1: {}".format(a.is_signed_v1()))
print("Is signed v2: {}".format(a.is_signed_v2()))
print("Is signed v3: {}".format(a.is_signed_v3()))
certs = set(a.get_certificates_der_v3() + a.get_certificates_der_v2() + [a.get_certificate_der(x) for x in a.get_signature_names()])
2018-12-14 16:09:03 +00:00
pkeys = set(a.get_public_keys_der_v3() + a.get_public_keys_der_v2())
if len(certs) > 0:
print("Found {} unique certificates".format(len(certs)))
for cert in certs:
if show:
x509_cert = x509.Certificate.load(cert)
print("Issuer:", get_certificate_name_string(x509_cert.issuer, short=True))
print("Subject:", get_certificate_name_string(x509_cert.subject, short=True))
print("Serial Number:", hex(x509_cert.serial_number))
print("Hash Algorithm:", x509_cert.hash_algo)
print("Signature Algorithm:", x509_cert.signature_algo)
print("Valid not before:", x509_cert['tbs_certificate']['validity']['not_before'].native)
print("Valid not after:", x509_cert['tbs_certificate']['validity']['not_after'].native)
if not args_all:
print("{} {}".format(args_hash.lower(), hashfunctions[args_hash.lower()](cert).hexdigest()))
else:
for k, v in hashfunctions.items():
print("{} {}".format(k, v(cert).hexdigest()))
print()
2018-12-14 16:09:03 +00:00
if len(certs) > 0:
print("Found {} unique public keys associated with the certs".format(len(pkeys)))
for public_key in pkeys:
if show:
x509_public_key = keys.PublicKeyInfo.load(public_key)
print("PublicKey Algorithm:", x509_public_key.algorithm)
print("Bit Size:", x509_public_key.bit_size)
print("Fingerprint:", binascii.hexlify(x509_public_key.fingerprint))
try:
print("Hash Algorithm:", x509_public_key.hash_algo)
except ValueError as ve:
# RSA pkey does not have an hash algorithm
pass
print()
except:
print(Fore.RED + "Error in {}".format(os.path.basename(path)) + Style.RESET_ALL, file=sys.stderr)
traceback.print_exc(file=sys.stderr)
if len(args_apk) > 1:
print()
def androdis_main(offset, size, dex_file):
from androguard.core.dex import DEX
with open(dex_file, "rb") as fp:
buf = fp.read()
d = DEX(buf)
2019-10-05 17:22:20 +00:00
if size == 0 and offset == 0:
# Assume you want to just get a disassembly of all classes and methods
for cls in d.get_classes():
print("# CLASS: {}".format(cls.get_name()))
for m in cls.get_methods():
print("## METHOD: {} {} {}".format(m.get_access_flags_string(), m.get_name(), m.get_descriptor()))
for idx, ins in m.get_instructions_idx():
print('{:08x} {}'.format(idx, ins.disasm()))
print()
print()
else:
if size == 0:
size = len(buf)
if d:
idx = offset
for nb, i in enumerate(d.disassemble(offset, size)):
print("%-8d(%08x)" % (nb, idx), end=' ')
i.show(idx)
print()
idx += i.get_length()
def androtrace_main(apk_file, list_modules, live=False, enable_ui=False):
2022-07-18 12:39:23 +00:00
from androguard.pentest import Pentest
2022-07-19 07:10:50 +00:00
from androguard.session import Session
2022-07-18 12:39:23 +00:00
s = Session()
2022-07-27 11:33:27 +00:00
if not live:
with open(apk_file, "rb") as fp:
raw = fp.read()
2022-07-18 12:39:23 +00:00
2022-07-27 11:33:27 +00:00
h = s.add(apk_file, raw)
logger.info("Added file to session: SHA256::{}".format(h))
2022-07-18 12:39:23 +00:00
p = Pentest()
p.print_devices()
p.connect_default_usb()
p.start_trace(apk_file, s, list_modules, live=live)
if enable_ui:
logger.remove(1)
from prompt_toolkit.eventloop.inputhook import InputHookContext, set_eventloop_with_inputhook
from prompt_toolkit.application import get_app
import time
time.sleep(1)
ui = DynamicUI(p.message_queue)
def inputhook(inputhook_context: InputHookContext):
while not inputhook_context.input_is_ready():
if ui.process_data():
get_app().invalidate()
else:
time.sleep(0.1)
set_eventloop_with_inputhook(inputhook=inputhook)
ui.run()
else:
logger.warning("Type 'e' to exit the strace ")
s = ""
while (s!='e') and (not p.is_detached()):
s = input("Type 'e' to exit:")
2022-08-29 10:01:48 +00:00
def androdump_main(package_name, list_modules):
from androguard.pentest import Pentest
from androguard.session import Session
s = Session()
p = Pentest()
p.print_devices()
p.connect_default_usb()
p.start_trace(package_name, s, list_modules, live=True, dump=True)