def clear(): """ Making space for progressbars """ with common.term.location(): logger.info('Please wait while QARK tries to decompile the code back to source using multiple decompilers. This may take a while.') print("\n"*11)
def clear(): """ Making space for progressbars """ with common.term.location(): logger.info( 'Please wait while QARK tries to decompile the code back to source using multiple decompilers. This may take a while.' ) print("\n" * 11)
def find_manifest_in_unpacked_apk(path, name): """ Finds manifest.xml from the unpacked APK """ pathFromAPK = path.rsplit(".", 1)[0] common.sourceDirectory = pathFromAPK logger.info('Finding %s in %s', name, pathFromAPK) common.logger.debug(pathFromAPK, name) for root, dirs, files in os.walk(pathFromAPK): for file in files: if name in file: logger.info('%s found', name) return os.path.join(root, name)
def find_manifest_in_unpacked_apk(path, name): """ Finds manifest.xml from the unpacked APK """ pathFromAPK = path.rsplit(".",1)[0] common.sourceDirectory=pathFromAPK logger.info('Finding %s in %s', name,pathFromAPK) common.logger.debug(pathFromAPK, name) for root, dirs, files in os.walk(pathFromAPK): for file in files: if name in file: logger.info('%s found', name) return os.path.join(root, name)
def unpack(): """ APK to DEX """ logger.info('Unpacking %s', common.apkPath) #Get the directory to unpack to try: dirname, extension = common.apkPath.rsplit(".", 1) #workaround for cases where path may include whitespace file_temp = open(common.apkPath, 'r') zf = zipfile.ZipFile(file_temp) logger.info('Zipfile: %s', zf) for filename in [zf.namelist()]: logger.info('UnpackAPK: in for loop file: %s', filename) if not os.path.exists(dirname + "/"): os.makedirs(dirname + "/") zf.extractall( dirname + "/", zf.namelist(), ) logger.info('Extracted APK to %s', dirname + '/') common.pathToDEX = dirname + "/classes.dex" common.pathToUnpackedAPK = dirname + '/' return True except Exception as e: print(e) if not common.interactive_mode: logger.error(common.args.pathtoapk + common.config.get('qarkhelper', 'NOT_A_VALID_APK')) exit() logger.error( common.config.get('qarkhelper', 'NOT_A_VALID_APK_INTERACTIVE')) raise
def unpack(): """ APK to DEX """ logger.info('Unpacking %s', common.apkPath) #Get the directory to unpack to try: dirname, extension = common.apkPath.rsplit(".",1) #workaround for cases where path may include whitespace file_temp = open(common.apkPath,'r') zf = zipfile.ZipFile(file_temp) logger.info('Zipfile: %s', zf) for filename in [ zf.namelist()]: if not os.path.exists(dirname + "/"): os.makedirs(dirname + "/") zf.extractall(dirname + "/", zf.namelist(), ) logger.info('Extracted APK to %s', dirname + '/') common.pathToDEX = dirname + "/classes.dex" common.pathToUnpackedAPK = dirname + '/' return True except Exception as e: if not common.interactive_mode: logger.error(common.args.pathtoapk + common.config.get('qarkhelper', 'NOT_A_VALID_APK')) exit() logger.error(common.config.get('qarkhelper', 'NOT_A_VALID_APK_INTERACTIVE')) raise
def unpack(): """ APK to DEX """ logger.info('Unpacking %s', common.apkPath) # Get the directory to unpack to try: dirname, extension = common.apkPath.rsplit(".", 1) # workaround for cases where path may include whitespace file_temp = open(common.apkPath, 'r') zf = zipfile.ZipFile(file_temp) logger.info('Zipfile: %s', zf) for filename in [zf.namelist()]: if not os.path.exists(dirname + "/"): os.makedirs(dirname + "/") zf.extractall( dirname + "/", zf.namelist(), ) logger.info('Extracted APK to %s', dirname + '/') common.pathToDEX = dirname + "/classes.dex" if not os.path.exists( common.pathToDEX ): # Some default/system APKs from Google don't have this file logger.error( "The classes.dex file was not found for this APK! Please select a different APK." ) raise Exception common.pathToUnpackedAPK = dirname + '/' return True except Exception as e: if not common.interactive_mode: logger.error(common.args.apkpath + common.config.get('qarkhelper', 'NOT_A_VALID_APK')) exit() logger.error( common.config.get('qarkhelper', 'NOT_A_VALID_APK_INTERACTIVE')) raise
def decompile(path): """ Converts DEX to JAR(containing class files) and then class files to near original java code using 3 different decompilers and selecting the best available decompiled code """ common.pathToDEX = path pathToDex2jar = common.rootDir + "/lib/dex2jar/dex2jar.sh" sp = subprocess.Popen([pathToDex2jar, common.pathToDEX], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output, error = sp.communicate() common.pathToJar = common.pathToDEX.rsplit(".", 1)[0] + "_dex2jar.jar" dirname, extension = common.pathToJar.rsplit(".", 1) zf = zipfile.ZipFile(common.pathToJar) # Total number of class files that need to be decompiled total_files = len(zf.namelist()) report.write("totalfiles", total_files) common.count = len( [s for s in zf.namelist() if ((".class" in s) and ("$" not in s))]) pub.subscribe(decompiler_update, 'decompile') thread0 = Process(name='clear', target=clear, args=()) thread1 = Process(name='jdcore', target=jdcore, args=(zf.filename, dirname)) thread2 = Process(name='procyon', target=cfr, args=(zf.filename, dirname)) thread3 = Process(name='cfr', target=procyon, args=(zf.filename, dirname)) thread0.start() thread0.join() progressbar1.start() progressbar2.start() progressbar3.start() thread1.start() thread2.start() thread3.start() thread1.join(0) thread2.join(0) thread3.join(0) with common.term.cbreak(): val = None while val not in (u'c', u'C'): with common.term.location(0, common.term.height - 3): print "Decompilation may hang/take too long (usually happens when the source is obfuscated)." print "At any time," + common.term.bold_underline_red_on_white( 'Press C to continue' ) + " and QARK will attempt to run SCA on whatever was decompiled." val = common.term.inkey(timeout=1) if not (thread1.is_alive() or thread2.is_alive() or thread3.is_alive()): break if thread1.is_alive(): thread1.terminate() if thread2.is_alive(): thread2.terminate() if thread3.is_alive(): thread3.terminate() # Go back to the bottom of the screen with common.term.location(0, common.term.height): print "" g1 = grep_1(dirname, "// Byte code:") g2 = grep_1(dirname + "1", "// This method has failed to decompile.") g3 = grep_1(dirname + "2", "// This method could not be decompiled.") # print list(set(g1) - set(g2)) logger.info("Trying to improve accuracy of the decompiled files") restored = 0 try: for filename in g1: relative_filename = str(filename).split(dirname)[1] if any(relative_filename in s for s in g2): if any(relative_filename in s for s in g3): logger.debug("Failed to reconstruct: " + relative_filename) else: shutil.copy(dirname + "2" + relative_filename, filename) restored = restored + 1 else: shutil.copy(dirname + "1" + relative_filename, filename) restored = restored + 1 except Exception as e: print e.message report.write( "restorestats", "Restored " + str(restored) + " file(s) out of " + str(len(g1)) + " corrupt file(s)") logger.info("Restored " + str(restored) + " file(s) out of " + str(len(g1)) + " corrupt file(s)") logger.debug("Deleting redundant decompiled files") try: shutil.rmtree(dirname + "1") logger.debug("Deleted " + dirname + "1") shutil.rmtree(dirname + "2") logger.debug("Deleted " + dirname + "2") except Exception as e: logger.debug( "Unable to delete redundant decompiled files (no impact on scan results): " + str(e))
def decompile(path): """ Converts DEX to JAR(containing class files) and then class files to near original java code using 3 different decompilers and selecting the best available decompiled code """ common.pathToDEX = path pathToDex2jar = common.rootDir + "/lib/dex2jar/dex2jar.sh" sp = subprocess.Popen([pathToDex2jar, common.pathToDEX], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output, error = sp.communicate() common.pathToJar = common.pathToDEX.rsplit(".",1)[0] + "_dex2jar.jar" dirname, extension = common.pathToJar.rsplit(".",1) zf = zipfile.ZipFile(common.pathToJar) #Total number of class files that need to be decompiled total_files = len(zf.namelist()) report.write("totalfiles", total_files) common.count = len([s for s in zf.namelist() if ((".class" in s) and ("$" not in s))]) pub.subscribe(decompiler_update, 'decompile') thread0 = Process(name='clear', target=clear, args = ()) thread1 = Process(name='jdcore', target=jdcore, args = (zf.filename,dirname)) thread2 = Process(name='procyon', target=cfr, args = (zf.filename,dirname)) thread3 = Process(name='cfr', target=procyon, args = (zf.filename,dirname)) thread0.start() thread0.join() progressbar1.start() progressbar2.start() progressbar3.start() thread1.start() thread2.start() thread3.start() thread1.join(0) thread2.join(0) thread3.join(0) with common.term.cbreak(): val = None while val not in (u'c', u'C'): with common.term.location(0,common.term.height-3): print "Decompilation may hang/take too long (usually happens when the source is obfuscated)." print "At any time," + common.term.bold_underline_red_on_white('Press C to continue') + " and QARK will attempt to run SCA on whatever was decompiled." val = common.term.inkey(timeout=1) if not (thread1.is_alive() or thread2.is_alive() or thread3.is_alive()): break if thread1.is_alive(): thread1.terminate() if thread2.is_alive(): thread2.terminate() if thread3.is_alive(): thread3.terminate() #Go back to the bottom of the screen with common.term.location(0,common.term.height): print "" g1 = grep_1(dirname, "// Byte code:") g2 = grep_1(dirname+"1", "// This method has failed to decompile.") g3 = grep_1(dirname+"2", "// This method could not be decompiled.") #print list(set(g1) - set(g2)) logger.info("Trying to improve accuracy of the decompiled files") restored = 0 try: for filename in g1: relative_filename = str(filename).split(dirname)[1] if any(relative_filename in s for s in g2): if any(relative_filename in s for s in g3): logger.debug("Failed to reconstruct: " + relative_filename) else: shutil.copy(dirname+"2"+relative_filename, filename) restored = restored +1 else: shutil.copy(dirname+"1"+relative_filename, filename) restored = restored +1 except Exception as e: print e.message report.write("restorestats","Restored " + str(restored) + " file(s) out of " + str(len(g1)) + " corrupt file(s)") logger.info("Restored " + str(restored) + " file(s) out of " + str(len(g1)) + " corrupt file(s)") logger.debug("Deleting redundant decompiled files") try: shutil.rmtree(dirname+"1") logger.debug("Deleted " + dirname+"1") shutil.rmtree(dirname+"2") logger.debug("Deleted " + dirname+"2") except Exception as e: logger.debug("Unable to delete redundant decompiled files (no impact on scan results): " + str(e))