diff --git a/aleapp.py b/aleapp.py index 07333fe39..b05a6a3d1 100644 --- a/aleapp.py +++ b/aleapp.py @@ -3,6 +3,7 @@ import scripts.report as report import shutil import traceback +import io from scripts.search_files import * from scripts.ilapfuncs import * @@ -12,7 +13,7 @@ def main(): parser = argparse.ArgumentParser(description='ALEAPP: Android Logs, Events, and Protobuf Parser.') - parser.add_argument('-t', choices=['fs','tar','zip', 'gz'], required=False, type=str.lower, action="store", help="Input type (fs = extracted to file system folder)") + parser.add_argument('-t', choices=['fs','tar','zip', 'gz', 'ab'], required=False, type=str.lower, action="store", help="Input type (fs = extracted to file system folder)") parser.add_argument('-o', '--output_path', required=False, action="store", help='Output folder path') parser.add_argument('-i', '--input_path', required=False, action="store", help='Path to input file/folder') parser.add_argument('-p', '--artifact_paths', required=False, action="store_true", help='Text file list of artifact paths') @@ -103,6 +104,9 @@ def crunch_artifacts(search_list, extracttype, input_path, out_params, ratio, wr elif extracttype == 'zip': seeker = FileSeekerZip(input_path, out_params.temp_folder) + elif extracttype == 'ab': + seeker = FileSeekerABackup(input_path, out_params.temp_folder) + else: logfunc('Error on argument -o (input type)') return False diff --git a/aleappGUI.py b/aleappGUI.py index 7663176cd..c94fa810d 100644 --- a/aleappGUI.py +++ b/aleappGUI.py @@ -27,7 +27,10 @@ def ValidateInput(values, window): elif os.path.isdir(i_path): ext_type = 'fs' else: # must be an existing file then - if not i_path.lower().endswith('.tar') and not i_path.lower().endswith('.zip') and not i_path.lower().endswith('.gz'): + if not i_path.lower().endswith('.tar') \ + and not i_path.lower().endswith('.zip') \ + and not i_path.lower().endswith('.gz') \ + and not i_path.lower().endswith('.ab'): sg.PopupError('Input file is not a supported archive! ', i_path) return False, ext_type else: @@ -85,7 +88,7 @@ def pickModules(): sg.FolderBrowse(font=normal_font, button_text='Browse Folder', target=(sg.ThisRow, -2), key='INPUTFOLDERBROWSE') ] ], - title='Select a file (tar/zip/gz) or directory of the target Android full file system extraction for parsing:')], + title='Select a file (tar/zip/gz/ab) or directory of the target Android full file system extraction for parsing:')], [sg.Frame(layout=[ [sg.Input(size=(112,1)), sg.FolderBrowse(font=normal_font, button_text='Browse Folder')] ], diff --git a/scripts/search_files.py b/scripts/search_files.py index 3c7ac3891..b58257cc2 100644 --- a/scripts/search_files.py +++ b/scripts/search_files.py @@ -98,3 +98,67 @@ def search(self, filepattern, return_on_first_hit=False): def cleanup(self): self.zip_file.close() + +class FileSeekerABackup(FileSeekerTar): + def __init__(self, ab_file_path, temp_folder): + logfunc("Generating tar from AB file") + logfunc(f"Tar will be generated in {temp_folder}") + tar_file_path = self.build_tar_filepath(ab_file_path, temp_folder) + try: + self.generate_tar_file(ab_file_path, tar_file_path) + except TypeError: + logfunc("File doesn't seem to be an AB backup") + raise TypeError + FileSeekerTar.__init__(self, tar_file_path, temp_folder) + + def build_tar_filepath(self, input_path, output_dir): + input_filename = os.path.splitext(os.path.basename(input_path))[0] + output_filename = f"{input_filename}.tar.gz" + logfunc(f"Output filename: {output_filename}") + output_filepath = os.path.join(output_dir, output_filename) + return output_filepath + + def generate_tar_file(self, ab_file_path, tar_file_path): + ab_header = b"ANDROID BACKUP" + tar_header = b"\x1f\x8b\x08\x00\x00\x00\x00\x00" + ignore_offset = 24 + + ab_data = open(ab_file_path, 'rb') + + ab_bytes_to_remove = ab_data.read(ignore_offset) + + if ab_bytes_to_remove[:14] == ab_header: + logfunc("AB Header checked and intact") + else: + logfunc("AB Header not found; is it definitely the right file?") + raise TypeError + + # Open the target tar file + output_path = tar_file_path + + try: + output_file = open(output_path, 'wb') + except: + logfunc("Unable to open file at {output_path}") + raise FileNotFoundError + + logfunc("Writing tar header..") + output_file.write(tar_header) + + logfunc("Writing rest of AB file..") + output_file.write(ab_data.read()) + + logfunc("..done.") + logfunc("Closing files..") + + output_file.close() + ab_data.close() + + # quick verify + try: + test_val = tarfile.is_tarfile(output_path) + logfunc("Output verified OK") + except: + logfunc("Verification failed; maybe it's encrypted?") + raise TypeError +