diff --git a/README.md b/README.md index 43463ec..709da78 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ -![[Version 0.6~beta](https://github.com/R3nt0n)](http://img.shields.io/badge/version-v0.6~beta-orange.svg) +![[Version 1.0](https://github.com/R3nt0n)](http://img.shields.io/badge/version-v1.0-orange.svg) ![[Python 3.2+](https://github.com/R3nt0n)](http://img.shields.io/badge/python-3.2+-blue.svg) ![[GPL-3.0 License](https://github.com/R3nt0n)](https://img.shields.io/badge/license-GPL%203.0-brightgreen.svg) -
- # wiper -Toolkit to perform secure destruction of sensitive virtual data, temporary files and swap memories. +Toolkit to perform **secure destruction of sensitive virtual data, temporary files and swap memories**. + +It has been **designed to make tasks about personal data destruction easier**, for example those which remain on work computers **when employees leave the company**. + +You can configure your own overwrite method, choosing and combining between **ones, zeros and/or random data methods** as many times as you want (e.g.: or, rzzrozr, roozr...). By default, it performs a single one-pass with random data. -It has been **designed to make tasks about personal data destruction easier**, those which remain on work computers **when employees leave the company**. ## Usage ``` @@ -18,18 +19,35 @@ optional arguments: -i, --interactive interactive mode, the script will guide you -f path, --free path wipe all free space on given path -p path, --path path path to dir/file you want to wipe + -m ozr, --method ozr overwrite methods to apply (o: ones, z: zeros, r: random), + you can combine it and choose the order -r path, --root path set a custom root path if you want to wipe with auto-search modes an unbooted system (e.g. /media/drive) - -t, --temp auto-search mode: locate actual user temp directory and wipes it -u, --home auto-search mode: locate actual user home directory and wipes it - -T, --temp-all auto-search mode: locate all users temp directory and wipes it -U, --home-all auto-search mode: locate all users home directory and wipes it -s, --swaps auto-search mode: locate swap partitions/pagefiles and wipes it (be careful: UUID swap partitions also will be wiped) ``` +### Interactive example + + + +### CLI example + + + +### Advanced examples ++ Wipes all free space in a given partition: +`wiper.py -f /home` ++ Wipes all files inside a given path using zeros, ones, zeros, zeros and random methods: +`wiper.py -p /home/user/Documents -m zozzr` ++ Locate and wipe pagination files inside an unbooted OS (mounted on `/home/media/`) using only zeros method: +`wiper.py -r /home/media -s -m z` + ## Features ++ You can **configure your own overwrite method**, choosing and combining between **ones, zeros and/or random data methods** as many times as you want (e.g.: or, rzzrozr, roozr...). By default, it performs a single one-pass with random data. + **MANUAL wipe selection**. You can wipe single files, whole directories and free space in partitions. + **AUTO wipe selection**. Suggests an automatic selection of paths to wipe, relatives to personal/temporary data and swap memories. **If you run it with the OS target unmounted**, e.g. from a live OS trying to wipe an @@ -48,6 +66,9 @@ unmounted hard drive containing another OS, you should provide the root path/mou + wmi (if you want to use auto-search mode booted on a Windows OS) ## Changelist +##### 1.0 version notes (05/07/2020) ++ Logic to choose, combine and apply diferent methods implemented. ++ Temp and temp-all features removed because it doesn't seem useful. ##### 0.9~beta version notes (29/06/2020) + One-pass overwrite methods already implemented: random, ones and zeros. + Fixing menu bug and updating usage. @@ -61,8 +82,7 @@ unmounted hard drive containing another OS, you should provide the root path/mou ## TODO list -+ Implement temp, home_all and temp_all features. -+ Write the logic to switch between different overwrite methods already implemented (ones, zeros and random) and allow user to combines it. ++ Implement home-all feature. ## Legal disclaimer diff --git a/img/wiper-0.9.gif b/img/wiper-0.9.gif deleted file mode 100644 index 411900e..0000000 Binary files a/img/wiper-0.9.gif and /dev/null differ diff --git a/img/wiper-1.0-cli.gif b/img/wiper-1.0-cli.gif new file mode 100644 index 0000000..29eca69 Binary files /dev/null and b/img/wiper-1.0-cli.gif differ diff --git a/img/wiper-1.0-interactive.gif b/img/wiper-1.0-interactive.gif new file mode 100644 index 0000000..6cb9355 Binary files /dev/null and b/img/wiper-1.0-interactive.gif differ diff --git a/r3ntlib/wiper_ops.py b/r3ntlib/wiper_ops.py index faaf081..2d28920 100644 --- a/r3ntlib/wiper_ops.py +++ b/r3ntlib/wiper_ops.py @@ -20,6 +20,37 @@ from r3ntlib.os_ops import run_command from r3ntlib.color import color + +def progressBar(iterable, prefix = '', suffix = '', decimals = 1, length = 100, fill = '█', printEnd = "\r"): + """ + Found in: https://stackoverflow.com/questions/3173320/text-progress-bar-in-the-console + Call in a loop to create terminal progress bar + @params: + iteration - Required : current iteration (Int) + total - Required : total iterations (Int) + prefix - Optional : prefix string (Str) + suffix - Optional : suffix string (Str) + decimals - Optional : positive number of decimals in percent complete (Int) + length - Optional : character length of bar (Int) + fill - Optional : bar fill character (Str) + printEnd - Optional : end character (e.g. "\r", "\r\n") (Str) + """ + total = len(iterable) + # Progress Bar Printing Function + def printProgressBar (iteration): + percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total))) + filledLength = int(length * iteration // total) + bar = fill * filledLength + '-' * (length - filledLength) + print(f'\r{prefix} |{bar}| {percent}% {suffix}', end=printEnd) + # Initial Call + printProgressBar(0) + # Update Progress Bar + for i, item in enumerate(iterable): + yield item + printProgressBar(i + 1) + # Print New Line on Complete + print() + ################################################################################ def wipe_bytes(path, mode, size_to_write, method='r'): @@ -33,40 +64,41 @@ def wipe_bytes(path, mode, size_to_write, method='r'): size-to-write -- size in bytes to overwrite methods -- if tuple contains: r -> random, z -> zeros, o -> ones """ - status = 0 + status = 1 try: - print(u' {}[+]{} Overwriting {}{}{} ({}{}{} bytes)'.format(color.ORANGE,color.END, - color.ORANGE, path, color.END, - color.PURPLE, size_to_write, color.END)) - print(u' {}[+]{} Starting one-pass random wipe...'.format(color.ORANGE, color.END)) - pointer = 0 - if method == 'z': - overwritebyte = b'\x00' - elif method == 'o': - overwritebyte = b'\xff' - with open(path, mode) as dummy_file: - while size_to_write > 0: - if method == 'r': - overwritebyte = bytearray(getrandbits(8) for _ in range(1)) - dummy_file.write(overwritebyte) - size_to_write -= 1 - if mode == 'wb': - pointer += 1 - dummy_file.seek(pointer) - print('{} [+]{} {}{}{} was randomly overwritten'.format(color.PURPLE, color.END, - color.ORANGE, path, color.END)) - try: - if os.path.isfile(path): - os.remove(path) - print(' {}[-]{} {}{}{} was {}succesfully{} wiped'.format(color.GREEN, color.END, + for m in method: + if m == 'z': + print(u' {}[-]{} Starting one-pass zeros wipe...'.format(color.ORANGE, color.END)) + overwritebyte = b'\x00' + elif m == 'o': + print(u' {}[-]{} Starting one-pass ones wipe...'.format(color.ORANGE, color.END)) + overwritebyte = b'\xff' + else: + print(u' {}[-]{} Starting one-pass random wipe...'.format(color.ORANGE, color.END)) + + pointer = 0 + with open(path, mode) as dummy_file: + #while size_to_write > 0: + for j in progressBar(range(1,size_to_write), prefix='Progress:', suffix='Complete', length=50): + if m == 'r': + overwritebyte = bytearray(getrandbits(8) for _ in range(1)) + dummy_file.write(overwritebyte) + #size_to_write -= 1 + if mode == 'wb': + pointer += 1 + dummy_file.seek(pointer) + status = 0 + print('\r\n {}[-]{} {}{}{} was {}succesfully{} wiped'.format(color.GREEN, color.END, color.ORANGE, path, color.END, color.GREEN, color.END)) - except Exception as exception: - status = 2 - print('{} [!]{} ERROR: {}'.format(color.RED, color.END, exception)) except Exception as exception: status = 2 print('{} [!]{} ERROR: {}'.format(color.RED,color.END,exception)) + + finally: + if (os.path.isfile(path) and status == 0): + os.remove(path) + return status def dd_linux_wipe(linux_path, method='r'): @@ -78,20 +110,24 @@ def dd_linux_wipe(linux_path, method='r'): status = 6 if os.name == 'posix': try: - bs = '1024' - if method == 'r': - src = '/dev/zero' - elif method == 'z': - src = "<(yes $'\\ff' | tr -d \"\\n\")" - else: - src = '/dev/urandom' - bs = '4096' - bytes_to_write = disk_usage(linux_path)[2] - print(' {}[+]{} Starting to wipe {}{}{} ({}{}{} bytes) with dd tool...\r\n'.format(color.ORANGE, color.END, - color.ORANGE,linux_path,color.END, - color.PURPLE,bytes_to_write,color.END)) - command = 'dd if={} of={} bs={} status=progress'.format(src,linux_path,bs) - status = run_command(command) + for m in method: + bs = '1024' + if m == 'z': + src = '/dev/zero' + print(u' {}[-]{} Starting one-pass zeros wipe...'.format(color.ORANGE, color.END)) + elif m == 'o': + src = "<(yes $'\\ff' | tr -d \"\\n\")" + print(u' {}[-]{} Starting one-pass ones wipe...'.format(color.ORANGE, color.END)) + else: + src = '/dev/urandom' + bs = '4096' + print(u' {}[-]{} Starting one-pass random wipe...'.format(color.ORANGE, color.END)) + bytes_to_write = disk_usage(linux_path)[2] + print(' {}[+]{} Starting to wipe {}{}{} ({}{}{} bytes) with dd tool...\r\n'.format(color.ORANGE, color.END, + color.ORANGE,linux_path,color.END, + color.PURPLE,bytes_to_write,color.END)) + command = 'dd if={} of={} bs={} status=progress'.format(src,linux_path,bs) + status = run_command(command) except Exception as exception: status = 4 print('{} [!]{} ERROR: {}'.format(color.RED, color.END, exception)) @@ -101,7 +137,7 @@ def dd_linux_wipe(linux_path, method='r'): ################################################################################ -def wipe_free_space(path): +def wipe_free_space(path, method): """Fill free space disk with random bytes and secure delete the file previously created. Returns a status code: @@ -109,29 +145,9 @@ def wipe_free_space(path): Arguments: path -- root directory where it begins to search. """ - tempfile = os.path.join(path,'00000001') + tempfile = os.path.join(path,'00000000.dummyfile') try: free_space = disk_usage(path)[2] - wipe_bytes(tempfile, 'ab+', free_space) + wipe_bytes(tempfile, 'ab+', free_space, method) except Exception as exception: print('{} [!]{} ERROR: {}'.format(color.RED, color.END, exception)) - finally: - # Makes sure to clean-up tempfile - try: os.remove(tempfile) - except: pass - -def wipe_file(path): - """Wipe a single file - Returns True or False regarding if it was succesfuly deleted or not. - Arguments: - path -- path to file - """ - try: - bytesize_to_write = os.stat(path).st_size - wipe_bytes(path, 'wb', bytesize_to_write) - status = True - except Exception as exception: - print('{} [!]{} ERROR: {}'.format(color.RED, color.END, exception)) - status = False - return status - diff --git a/wiper.py b/wiper.py index e307fdc..345fe81 100644 --- a/wiper.py +++ b/wiper.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # r3nt0n # https://github.com/r3nt0n/wiper @@ -8,7 +8,7 @@ EXIT CODE 0 - OK EXIT CODE 3 - KEYBOARD INTERRUPT -EXIT CODE 4 - FEW ARGUMENTS PROVIDED +EXIT CODE 4 - FEW/INCORRECT ARGUMENTS PROVIDED EXIT CODE 5 - INCOMPATIBLE OPERATIVE SYSTEM Wiper is a set of tools to perform secure destruction of sensitive virtual data, temporary files and swap memories. @@ -41,6 +41,15 @@ ################################################################################ # ARGS DEFINITION ################################################################################ +def check_methods(method, interactive): + for char in method: + if char not in ('o', 'z', 'r'): + print(u'{} [!]{} ERROR: {} method not found'.format(color.RED, color.END, char)) + if not interactive: + sys.exit(4) + return True + + def read_args(): parser = argparse.ArgumentParser(description='set of tools to perform secure destruction of sensitive virtual data, \ temporary files and swap memories.. Absolute and relative paths are \ @@ -52,20 +61,51 @@ def read_args(): help='wipe all free space on given path') parser.add_argument('-p', '--path',action='store',dest='path',type=str, default=False,metavar='path', help='path to dir/file you want to wipe') - + parser.add_argument('-m', '--method', action='store', dest='method', type=str, default='r', metavar='ozr', + help='overwrite methods to apply (o: ones, z: zeros, r: random), you can combine it and choose the order') parser.add_argument('-r', '--root',action='store',dest='root_path',type=str,default=False, metavar='path',help='set a custom root path if you want to wipe with auto-search modes an unbooted system (e.g. /media/drive)') - parser.add_argument('-t', '--temp', action='store_true',dest='temp',default=False, - help='auto-search mode: locate actual user temp directory and wipes it') + # parser.add_argument('-t', '--temp', action='store_true',dest='temp',default=False, + # help='auto-search mode: locate actual user temp directory and wipes it') parser.add_argument('-u', '--home', action='store_true',dest='home',default=False, help='auto-search mode: locate actual user home directory and wipes it') - parser.add_argument('-T', '--temp-all', action='store_true',dest='temp_all',default=False, - help='auto-search mode: locate all users temp directory and wipes it') + # parser.add_argument('-T', '--temp-all', action='store_true',dest='temp_all',default=False, + # help='auto-search mode: locate all users temp directory and wipes it') parser.add_argument('-U', '--home-all', action='store_true',dest='home_all',default=False, help='auto-search mode: locate all users home directory and wipes it') parser.add_argument('-s', '--swaps', action='store_true',dest='swaps',default=False, help='auto-search mode: locate swap partitions/pagefiles and wipes it (be careful: UUID swap partitions also will be wiped)') - return parser + + args = parser.parse_args() + interactive = args.interactive + wipe_free_arg = args.free + path = args.path + method = args.method + custom_root_path = args.root_path + #wipe_temp_arg = args.temp + wipe_home_arg = args.home + #wipe_temp_all_arg = args.temp_all + wipe_home_all_arg = args.home_all + wipe_swaps_arg = args.swaps + # Print help and exit when runs without args + if len(sys.argv) == 1: parser.print_help(sys.stdout); sys.exit(4) + # Print help and exit when runs non-interactive without path + if (not interactive and not wipe_free_arg and not path and not wipe_home_arg and not wipe_home_all_arg and not wipe_swaps_arg): + parser.print_help(sys.stdout) + sys.exit(4) + # Check if methods introduced are right + check_methods(method, interactive) + return interactive, wipe_free_arg, path, method, custom_root_path, wipe_home_arg, wipe_home_all_arg, wipe_swaps_arg + + +def print_methods(method): + print(u'\r\n {}[+]{} Current configuration:'.format(color.ORANGE, color.END)) + for char in method: + if char == 'o': method_name = 'One-pass with ones' + if char == 'z': method_name = 'One-pass with zeros' + if char == 'r': method_name = 'One-pass with random data' + print(u' {}[-]{} {}'.format(color.PURPLE, color.END, method_name)) + def banner(): headers_color = color.PURPLE @@ -90,12 +130,12 @@ def banner(): print(u' | [{}1{}] Wipe all free space in a choosen partition |'.format(options_color,color.END)); time.sleep(delay_per_line) print(u' | [{}2{}] Wipe a single file/all files under a choosen path |'.format(options_color,color.END)); time.sleep(delay_per_line) print(u' +-- {}AUTO SEARCH MODE{} ------------------------------------+'.format(headers_color,color.END)); time.sleep(delay_per_line) - print(u' | [{}3{}] Wipe my temporal directory |'.format(options_color,color.END)); time.sleep(delay_per_line) - print(u' | [{}4{}] Wipe my personal directory (includes tempdir) |'.format(options_color,color.END)); time.sleep(delay_per_line) + print(u' | [{}3{}] Wipe my personal directory |'.format(options_color,color.END)); time.sleep(delay_per_line) print(u' +-- {}AUTO SEARCH MODE (elevated privileges){} --------------+'.format(headers_color,color.END)); time.sleep(delay_per_line) - print(u' | [{}5{}] Wipe all users temporal directories |'.format(options_color,color.END)); time.sleep(delay_per_line) - print(u' | [{}6{}] Wipe all users personal directories |'.format(options_color,color.END)); time.sleep(delay_per_line) - print(u' | [{}7{}] Wipe all swap partitions/pagination files |'.format(options_color,color.END)); time.sleep(delay_per_line) + print(u' | [{}4{}] Wipe all users personal directories |'.format(options_color,color.END)); time.sleep(delay_per_line) + print(u' | [{}5{}] Wipe all swap partitions/pagination files |'.format(options_color,color.END)); time.sleep(delay_per_line) + print(u' +-- {}OVERWRITE METHODS{} -----------------------------------+'.format(headers_color, color.END));time.sleep(delay_per_line) + print(u' | [{}6{}] Change overwrite method |'.format(options_color, color.END));time.sleep(delay_per_line) print(u' +--------------------------------------------------------+'); time.sleep(delay_per_line) print(u' | [{}0{}] Exit |'.format(options_color,color.END)); time.sleep(delay_per_line) print(u' +--------------------------------------------------------+\r\n'); time.sleep(delay_per_line) @@ -126,7 +166,7 @@ def set_root_path(): except Exception as exception: print('{} [!]{} ERROR: {}'.format(color.RED, color.END, exception)) return root_path -def wipe(path, exclude_script=True): +def wipe(path, method='r', exclude_script=True): status = 3 if not exclude_script: files_to_wipe = os_ops.find_files(path) @@ -140,19 +180,36 @@ def wipe(path, exclude_script=True): status = 2 else: print(u' {}[+]{} Files found: {}{}{}'.format(color.GREEN, color.END, color.PURPLE, len(files_to_wipe), color.END)) + + #if method == 'z': + #print(u' {}[+]{} Starting one-pass zeros wipe...'.format(color.ORANGE, color.END)) + #elif method == 'o': + #print(u' {}[+]{} Starting one-pass ones wipe...'.format(color.ORANGE, color.END)) + #else: + #print(u' {}[+]{} Starting one-pass random wipe...'.format(color.ORANGE, color.END)) + # Wipe each file counter = 0 for f in files_to_wipe: - wiped = wiper_ops.wipe_file(f) - if wiped: counter += 1 + wiped_status = False + try: + bytesize_to_write = os.stat(f).st_size + print(u'\r\n {}[+]{} Overwriting {}{}{} ({}{}{} bytes)'.format(color.ORANGE, color.END, + color.ORANGE, f, color.END, + color.PURPLE, bytesize_to_write, color.END)) + wiped_status = wiper_ops.wipe_bytes(f, 'wb', bytesize_to_write, method) + except Exception as exception: + print(u'{} [!]{} ERROR: {}'.format(color.RED, color.END, exception)) + status = 1 + if wiped_status == 0: counter += 1 print(u' {}[+]{} Files wiped: {}{}{}'.format(color.GREEN, color.END,color.PURPLE,counter,color.END)) # Remove the empty tree if path given was a dir AFTER ALL OVERWRITES - if os.path.isdir(path): + if (os.path.isdir(path) and status == 3): try: - print(u' {}[+]{} Removed empty tree (path given was a dir)'.format(color.GREEN, color.END)) + print(u' {}[+]{} Empty tree removed (path given was a dir)'.format(color.GREEN, color.END)) rmtree(path) status = 0 - except Exception as exception: print('{} [!]{} ERROR: {}'.format(color.RED,color.END,exception)); status=1 + except Exception as exception: print('{} [!]{} ERROR: {}'.format(color.RED,color.END,exception)); status=5 return status @@ -161,23 +218,9 @@ def main(): print(u'{}[!]{} Incompatible Operative System detected. Exiting...'.format(color.RED, color.END)) sys.exit(5) else: - # Load arguments - parser = read_args() - args = parser.parse_args() - interactive = args.interactive - wipe_free_arg = args.free - path = args.path - custom_root_path = args.root_path - wipe_temp_arg = args.temp - wipe_home_arg = args.home - wipe_temp_all_arg = args.temp_all - wipe_home_all_arg = args.home_all - wipe_swaps_arg = args.swaps - # Print help and exit when runs without args - if len(sys.argv) == 1: parser.print_help(sys.stdout); sys.exit(4) - # Print help and exit when runs non-interactive without path - if (not interactive and not wipe_free_arg and not path and not wipe_temp_arg and not wipe_home_arg and not wipe_temp_arg and not wipe_temp_all_arg and not wipe_swaps_arg): - parser.print_help(sys.stdout); sys.exit(4) + # Read CLI arguments + interactive, wipe_free_arg, path, method, custom_root_path, wipe_home_arg, wipe_home_all_arg, wipe_swaps_arg = read_args() + if interactive: # Clear screen and print banner with options in interactive mode os_ops.clear() @@ -190,7 +233,7 @@ def main(): if interactive: # Get opt and check it path = '' - opt = input(u'\r\n {}[?]{} Choose an option [{}0-9{}]: '.format(color.PURPLE,color.END,color.ORANGE,color.END)) + opt = input(u'\r\n {}[?]{} Choose an option (help to show again) [{}0-9{}]: '.format(color.PURPLE,color.END,color.ORANGE,color.END)) # 0. Wipes all free space in the given paths if (opt == '1' or wipe_free_arg): # Get the path from user input @@ -207,13 +250,13 @@ def main(): elif (opt == '2' or path): # Get the path from user input if not path: path = input(u' {}[?]{} Enter the path you want to wipe: '.format(color.PURPLE, color.END)) - wipe(path) + wipe(path, method) - elif (opt == '3' or wipe_temp_arg): - print(u' {}[x]{} This feature is not still implemented.'.format(color.ORANGE, color.END)) - continue + # elif (opt == '3' or wipe_temp_arg): + # print(u' {}[x]{} This feature is not still implemented.'.format(color.ORANGE, color.END)) + # continue # Wipes user personal dir - elif (opt == '4' or wipe_home_arg): + elif (opt == '3' or wipe_home_arg): personal_dirs = os_ops.get_personal_dirs() if not personal_dirs: print(u' {}[!]{} ERROR: Any home directory found'.format(color.RED, color.END)) @@ -234,15 +277,15 @@ def main(): if (type(pdir) is not int or int(pdir) >= len(personal_dirs)): print(u' {}[!]{} ERROR: Bad option choosen'.format(color.RED, color.END)) continue # Back to menu - wipe(pdir) - elif (opt == '5' or wipe_temp_all_arg): - print(u' {}[x]{} This feature is not still implemented.'.format(color.ORANGE, color.END)) - continue - elif (opt == '6' or wipe_home_all_arg): + wipe(pdir, method) + # elif (opt == '5' or wipe_temp_all_arg): + # print(u' {}[x]{} This feature is not still implemented.'.format(color.ORANGE, color.END)) + # continue + elif (opt == '4' or wipe_home_all_arg): print(u' {}[x]{} This feature is not still implemented.'.format(color.ORANGE, color.END)) continue # Wipes swaps/pagefiles - elif (opt == '7' or wipe_swaps_arg): + elif (opt == '5' or wipe_swaps_arg): print(u' {}[-]{} Searching swap/pagefiles...'.format(color.ORANGE, color.END)) swaplist = os_ops.get_swaps(custom_root_path) if not swaplist: @@ -259,16 +302,23 @@ def main(): confirm = input(u' {}[?]{} Do you want to confirm? (y/n) > '.format(color.PURPLE,color.END)) if (confirm.lower().startswith('y')): if (os.name == 'nt' or custom_root_path): - wipe(swaplist) + wipe(swaplist, method) elif (os.name == 'posix'): for swap in swaplist: - status = wiper_ops.dd_linux_wipe(swap) + status = wiper_ops.dd_linux_wipe(swap, method) if str(status) == '0': print(u' {}[+]{} {}{}{} was succesfully wiped.'.format(color.GREEN,color.END, color.PURPLE,swap,color.END)) - + elif (opt == '6'): + print_methods(method) + m = input(u'\r\n {}[?]{} Set the new configuration (examples: o, zr, ozozr): '.format(color.PURPLE,color.END)) + if check_methods(m, interactive): + method = m + print_methods(method) elif (opt == '0' or opt == 'quit' or opt == 'exit'): interactive = False + elif (opt.lower() == 'help'): + banner() else: print(u' {}[!]{} Incorrect option.'.format(color.RED, color.END)) continue