diff --git a/README_mobile_analyzer.md b/README_mobile_analyzer.md new file mode 100644 index 0000000..d42909a --- /dev/null +++ b/README_mobile_analyzer.md @@ -0,0 +1,54 @@ +# Mobile App Analyzer + +This tool analyzes Android APK files for potential security issues, including high-risk permissions, hardcoded secrets, and indicators of phishing or scams. + +## Features + +* **APK Decompilation:** Uses `apktool` to decompile the APK for source code analysis. +* **Permission Analysis:** Extracts and identifies high-risk permissions from the `AndroidManifest.xml`. +* **Secret Scanning:** Scans the decompiled code for hardcoded secrets like API keys and private keys. +* **Scam Detection:** Analyzes text content in the app's files for suspicious URLs, phishing keywords, and other scam indicators. + +## Prerequisites + +* Python 3.x +* `apktool`: Must be installed and available in your system's PATH. +* `aapt`: Must be installed and available in your system's PATH. + +You can typically install these tools on a Debian-based system (like Ubuntu) with: +`sudo apt-get install apktool aapt` + +## How to Run + +1. Navigate to the root directory of this repository. +2. Run the analyzer from your terminal, passing the path to the APK file you want to analyze: + + ```bash + python mobile_analyzer_main.py /path/to/your/app.apk + ``` + +### Options + +* `--keep-files`: Use this flag to prevent the script from deleting the `decompiled_apk` directory after the analysis is complete. This is useful for debugging or manual inspection. + + ```bash + python mobile_analyzer_main.py /path/to/your/app.apk --keep-files + ``` + +## How to Interpret the Output + +The tool will print a report to the console with the following sections: + +* **High-Risk Permissions Found:** A list of permissions that could potentially be abused to access sensitive user data or control the device. +* **Potential Secrets Found:** A list of files that may contain hardcoded sensitive data. Review these files carefully. +* **Scam Indicators Found:** A list of files containing suspicious URLs, keywords, or other patterns that might indicate phishing or other scams. + +## Limitations + +* **Android Only:** This tool currently only supports Android APK files. iOS app analysis is not supported. +* **Static Analysis Only:** The analysis is purely static (it only examines the code and files). It does not run the app or monitor its behavior at runtime. +* **Not Foolproof:** This tool uses patterns and heuristics to find potential issues. It is not guaranteed to find all vulnerabilities, and it may produce false positives. Always use your judgment and, if possible, combine this with other security testing methods. + +## Disclaimer + +This tool is for educational and research purposes only. The user is responsible for any use of this tool. Do not use it to analyze apps for which you do not have permission. diff --git a/mobile_analyzer/__init__.py b/mobile_analyzer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mobile_analyzer/analyzer.py b/mobile_analyzer/analyzer.py new file mode 100644 index 0000000..9809191 --- /dev/null +++ b/mobile_analyzer/analyzer.py @@ -0,0 +1,192 @@ +import subprocess +import os +import re +import sys + +# HACK: This is not ideal, but the project is structured as a collection of +# top-level scripts and not as a single installable package. This allows us +# to import modules from sibling directories. +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + +from sensitive_data_scanner.scanner import scan_directory as scan_for_secrets +from social_media_analyzer.scam_detector import analyze_text_for_scams + + +# Based on Android documentation and security best practices. +# These permissions grant access to sensitive user data or system control. +HIGH_RISK_PERMISSIONS = [ + "android.permission.READ_CALENDAR", + "android.permission.WRITE_CALENDAR", + "android.permission.CAMERA", + "android.permission.READ_CONTACTS", + "android.permission.WRITE_CONTACTS", + "android.permission.GET_ACCOUNTS", + "android.permission.ACCESS_FINE_LOCATION", + "android.permission.ACCESS_COARSE_LOCATION", + "android.permission.RECORD_AUDIO", + "android.permission.READ_PHONE_STATE", + "android.permission.READ_PHONE_NUMBERS", + "android.permission.CALL_PHONE", + "android.permission.ANSWER_PHONE_CALLS", + "android.permission.ADD_VOICEMAIL", + "android.permission.USE_SIP", + "android.permission.PROCESS_OUTGOING_CALLS", + "android.permission.READ_CALL_LOG", + "android.permission.WRITE_CALL_LOG", + "com.android.voicemail.permission.ADD_VOICEMAIL", + "android.permission.BODY_SENSORS", + "android.permission.SEND_SMS", + "android.permission.RECEIVE_SMS", + "android.permission.READ_SMS", + "android.permission.RECEIVE_WAP_PUSH", + "android.permission.RECEIVE_MMS", + "android.permission.READ_EXTERNAL_STORAGE", + "android.permission.WRITE_EXTERNAL_STORAGE", + "android.permission.SYSTEM_ALERT_WINDOW", + "android.permission.WRITE_SETTINGS", + "android.permission.REQUEST_INSTALL_PACKAGES", + "android.permission.ACCESS_BACKGROUND_LOCATION", +] + +def decompile_apk(apk_path, output_dir): + """ + Decompiles an APK file using apktool. + + Args: + apk_path (str): The path to the APK file. + output_dir (str): The directory to store the decompiled code. + + Returns: + bool: True if decompilation was successful, False otherwise. + """ + if not os.path.exists(apk_path): + print(f"Error: APK file not found at {apk_path}") + return False + + print(f"Decompiling {apk_path} to {output_dir}...") + try: + # Using -f to force overwrite the output directory if it exists + command = ["apktool", "d", "-f", apk_path, "-o", output_dir] + result = subprocess.run(command, capture_output=True, text=True, check=True) + print("Decompilation successful.") + return True + except subprocess.CalledProcessError as e: + print("Error during decompilation:") + print(e.stderr) + return False + except FileNotFoundError: + print("Error: 'apktool' not found. Make sure it is installed and in your PATH.") + return False + +def get_permissions(apk_path): + """ + Extracts permissions from an APK's AndroidManifest.xml using aapt. + + Args: + apk_path (str): The path to the APK file. + + Returns: + list: A list of permissions found in the APK. + """ + if not os.path.exists(apk_path): + print(f"Error: APK file not found at {apk_path}") + return [] + + print(f"Extracting permissions from {apk_path}...") + try: + command = ["aapt", "dump", "permissions", apk_path] + result = subprocess.run(command, capture_output=True, text=True, check=True) + + # Regex to find package permissions + permissions = re.findall(r"uses-permission: name='([^']*)'", result.stdout) + print(f"Found {len(permissions)} permissions.") + return permissions + except subprocess.CalledProcessError as e: + print("Error extracting permissions:") + print(e.stderr) + return [] + except FileNotFoundError: + print("Error: 'aapt' not found. Make sure it is installed and in your PATH.") + return [] + +def check_high_risk_permissions(permissions): + """ + Checks a list of permissions against the high-risk permissions list. + + Args: + permissions (list): The list of permissions from an APK. + + Returns: + list: A list of high-risk permissions found in the APK. + """ + found_high_risk = [] + for perm in permissions: + if perm in HIGH_RISK_PERMISSIONS: + found_high_risk.append(perm) + + print(f"Found {len(found_high_risk)} high-risk permissions.") + return found_high_risk + + +def scan_for_sensitive_data(decompiled_dir): + """ + Scans a directory for sensitive data using the sensitive_data_scanner module. + + Args: + decompiled_dir (str): The path to the directory of decompiled code. + + Returns: + dict: A dictionary of findings. + """ + print("\nScanning for sensitive data...") + findings = scan_for_secrets(decompiled_dir) + if findings: + print(f"Found sensitive data in {len(findings)} files.") + else: + print("No sensitive data found.") + return findings + +def scan_for_scam_indicators(decompiled_dir): + """ + Scans files in a directory for scam indicators using the scam_detector module. + + Args: + decompiled_dir (str): The path to the directory of decompiled code. + + Returns: + dict: A dictionary of findings, where keys are file paths. + """ + print("\nScanning for scam indicators (phishing, suspicious URLs)...") + all_findings = {} + + # Extensions of text-like files to scan. + # Smali, xml, and yml are common in decompiled APKs. + scan_extensions = {'.smali', '.xml', '.yml', '.yaml', '.json', '.html', '.js', '.txt'} + + for root, _, files in os.walk(decompiled_dir): + for filename in files: + # Check if the file has one of the scannable extensions + if not any(filename.endswith(ext) for ext in scan_extensions): + continue + + filepath = os.path.join(root, filename) + try: + with open(filepath, 'r', encoding='utf-8', errors='ignore') as f: + content = f.read() + # Skip very large files to avoid performance issues + if len(content) > 1000000: # 1MB limit + continue + + analysis_result = analyze_text_for_scams(content) + if analysis_result.get("indicators_found"): + all_findings[filepath] = analysis_result + except Exception: + # Ignore files that can't be read for any reason + continue + + if all_findings: + print(f"Found scam indicators in {len(all_findings)} files.") + else: + print("No scam indicators found.") + + return all_findings diff --git a/mobile_analyzer_main.py b/mobile_analyzer_main.py new file mode 100644 index 0000000..e133799 --- /dev/null +++ b/mobile_analyzer_main.py @@ -0,0 +1,77 @@ +import argparse +import os +import shutil +import sys +from mobile_analyzer import analyzer + +def main(): + parser = argparse.ArgumentParser(description="Analyze an Android APK for security vulnerabilities.") + parser.add_argument("apk_path", help="The path to the APK file to analyze.") + parser.add_argument("--keep-files", action="store_true", help="Keep the decompiled files after analysis.") + args = parser.parse_args() + + apk_path = args.apk_path + if not os.path.exists(apk_path): + print(f"Error: APK file not found at {apk_path}") + sys.exit(1) + + # Create a directory for the decompiled code + output_dir = "decompiled_apk" + if os.path.exists(output_dir): + shutil.rmtree(output_dir) + os.makedirs(output_dir) + + print(f"--- Starting analysis for {os.path.basename(apk_path)} ---") + + # 1. Decompile the APK + if not analyzer.decompile_apk(apk_path, output_dir): + print("Failed to decompile APK. Aborting analysis.") + sys.exit(1) + + # 2. Analyze permissions + print("\n--- Analyzing Permissions ---") + all_permissions = analyzer.get_permissions(apk_path) + if all_permissions: + high_risk_permissions = analyzer.check_high_risk_permissions(all_permissions) + if high_risk_permissions: + print("\n[!] High-Risk Permissions Found:") + for perm in high_risk_permissions: + print(f" - {perm}") + else: + print("\nNo high-risk permissions found.") + else: + print("\nCould not extract permissions.") + + # 3. Scan for sensitive data + print("\n--- Scanning for Hardcoded Secrets ---") + sensitive_data = analyzer.scan_for_sensitive_data(output_dir) + if sensitive_data: + print("\n[!] Potential Secrets Found:") + for file, findings in sensitive_data.items(): + print(f" - In file: {file}") + for finding_type, matches in findings.items(): + print(f" - {finding_type}: {len(matches)} found") + else: + print("\nNo hardcoded secrets found.") + + # 4. Scan for scam indicators + print("\n--- Scanning for Phishing and Scam Indicators ---") + scam_indicators = analyzer.scan_for_scam_indicators(output_dir) + if scam_indicators: + print("\n[!] Scam Indicators Found:") + for file, result in scam_indicators.items(): + print(f" - In file: {file}") + for indicator in result["indicators_found"]: + print(f" - {indicator}") + else: + print("\nNo scam indicators found.") + + # Clean up the decompiled files + if not args.keep_files: + print("\nCleaning up temporary files...") + shutil.rmtree(output_dir) + + print("\n--- Analysis Complete ---") + +if __name__ == "__main__": + main()