mirror of
https://github.com/MobSF/Mobile-Security-Framework-MobSF.git
synced 2026-10-11 22:50:09 +00:00
* Anti-analysis bypass - JADX fallback to DEX files on APK decompilation failure - apktool fallback to androguard for AndroidManifest.xml extraction - apksigner.jar fallback to apksigtool/androguard for signature version extraction - Graceful erorrs for failures instead of exceptions
156 lines
5.0 KiB
Python
Executable File
156 lines
5.0 KiB
Python
Executable File
# -*- coding: utf_8 -*-
|
|
"""Module holding the functions for converting."""
|
|
|
|
import glob
|
|
import logging
|
|
import os
|
|
import platform
|
|
import shutil
|
|
import subprocess
|
|
import threading
|
|
import stat
|
|
from pathlib import Path
|
|
|
|
from django.conf import settings
|
|
|
|
from mobsf.MobSF.utils import (
|
|
append_scan_status,
|
|
filename_from_path,
|
|
find_java_binary,
|
|
is_file_exists,
|
|
settings_enabled,
|
|
)
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def get_dex_files(app_dir):
|
|
"""Get all Dex Files for analysis."""
|
|
glob_pattern = app_dir + '*.dex'
|
|
return glob.glob(glob_pattern)
|
|
|
|
|
|
def dex_2_smali(checksum, app_dir, tools_dir):
|
|
"""Run dex2smali."""
|
|
try:
|
|
if not settings_enabled('DEX2SMALI_ENABLED'):
|
|
return
|
|
msg = 'Converting DEX to Smali'
|
|
logger.info(msg)
|
|
append_scan_status(checksum, msg)
|
|
dexes = get_dex_files(app_dir)
|
|
for dex_path in dexes:
|
|
try:
|
|
logger.info('Converting %s to Smali Code',
|
|
filename_from_path(dex_path))
|
|
if (len(settings.BACKSMALI_BINARY) > 0
|
|
and is_file_exists(settings.BACKSMALI_BINARY)):
|
|
bs_path = settings.BACKSMALI_BINARY
|
|
else:
|
|
bs_path = os.path.join(tools_dir, 'baksmali-2.5.2.jar')
|
|
output = os.path.join(app_dir, 'smali_source/')
|
|
smali = [
|
|
find_java_binary(),
|
|
'-jar',
|
|
bs_path,
|
|
'd',
|
|
dex_path,
|
|
'-o',
|
|
output,
|
|
]
|
|
trd = threading.Thread(target=subprocess.call, args=(smali,))
|
|
trd.daemon = True
|
|
trd.start()
|
|
except Exception:
|
|
# Fixes a bug #2014
|
|
pass
|
|
except Exception as exp:
|
|
msg = 'Failed to convert DEX to Smali'
|
|
logger.exception(msg)
|
|
append_scan_status(checksum, msg, repr(exp))
|
|
|
|
|
|
def apk_2_java(checksum, app_path, app_dir, dwd_tools_dir):
|
|
"""Run JADX to decompile APK or all DEX files to Java source code."""
|
|
try:
|
|
jadx_version = '1.5.0'
|
|
jadx_base_path = Path(dwd_tools_dir) / 'jadx' / f'jadx-{jadx_version}' / 'bin'
|
|
output_dir = Path(app_dir) / 'java_source'
|
|
|
|
msg = 'Decompiling APK to Java with JADX'
|
|
logger.info(msg)
|
|
append_scan_status(checksum, msg)
|
|
|
|
# Clean output directory if it exists
|
|
if output_dir.exists():
|
|
shutil.rmtree(output_dir, ignore_errors=True)
|
|
|
|
# Determine JADX executable path
|
|
if (len(settings.JADX_BINARY) > 0
|
|
and is_file_exists(settings.JADX_BINARY)):
|
|
jadx = Path(settings.JADX_BINARY)
|
|
elif platform.system() == 'Windows':
|
|
jadx = jadx_base_path / 'jadx.bat'
|
|
else:
|
|
jadx = jadx_base_path / 'jadx'
|
|
|
|
# Ensure JADX has execute permissions
|
|
if not os.access(str(jadx), os.X_OK):
|
|
os.chmod(str(jadx), stat.S_IEXEC)
|
|
|
|
# Prepare the base arguments for JADX
|
|
def run_jadx(arguments):
|
|
"""Run JADX command with the specified arguments."""
|
|
with open(os.devnull, 'w') as fnull:
|
|
return subprocess.run(
|
|
arguments,
|
|
stdout=fnull,
|
|
stderr=subprocess.STDOUT,
|
|
timeout=settings.JADX_TIMEOUT)
|
|
|
|
# First attempt to decompile APK
|
|
args = [
|
|
str(jadx), '-ds', str(output_dir),
|
|
'-q', '-r', '--show-bad-code', app_path]
|
|
result = run_jadx(args)
|
|
if result.returncode == 0:
|
|
return # Success
|
|
|
|
# If APK decompilation fails, attempt to decompile all DEX files recursively
|
|
msg = 'Decompiling with JADX failed, attempting on all DEX files'
|
|
logger.warning(msg)
|
|
append_scan_status(checksum, msg)
|
|
|
|
dex_files = Path(app_path).parent.rglob('*.dex')
|
|
decompile_failed = False
|
|
|
|
for dex_file in dex_files:
|
|
msg = f'Decompiling {dex_file.name} with JADX'
|
|
logger.info(msg)
|
|
append_scan_status(checksum, msg)
|
|
|
|
# Update argument to point to the current DEX file
|
|
args[-1] = str(dex_file)
|
|
result_dex = run_jadx(args)
|
|
|
|
if result_dex.returncode != 0:
|
|
decompile_failed = True
|
|
msg = f'Decompiling with JADX failed for {dex_file.name}'
|
|
logger.error(msg)
|
|
append_scan_status(checksum, msg)
|
|
|
|
if decompile_failed:
|
|
msg = 'Some DEX files failed to decompile'
|
|
logger.error(msg)
|
|
append_scan_status(checksum, msg)
|
|
|
|
except subprocess.TimeoutExpired as exp:
|
|
msg = 'Decompiling with JADX timed out'
|
|
logger.warning(msg)
|
|
append_scan_status(checksum, msg, repr(exp))
|
|
except Exception as exp:
|
|
msg = 'Decompiling with JADX failed'
|
|
logger.exception(msg)
|
|
append_scan_status(checksum, msg, repr(exp))
|