1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! SUPER Android Analyzer launcher.
//!
//! This code controls the CLI of the application and launches the analysis of the application
//! using the core library.

#![forbid(anonymous_parameters)]
#![warn(clippy::pedantic)]
#![deny(
    clippy::all,
    variant_size_differences,
    unused_results,
    unused_qualifications,
    unused_import_braces,
    unsafe_code,
    trivial_numeric_casts,
    trivial_casts,
    missing_docs,
    unused_extern_crates,
    missing_debug_implementations,
    missing_copy_implementations,
    rust_2018_idioms
)]

use std::{
    collections::BTreeMap,
    io::{self, Write},
    thread::sleep,
    time::{Duration, Instant},
};

use colored::Colorize;
use failure::{Error, ResultExt};
use log::{error, log_enabled, Level};

use super_analyzer_core::{
    analyze_package, cli, initialize_config, initialize_logger, Benchmark, ErrorKind, BANNER,
};

/// Program entry point.
///
/// This function will just call the `run()` function and report any fatal error that comes out
/// of it. It will also exit with a non-zero exit code if things go wrong.
fn main() {
    // Call the `run()` function and check for errors.
    if let Err(e) = run() {
        error!("{}", e);

        // After printing the error, print the causes, in order.
        for e in e.iter_causes() {
            println!("\t{}{}", "Caused by: ".bold(), e);
        }

        // If the verbose mode is not enabled, we add a message so that the user knows that can
        // get further information with the `-v` flag in the CLI.
        if !log_enabled!(Level::Debug) {
            println!(
                "If you need more information, try to run the program again with the {} flag.",
                "-v".bold()
            );
        }

        // Exit with a non-zero exit code.
        ::std::process::exit(1);
    }
}

/// Execute the analysis.
///
/// This runs the actual analysis. It checks the CLI, creates the logger, loads the configuration
/// and if everything goes well, it starts the analysis. It also runs benchmarks and shows the
/// results.
fn run() -> Result<(), Error> {
    // Check the CLI arguments.
    let cli = cli::generate().get_matches();
    let verbose = cli.is_present("verbose");
    // Initialize all logger, specifying if the user wanted verbose mode.
    initialize_logger(verbose).context("could not initialize the logger")?;

    // Load the configuration.
    let mut config = initialize_config(&cli)?;

    // Check the configuration and return an error with the loaded files.
    if !config.check() {
        let mut error_string = String::from("configuration errors were found:\n");
        for error in config.errors() {
            error_string.push_str(&error);
            error_string.push('\n');
        }
        error_string.push_str(
            "the configuration was loaded, in order, from the following files: \
             \n\t- Default built-in configuration\n",
        );
        for file in config.loaded_config_files() {
            error_string.push_str(&format!("\t- {}\n", file.display()));
        }

        return Err(ErrorKind::Config {
            message: error_string,
        }
        .into());
    }

    // Print the banner if we are in verbose mode.
    if config.is_verbose() {
        for c in BANNER.chars() {
            print!("{}", c);
            io::stdout().flush().expect("error flushing stdout");
            sleep(Duration::from_millis(3));
        }
        println!(
            "Welcome to the SUPER Android Analyzer. We will now try to audit the given application."
        );
        println!(
            "You activated the verbose mode. {}",
            "May Tux be with you!".bold()
        );
        println!();
        sleep(Duration::from_millis(1250));
    }

    // Start benchmarks.
    let mut benchmarks = BTreeMap::new();

    let total_start = Instant::now();
    // Analyze each apk one by one.
    for package in config.app_packages() {
        config.reset_force();
        analyze_package(package, &mut config, &mut benchmarks)
            .context("application analysis failed")?;
    }

    // Print benchmarks if in benchmark mode.
    if config.is_bench() {
        let total_time = Benchmark::new("Total time", total_start.elapsed());
        println!();
        println!("{}", "Benchmarks:".bold());
        for (package_name, benchmarks) in benchmarks {
            println!("{}:", package_name.italic());
            for bench in benchmarks {
                println!("{}", bench);
            }
            println!();
        }
        println!("{}", total_time);
    }

    Ok(())
}