Files
Mobile-Security-Framework-M…/mobsf/StaticAnalyzer/views/android/converter.py
T
Ajin Abraham 3cf21cec16 4.1.9 (#2449)
* 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
2024-11-11 15:44:03 -08:00

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