# # # import os import argparse import logging from subprocess import Popen, PIPE, check_call # our libs from libs import android from libs.toolbox import ToolBox from libs.fingerprint import FingerprintDB from libs.fingerprint_index import FingerprintIndex BASE_DIR = "data" FP_BASE_DIR = "fingerprints" def main(): parseArgs() # def compareFingerprint(file_in, file_json): db = FingerprintDB() db.scanDBFile(file_in) percent = db.compareDB(file_json) print "Percent match: {}".format(str(percent)) # def createFingerprint(file_in, app_name, app_ver, notes): print "Reading database file: {}".format(file_in) try: db = FingerprintDB() db.scanDBFile(file_in) if app_name: db.setAppName(app_name) if app_ver: db.setAppVer(app_ver) if notes: db.setNotes(notes) filename = db.writeFingerprint() print "Fingerprint generated: {}".format(filename) except Exception as ex: print ex # def indexFingerprints(fp_dir): db = FingerprintDB() fp = FingerprintIndex() try: fp.openIndex(fp_dir) print "WARN: index already exists. remove, then reindex" except: try: retVal = fp.createIndex(fp_dir) print "Index created:" print " {} fingerprints processed.".format(retVal[0]) print " {} fingprint processing errors.".format(retVal[1]) print " {} files skipped (N/A, not a fingerprint file)".format(retVal[2]) except Exception as ex: print ex # def compareFPIndex(fp_dir, db_in, json_in): try: db = FingerprintDB() if (db_in): db.scanDBFile(db_in) else: db.importJson(json_in) fp = FingerprintIndex() fp.openIndex(fp_dir) # search for fingerprints with exact database match logging.info("Searching for MD5 DB: {}".format(db.getMD5DB())) fp_ret = fp.findFP(db.getMD5DB()) if (fp_ret): print "Database matche(s) found" print "RESULTS:" for fp_list in fp_ret: for fp in fp_list[1].split(','): print fp # search for fingerprints with similar tables else: print "[ Table percent match: ]" logging.info("Searching for md5 tables: {}".format(db.getMD5Tables())) fp_list = fp.findFPTables(db.getMD5Tables().values()) for fp in fp_list: fq_fp = fp_dir + os.path.sep + fp[0] logging.info("Comparing fingerprint: {}".format(fq_fp)) percent = db.compareDB(fq_fp) print "{:2.2f}%: {}".format(percent, fp[0]) except Exception as ex: print "ERROR: error occured while comparing fingerprint" print ex # def androidPull(): fin_count = 0 print "Android pull started..." ap = android.AndroidAppPull() isRoot = ap.isADBRoot(); if (not isRoot): print "ERROR: adb is not running as root, exec 'adb root'" return if (not mkdir(BASE_DIR)): print "ERROR creating directory: {}".format(BASE_DIR) return if (not mkdir(FP_BASE_DIR)): print "ERROR creating directory: {}".format(FP_BASE_DIR) return dir_names = ap.getAppsDir() for dir_name in dir_names: print "Processing directory [{}]".format(dir_name) ap.pullApp(BASE_DIR, dir_name) fq_dir = BASE_DIR + os.path.sep + dir_name count = __createFingerprint(fq_dir, FP_BASE_DIR, dir_name) fin_count += count print "Fingerprints created: {}".format(str(count)) print "\nTotal Fingerprints created: {}".format(str(fin_count)) # def androidData(data_dir): dir_names = [] try: dirs = os.listdir(data_dir) print "Opening directory: [{}], [{} folders found]".format(data_dir, len(dirs)) except Exception, ex: print "ERROR opening Android Data Directory (-dd): {}\n{}".format(data_dir, ex) return out_dir = FP_BASE_DIR + "_" + ToolBox.getTimestampStr() if (not mkdir(out_dir)): print "ERROR creating directory: {}".format(FP_BASE_DIR) return fin_count = 0 for ddir in dirs: in_dir = data_dir + os.path.sep + ddir count = __createFingerprint(in_dir, out_dir, ddir) fin_count += count print "COMPLETED: created {} fingerprints".format(str(fin_count)) # def queryMD5(fp_dir, md5_db): try: fp = FingerprintIndex() fp.openIndex(fp_dir) results = fp.queryAppDetails(md5_db) for row in results: print "[{}]\nDB: {}\nFP: {}\nDate: {}\n".format(row[0], row[2], row[3], row[4]) except Exception as ex: print "ERROR: {}".format(ex) # def insertFP(db_file, fp_file, fp_idx_dir): try: dbfp = FingerprintDB() fpidx = FingerprintIndex() fpidx.openIndex(fp_idx_dir) if (db_file): dbfp.scanDBFile(db_file) # db.debugFingerprint() fpidx.insertFP(dbfp, db_file) elif (fp_file): dbfp.importJson(fp_file) fpidx.insertFP(dbfp, fp_file) print "Insert suceessful" except Exception as ex: print ex # in_dir: fully qualified directory path to find sqlite files def __createFingerprint(in_dir, out_dir, dir_name): fin_count = 0 try: db_dir = in_dir + os.path.sep + "databases" #logging.info("in_dir=={}".format(db_dir)) files = os.listdir(db_dir) except: # not finding a databases folder is normal, not all apps use sqlite return fin_count for filein in files: try: db = FingerprintDB() ddir = db_dir + os.path.sep + filein logging.info('Parsing file "{}"'.format(ddir)) db.scanDBFile(ddir) fname = dir_name + "__" + filein + "__dbfp" + ".json" fq_name = out_dir + os.path.sep + fname db.setAppName(dir_name) db.writeFingerprintFile(fq_name) fin_count += 1 except Exception as ex: # log error, but move on in hopes of writing more fingerprints logging.error(ex) return fin_count # def __getFileName(): '''standardize on a file name, use timestamp? ''' pass # def mkdir(fdir): retval = False try: check_call(["mkdir", fdir]) retval = True except: print 'ERROR: problem creating directory "{}"'.format(fdir) return retval # def parseArgs(): print '***** ***** ***** *****' print ' DB Fingerprint' print '***** ***** ***** *****\n' parser = argparse.ArgumentParser(description="Fingerprint a sqlite database based on its schema") parser.add_argument('-db', '--database', required=False, help="path to file to be fingerprinted") parser.add_argument('-fd', '--fpdir', required=False, help="path to directory of fingerprint files, compare each file") parser.add_argument('-fp', '--fingerprint', required=False, help="fingerprint file to use in comparison") parser.add_argument('-ad', '--android_dir', required=False, help="path to a directory with android folder structure sqlite files") parser.add_argument('-dd', '--data_dir', required=False, help="path to a directory to search for sqlite files") # parser.add_argument('-idx', '--index_fingerprints', required=False, help="path to a directory with sqlite files, index fingerprints if no other args given") parser.add_argument('-an', '--app_name', required=False) parser.add_argument('-av', '--app_version', required=False) parser.add_argument('-n', '--notes', required=False) parser.add_argument('-idx', action='store_true', help="add a fingerprint to the index") parser.add_argument('-md5', required=False, help="md5 hash to query the index`") parser.add_argument('-android_pull', action='store_true', help="automated pull of applications from a physical android phone") parser.add_argument('-v', '--verbose', action='store_true', help="will set logging level to INFO") parser.add_argument('-vv', '--vverbose', action='store_true', help="will set logging level to DEBUG") parser.add_argument('-l', '--logging', action='store_true', help="will supercede the -v option and send all logging to a file, logging.DEBUG") # parser.add_argument('-t', '--title', required=False) args = parser.parse_args() if (args.logging): logging.basicConfig(filename='dbfp.log', level=logging.DEBUG) if (args.verbose): logging.basicConfig(level=logging.INFO) elif (args.vverbose): logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig(level=logging.CRITICAL) if args.fpdir and args.idx and (args.database or args.fingerprint): insertFP(args.database, args.fingerprint, args.fpdir) elif args.fpdir and args.idx: indexFingerprints(args.fpdir) elif (args.database and args.fingerprint): compareFingerprint(args.database, args.fingerprint) elif (args.fpdir and (args.database or args.fingerprint)): compareFPIndex(args.fpdir, args.database, args.fingerprint) elif (args.fpdir and args.md5): queryMD5(args.fpdir, args.md5) elif (args.android_dir): androidData(args.android_dir) elif (args.android_pull): androidPull() elif (args.database): createFingerprint(args.database, args.app_name, args.app_version, args.notes) else: print 'Create fingerprint:' print ' dbfp.py -db \n' print 'Create fingerprint index:' print ' dbfp.py -fd -idx\n' print 'Add fingerprint to index:' print ' dbfp.py -fp -idx (-db | -fp )\n' print 'Compare fingerprint to a database file:' print ' dbfp.py -fp -db \n' print 'Lookup fingerprint from index:' print ' dbfp.py -fd -fp )\n' print 'Lookup database from index:' print ' dbfp.py -fd -db \n' print 'Lookup MD5 hash from index:' print ' dbfp.py -fd -md5 \n' print 'Android App pull and fingerprint:' print ' dbfp.py -android_pull' print '\n***** ***** ***** *****\n' parser.print_help() if __name__ == "__main__": main() print