├── README.md ├── abo.py └── requirements.txt /README.md: -------------------------------------------------------------------------------- 1 | # AndroidBlastOff 2 | A tool to speed up Android pentesting by automating the APK acquisition and initial information gathering. 3 | 4 | Moved to only Python 3 5 | 6 | Features: 7 | - Extract from an adb connected device (listing all available packages) 8 | - Work with a local copy of an APK 9 | - Extract initial information (Timestamp, MD5, SHA1, SHA256, SHA512, internal version, public version, and min and target SDK) 10 | - Build apk from decompiled 11 | - Sign apk 12 | - Install apk (through adb) 13 | - Or build, sign and install automatically 14 | 15 | Parameters: 16 | 17 | -h, --help show this help message and exit 18 | --apk APK Path of the apk (default: current path) 19 | --adb Select the apk using adb 20 | --device DEVICE Device to connect to using adb 21 | --skipinfo Skip info tasks (hash, version, etc) 22 | --build BUILD Folder of the unpacked APK to build (won't decompile or unpack) (can be set to "same" if --sign or --install are set) 23 | --sign SIGN Folder of the unpacked APK to sign (won't decompile or unpack) (can be set to "same" if --build or --install are set) 24 | --install INSTALL Folder of the unpacked APK to install (WILL UNINSTALL APK FROM DEVICE and won't decompile or unpack) (can be set to "same" if --sign or --build are set) 25 | --bsi BSI Build, sign and install the apk (WILL UNINSTALL APK FROM DEVICE requires --apk, --playstore or --adb and a device connected through adb) 26 | --keystore KEYSTORE Keystore to use for signing 27 | --keystorepassword KEYSTOREPASSWORD 28 | Password for the keystore to use for signing 29 | --keystorealias KEYSTOREALIAS 30 | Alias for the new keystore to use for signing 31 | --keystorealg KEYSTOREALG 32 | Algorithm for the new keystore to use for signing 33 | --keystorename KEYSTORENAME 34 | Name for the new keystore to use for signing 35 | 36 | Example of use 37 | 38 | Perform operations with a local copy of APK: 39 | abo --apk {PATH_TO_LOCAL_APK} 40 | Get APK using ADB (requires a connection via ADB): 41 | abo --adb 42 | Get APK using ADB using specific device (requires a connection via ADB): 43 | abo --adb --device {IP_ADDRESS_OF_ADB_CONNECTED_DEVICE} 44 | Skip info tasks: 45 | abo --skipinfo 46 | Build an unpacked app: 47 | abo --build {FOLDER_OF_UNPACKED_APK} 48 | Sign an unpacked app: 49 | abo --sign {FOLDER_OF_UNPACKED_APK} 50 | Install an unpacked app (requires a connection via ADB): 51 | abo --install {FOLDER_OF_UNPACKED_APK} 52 | Install an unpacked app using specific device(requires a connection via ADB): 53 | abo --install {FOLDER_OF_UNPACKED_APK} --device {IP_ADDRESS_OF_ADB_CONNECTED_DEVICE} 54 | Build, sign and install an unpacked app (requires a connection via ADB): 55 | abo --bsi {FOLDER_OF_UNPACKED_APK} 56 | Build, sign and install an unpacked app (requires a connection via ADB): 57 | abo --bsi {FOLDER_OF_UNPACKED_APK} --device {IP_ADDRESS_OF_ADB_CONNECTED_DEVICE} 58 | 59 | ADITIONAL 60 | Use specific keystore for signing: 61 | --keystore {PATH_TO_KEYSTORE} 62 | Use specific keystore for signing specifying its password and alias: 63 | --keystore {PATH_TO_KEYSTORE} --keystorepassword {PASSWORD} --keystorealias {ALIAS} 64 | Use specific algorithm for signing (only when custom keystore is not specified): 65 | --keystorealg {ALGORITHM} 66 | Name for the keystore that will be created (only when custom keystore is not specified): 67 | --keystorename {NAME} 68 | -------------------------------------------------------------------------------- /abo.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 1/Change the order of the packages using adb, up to down and left to right instead of left to right and up to down 3 | 2/print(progress of tasks, where possible) 4 | 3/seems like installation has some issues when the original is already installed 5 | ''' 6 | 7 | ''' 8 | If there is a problem like: 9 | requests.exceptions.SSLError: [Errno 1] _ssl.c:510: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure 10 | 11 | Then install the following: 12 | sudo apt-get install python-dev libssl-dev libffi-dev 13 | pip install --user pyopenssl==0.13.1 pyasn1 ndg-httpsclient 14 | 15 | ''' 16 | 17 | import sys 18 | import argparse 19 | import hashlib 20 | import os 21 | import os.path 22 | import concurrent.futures 23 | import multiprocessing 24 | import requests 25 | import random 26 | import glob 27 | import time 28 | import colorama 29 | import warnings 30 | from argparse import RawTextHelpFormatter 31 | from termcolor import colored 32 | from lxml import etree 33 | from getpass import getpass 34 | from subprocess import Popen, PIPE 35 | from datetime import datetime 36 | from prettytable import PrettyTable 37 | from prettytable import PLAIN_COLUMNS 38 | 39 | args = "" 40 | user_agent_list = [ 41 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36', 42 | 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36', 43 | 'Mozilla/5.0 (Windows NT 5.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36', 44 | 'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36', 45 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36', 46 | 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36', 47 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36', 48 | 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36', 49 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36', 50 | 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36', 51 | 'Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1)', 52 | 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko', 53 | 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)', 54 | 'Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko', 55 | 'Mozilla/5.0 (Windows NT 6.2; WOW64; Trident/7.0; rv:11.0) like Gecko', 56 | 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko', 57 | 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0)', 58 | 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko', 59 | 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)', 60 | 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; Trident/7.0; rv:11.0) like Gecko', 61 | 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)', 62 | 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)', 63 | 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)' 64 | ] 65 | 66 | colorama.init() 67 | time1 = "" 68 | 69 | def parseArgs(): 70 | global args 71 | parser = argparse.ArgumentParser(description='Basic tool for automating the boring tasks of Android pentesting', formatter_class=RawTextHelpFormatter, 72 | epilog=colored("Example of use\n\n", 'yellow', attrs=['bold']) + \ 73 | colored("Perform operations with a local copy of APK:\n", 'yellow', attrs=['bold']) + \ 74 | colored("\tabo --apk {PATH_TO_LOCAL_APK}\n", 'red', attrs=['bold']) + \ 75 | colored("Get APK using ADB (requires a connection via ADB):\n", 'yellow', attrs=['bold']) + \ 76 | colored("\tabo --adb\n", 'red', attrs=['bold']) + \ 77 | colored("Get APK using ADB using specific device (requires a connection via ADB):\n", 'yellow', attrs=['bold']) + \ 78 | colored("\tabo --adb --device {IP_ADDRESS_OF_ADB_CONNECTED_DEVICE}\n", 'red', attrs=['bold']) + \ 79 | colored("Skip info tasks:\n", 'yellow', attrs=['bold']) + \ 80 | colored("\tabo --skipinfo\n", 'red', attrs=['bold']) + \ 81 | colored("Build an unpacked app:\n", 'yellow', attrs=['bold']) + \ 82 | colored("\tabo --build {FOLDER_OF_UNPACKED_APK}\n", 'red', attrs=['bold']) + \ 83 | colored("Sign an unpacked app:\n", 'yellow', attrs=['bold']) + \ 84 | colored("\tabo --sign {FOLDER_OF_UNPACKED_APK}\n", 'red', attrs=['bold']) + \ 85 | colored("Install an unpacked app (requires a connection via ADB):\n", 'yellow', attrs=['bold']) + \ 86 | colored("\tabo --install {FOLDER_OF_UNPACKED_APK}\n", 'red', attrs=['bold']) + \ 87 | colored("Install an unpacked app using specific device(requires a connection via ADB):\n", 'yellow', attrs=['bold']) + \ 88 | colored("\tabo --install {FOLDER_OF_UNPACKED_APK} --device {IP_ADDRESS_OF_ADB_CONNECTED_DEVICE}\n", 'red', attrs=['bold']) + \ 89 | colored("Build, sign and install an unpacked app (requires a connection via ADB):\n", 'yellow', attrs=['bold']) + \ 90 | colored("\tabo --bsi {FOLDER_OF_UNPACKED_APK}\n", 'red', attrs=['bold']) + \ 91 | colored("Build, sign and install an unpacked app (requires a connection via ADB):\n", 'yellow', attrs=['bold']) + \ 92 | colored("\tabo --bsi {FOLDER_OF_UNPACKED_APK} --device {IP_ADDRESS_OF_ADB_CONNECTED_DEVICE}\n", 'red', attrs=['bold']) + \ 93 | colored("\nADITIONAL\n", 'red', attrs=['bold']) + \ 94 | colored("Use specific keystore for signing:\n", 'yellow', attrs=['bold']) + \ 95 | colored("\t--keystore {PATH_TO_KEYSTORE}\n", 'red', attrs=['bold']) + \ 96 | colored("Use specific keystore for signing specifying its password and alias:\n", 'yellow', attrs=['bold']) + \ 97 | colored("\t--keystore {PATH_TO_KEYSTORE} --keystorepassword {PASSWORD} --keystorealias {ALIAS}\n", 'red', attrs=['bold']) + \ 98 | colored("Use specific algorithm for signing (only when custom keystore is not specified):\n", 'yellow', attrs=['bold']) + \ 99 | colored("\t--keystorealg {ALGORITHM}\n", 'red', attrs=['bold']) + \ 100 | colored("Name for the keystore that will be created (only when custom keystore is not specified):\n", 'yellow', attrs=['bold']) + \ 101 | colored("\t--keystorename {NAME}", 'red', attrs=['bold'])) 102 | 103 | parser.add_argument('--apk', default="", help='Path of the apk (default: current path)', required=False) 104 | parser.add_argument('--adb', default=False, help='Select the apk using adb', required=False, action='store_true') 105 | parser.add_argument('--device', default="", help='Device to connect to using adb', required=False) 106 | parser.add_argument('--skipinfo', default=False, help='Skip info tasks (hash, version, etc)', required=False, action='store_true') 107 | parser.add_argument('--build', default=None, help='Folder of the unpacked APK to build (won\'t decompile or unpack) (can be set to "same" if --sign or --install are set)', required=False) 108 | parser.add_argument('--sign', default=None, help='Folder of the unpacked APK to sign (won\'t decompile or unpack) (can be set to "same" if --build or --install are set)', required=False) 109 | parser.add_argument('--install', default=None, help='Folder of the unpacked APK to install (WILL UNINSTALL APK FROM DEVICE and won\'t decompile or unpack) (can be set to "same" if --sign or --build are set)', required=False) 110 | parser.add_argument('--bsi', default=None, help='Build, sign and install the apk (WILL UNINSTALL APK FROM DEVICE requires --apk or --adb and a device connected through adb)', required=False) 111 | parser.add_argument('--keystore', default="", help='Keystore to use for signing', required=False) 112 | parser.add_argument('--keystorepassword', default="", help='Password for the keystore to use for signing', required=False) 113 | parser.add_argument('--keystorealias', default="", help='Alias for the new keystore to use for signing', required=False) 114 | parser.add_argument('--keystorealg', default="", help='Algorithm for the new keystore to use for signing', required=False) 115 | parser.add_argument('--keystorename', default="", help='Name for the new keystore to use for signing', required=False) 116 | 117 | args = parser.parse_args() 118 | 119 | if not len(sys.argv) > 1: 120 | print(colored("[-] No option has been selected", 'red', attrs=['bold'])) 121 | parser.print_help() 122 | sys.exit() 123 | 124 | if (args.apk != "" and args.adb): 125 | print(colored("[-] --apk and --adb are mutually exclusive. Only one can be used", 'red', attrs=['bold'])) 126 | 127 | '''if args.apk == "" and not args.adb and args.sign == None and args.build == None and args.install == None and args.bsi == False: 128 | print(colored("[-] No option has been selected", 'red', attrs=['bold'])) 129 | parser.print_help() 130 | sys.exit() 131 | ''' 132 | 133 | if args.apk != "" and not os.path.exists(args.apk): 134 | print(colored("[-] File " + args.apk + " does not exist", 'red', attrs=['bold'])) 135 | sys.exit() 136 | 137 | if args.bsi and args.apk == "" and args.adb == "": 138 | print(colored("[-] Must specify --apk, --adb to use --bsi", 'red', attrs=['bold'])) 139 | sys.exit() 140 | 141 | if args.keystore != "" and args.keystorealias == "": 142 | print(colored("[-] Must specify a keystore alias if a keystore is specified (use --keystorealias)", 'red', attrs=['bold'])) 143 | sys.exit() 144 | 145 | if args.sign != None: 146 | #remove last slash 147 | if args.sign[-1:] == "/": 148 | args.sign = args.sign[:-1] 149 | elif args.sign == "same": 150 | #If one parameter (--sign, --install or --build) is set to a folder and another one is not None, set that to the same folder 151 | if args.install != None and args.install != "same": 152 | args.sign = args.install 153 | elif args.build != None and args.build != "same": 154 | args.sign = args.build 155 | 156 | if args.build != None: 157 | #remove last slash 158 | if args.build[-1:] == "/": 159 | args.build = args.build[:-1] 160 | elif args.build == "same": 161 | #If one parameter (--sign, --install or --build) is set to a folder and another one is not None, set that to the same folder 162 | if args.install != None and args.install != "same": 163 | args.build = args.install 164 | elif args.sign != None and args.sign != "same": 165 | args.build = args.sign 166 | 167 | if args.install != None: 168 | #remove last slash 169 | if args.install[-1:] == "/": 170 | args.install = args.install[:-1] 171 | elif args.install == "same": 172 | #If one parameter (--sign, --install or --build) is set to a folder and another one is not None, set that to the same folder 173 | if args.build != None and args.build != "same": 174 | args.install = args.build 175 | elif args.sign != None and args.sign != "same": 176 | args.install = args.sign 177 | 178 | def unpackAndDecompile(): 179 | global args 180 | if args.apk != "": 181 | print(colored("[+] Unpacking APK using Apktool", 'green', attrs=['bold'])) 182 | print(colored("[+] Decompiling APK using enjarify", 'green', attrs=['bold'])) 183 | with concurrent.futures.ThreadPoolExecutor(max_workers=multiprocessing.cpu_count()) as executor: 184 | executor.map(executeCommand, [["apktool", "d", args.apk, "-f"], ["enjarify", args.apk, "-f"]]) 185 | 186 | #Connect using adb, show packages and download selected apk 187 | def adb(): 188 | global args 189 | _, output = executeCommand(["adb", "devices"]) 190 | device = "" 191 | if args.device and output.find(args.device) != -1: 192 | device = args.device 193 | else: 194 | device = output.splitlines()[1].split(":")[0] 195 | if device: 196 | #list all installed packages 197 | _, output = executeCommand(["adb", "shell", "pm", "list", "packages"]) 198 | packages = output.splitlines() 199 | 200 | packages = [package.split(':')[1].splitlines()[0] for package in packages] 201 | packages.sort() 202 | print(colored("[+] Available packages:", 'green', attrs=['bold'])) 203 | prettyPrintPackages(packages) 204 | #get users choice 205 | selectedPackage = -1 206 | while True: 207 | try: 208 | print(colored("[+] Select package to download APK file (0 - " + str(len(packages) - 1) + "):", 'green', attrs=['bold'])) 209 | selectedPackage = int(input()) 210 | break 211 | except KeyboardInterrupt as e: 212 | sys.exit() 213 | except: 214 | pass 215 | 216 | #Get full path for the APK file 217 | print(colored("[+] Obtaining full path of selected APK", 'green', attrs=['bold'])) 218 | _, output = executeCommand(["adb", "shell", "pm", "path", packages[selectedPackage]]) 219 | 220 | #Extract apk into destination 221 | print(colored("[+] Extracting APK", 'green', attrs=['bold'])) 222 | args.apk = packages[selectedPackage] + ".apk" 223 | _, output = executeCommand(["adb", "pull", output.split(":")[1].splitlines()[0], args.apk]) 224 | print(colored("[+] APK extracted to " + args.apk, 'green', attrs=['bold'])) 225 | else: 226 | print(colored("[-] ADB: Specified device is not found or there is no device.", 'red', attrs=['bold'])) 227 | sys.exit() 228 | 229 | def prettyPrintPackages(packages): 230 | pt = PrettyTable() 231 | pt.set_style(PLAIN_COLUMNS) 232 | pt.header = False 233 | terminalWidth, _ = getTerminalSize() 234 | numberOfColumns = terminalWidth // (len(max(packages, key=len)) + 3) 235 | rest = terminalWidth % (len(max(packages, key=len)) + 3) 236 | headers = [] 237 | for i in range(numberOfColumns): 238 | headers.append(str(i)) 239 | pt.field_names = headers 240 | for i in range(numberOfColumns): 241 | pt.align[str(i)] = "l" 242 | 243 | rows = len(packages) // numberOfColumns 244 | if (len(packages) % numberOfColumns) != 0: 245 | rows = rows + 1 246 | 247 | i = 0 248 | while i < rows: 249 | row = [] 250 | for j in range(0, numberOfColumns): 251 | try: 252 | row.append(" [" + str((rows * j) + i) +"]" + packages[(rows * j) + i]) 253 | except: 254 | row.append("") 255 | i += 1 256 | pt.add_row(row) 257 | print(colored(pt, 'green', attrs=['bold'])) 258 | 259 | def getAPKName(): 260 | global args 261 | if args.apk: 262 | return args.apk.replace("\\", "/").split("/")[-1][0:-4] 263 | else: 264 | return "" 265 | 266 | def executeCommand(command): 267 | global args 268 | try: 269 | cmd = "" 270 | if type(command) is str: 271 | cmd = command 272 | else: 273 | cmd = command[0] 274 | try: 275 | #Check if tool exists 276 | p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 277 | output, err = p.communicate() 278 | except OSError as e: 279 | print(colored("[-] Tool " + cmd + " is not found in the system.\n", 'red', attrs=['bold'])) 280 | os._exit(1) 281 | p = Popen(command, stdin=PIPE, stdout=PIPE, stderr=PIPE) 282 | output, err = p.communicate() 283 | return True, output.decode('utf-8') 284 | except OSError as e: 285 | return False, None 286 | except Exception as e: 287 | print(colored("[-] " + command[0], 'red', attrs=['bold'])) 288 | print(colored(e, 'red', attrs=['bold'])) 289 | return False, None 290 | 291 | def processAPK(): 292 | global args 293 | if not args.sign and not args.build and not args.install and not args.bsi: 294 | if args.adb: 295 | adb() 296 | unpackAndDecompile() 297 | 298 | def info(): 299 | global args 300 | if not args.skipinfo and not args.sign and not args.build and not args.install and not args.bsi: 301 | #Clear contents of info file 302 | f = open(getAPKName() + "-info.txt", "w") 303 | f.write("APK name: " + args.apk.split("/")[-1] + "\n") 304 | f.write("Date: " + str(datetime.now()) + "\n") 305 | f.close() 306 | print(colored("[+] Calculating hashes", 'green', attrs=['bold'])) 307 | calculateHash() 308 | print(colored("[+] Retrieving version", 'green', attrs=['bold'])) 309 | version() 310 | 311 | def calculateHash(): 312 | global args 313 | md5 = hashlib.md5() 314 | sha1 = hashlib.sha1() 315 | sha256 = hashlib.sha256() 316 | sha512 = hashlib.sha512() 317 | with open(args.apk, "rb") as f: 318 | # Read and update hash string value in blocks of 4K 319 | for byte_block in iter(lambda: f.read(4096),b""): 320 | md5.update(byte_block) 321 | sha1.update(byte_block) 322 | sha256.update(byte_block) 323 | sha512.update(byte_block) 324 | 325 | f = open(getAPKName() + "-info.txt", "a") 326 | f.write("MD5: " + md5.hexdigest() + "\n") 327 | f.write("SHA1: " + sha1.hexdigest() + "\n") 328 | f.write("SHA256: " + sha256.hexdigest() + "\n") 329 | f.write("SHA512: " + sha512.hexdigest() + "\n") 330 | f.close() 331 | 332 | def version(): 333 | f = open(getAPKName() + "-info.txt", "a") 334 | for line in open(getAPKName() + "/apktool.yml").readlines(): 335 | if line.find("versionCode") != -1: 336 | f.write("Internal version: " + line.replace("'", "").split(" ")[3]) 337 | if line.find("versionName") != -1: 338 | f.write("Public version: " + line.replace("'", "").split(" ")[3]) 339 | if line.find("minSdkVersion") != -1: 340 | f.write("Min SDK: " + line.replace("'", "").split(" ")[3]) 341 | if line.find("targetSdkVersion") != -1: 342 | f.write("Target SDK: " + line.replace("'", "").split(" ")[3]) 343 | f.close() 344 | 345 | def getTerminalSize(): 346 | import fcntl, termios, struct 347 | th, tw, hp, wp = struct.unpack('HHHH', 348 | fcntl.ioctl(0, termios.TIOCGWINSZ, 349 | struct.pack('HHHH', 0, 0, 0, 0))) 350 | return tw, th 351 | 352 | def signAPK(): 353 | global args 354 | print(colored("[+] Signing APK", 'green', attrs=['bold'])) 355 | if args.keystore != "" and len(glob.glob(args.sign + "/dist/*.apk")) == 1: 356 | if os.path.exists(args.keystore): 357 | if args.keystorepassword == "": 358 | while True: 359 | try: 360 | args.keystorepassword = getpass("[+] Input keystore password:") 361 | break 362 | except KeyboardInterrupt as e: 363 | sys.exit() 364 | except: 365 | pass 366 | else: 367 | print(colored("[-] Keystore file not found (" + args.keystore + ")", 'red', attrs=['bold'])) 368 | else: 369 | createKeystore() 370 | 371 | _, output = executeCommand(["jarsigner", "-sigalg", "MD5withRSA", "-digestalg", "SHA1", "-storepass", args.keystorepassword, "-keystore", args.keystore, glob.glob(args.sign + "/dist/*.apk")[0], (args.keystorealias if args.keystorealias else "abo")]) 372 | 373 | if "jarsigner error" in output: 374 | print(colored("[-] APK not signed - Incorrect password", 'red', attrs=['bold'])) 375 | elif "jar signed" in output: 376 | print(colored("[+] APK signed", 'green', attrs=['bold'])) 377 | 378 | def createKeystore(): 379 | global args 380 | 381 | if args.keystorename == "": 382 | args.keystorename = "abo.keystore" 383 | elif args.keystorename and not (".keystore" in args.keystorename): 384 | args.keystorename = args.keystorename + ".keystore" 385 | 386 | if os.path.exists(args.keystorename): 387 | os.remove(args.keystorename) 388 | 389 | command = ["keytool", "-genkey"] + \ 390 | ["-alias"] + ([args.keystorealias] if args.keystorealias else ["abo"]) + \ 391 | ["-keyalg"] + ([args.keystorealg] if args.keystorealg else ["RSA"]) + \ 392 | ["-keystore"] + ([args.keystorename] if args.keystorename else ["abo.keystore"]) + \ 393 | ["-storepass"] + ([args.keystorepassword] if args.keystorepassword else ["abo123abo"]) + \ 394 | ["-keysize", "2048", "-validity", "10000"] + \ 395 | ["-dname", "CN=ABO, OU=ABO, O=ABO, L=ABO, S=ABO, C=US"] 396 | 397 | print(colored("[+] Using internal keystore with:", 'green', attrs=['bold'])) 398 | print(colored("\tAlias: " + (args.keystorealias if args.keystorealias != "" else "abo"), 'green', attrs=['bold'])) 399 | print(colored("\tAlg: " + (args.keystorealg if args.keystorealg != "" else "RSA"), 'green', attrs=['bold'])) 400 | print(colored("\tName: " + (args.keystorename if args.keystorename != "" else "abo.keystore"), 'green', attrs=['bold'])) 401 | print(colored("\tPassword: " + (args.keystorepassword if args.keystorepassword != "" else "abo123abo"), 'green', attrs=['bold'])) 402 | print(colored("\tKey size: 2048", 'green', attrs=['bold'])) 403 | print(colored("\tValidity: 10000", 'green', attrs=['bold'])) 404 | print(colored("\tDistinguished name: CN=ABO, OU=ABO, O=ABO, L=ABO, S=ABO, C=US", 'green', attrs=['bold'])) 405 | 406 | _, output = executeCommand(command) 407 | 408 | args.keystore = args.keystorename 409 | args.keystorepassword = "abo123abo" 410 | 411 | def buildAPK(): 412 | global args 413 | if args.build != "" and os.path.exists(args.build): 414 | print(colored("[+] Building APK", 'green', attrs=['bold'])) 415 | executeCommand(["apktool", "b", args.build]) 416 | 417 | def installAPK(): 418 | global args 419 | 420 | _, output = executeCommand(["adb", "devices"]) 421 | device = "" 422 | if args.device and output.find(args.device) != -1: 423 | device = args.device 424 | else: 425 | device = output.splitlines()[1].split(":")[0] 426 | if device: 427 | apk = glob.glob(args.install + "/dist/" + args.install + ".apk")[0] 428 | 429 | package = etree.parse(glob.glob(args.install + "/AndroidManifest.xml")[0]).xpath("/manifest")[0].get("package") 430 | 431 | #Check if the apk is already installed 432 | _, output = executeCommand(["adb", "shell", "pm", "list", "packages"]) 433 | if package in output.splitlines(): 434 | #uninstall first the package just in case the signature does not match 435 | _, output = executeCommand(["adb", "uninstall", package]) 436 | print(colored("[+] Uninstalling original APK", 'green', attrs=['bold'])) 437 | 438 | #install the apk 439 | _, output = executeCommand(["adb", "install", apk]) 440 | 441 | if "INSTALL_PARSE_FAILED_NO_CERTIFICATES" in output: 442 | print(colored("[-] APK is not signed. Use --sign", 'red', attrs=['bold'])) 443 | sys.exit() 444 | 445 | print(colored("[+] Installing APK", 'green', attrs=['bold'])) 446 | else: 447 | print(colored("[-] ADB: Specified device is not found or there is no device.", 'red', attrs=['bold'])) 448 | sys.exit() 449 | 450 | def bsi(): 451 | global args 452 | if args.build: 453 | buildAPK() 454 | if args.sign: 455 | signAPK() 456 | if args.install: 457 | installAPK() 458 | if args.bsi: 459 | args.sign = args.build = args.install = args.bsi 460 | buildAPK() 461 | signAPK() 462 | installAPK() 463 | 464 | if __name__== "__main__": 465 | parseArgs() 466 | processAPK() 467 | info() 468 | bsi() 469 | 470 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.9.1 2 | colorama==0.4.4 3 | lxml==4.2.1 4 | prettytable==1.0.1 5 | termcolor==1.1.0 6 | --------------------------------------------------------------------------------