From d9a4209bcd8c13c34cbe5f288b7a5c507875f073 Mon Sep 17 00:00:00 2001 From: dash Date: Mon, 13 May 2019 16:57:27 +0200 Subject: [PATCH] public 0.5.2 release :) --- README.md | 2 +- ReleaseNotes_0.5.2.txt | 24 + ffudger.py | 261 +++++++ lib/FUDGEanalyse.py | 596 ++++++++++++++ lib/FUDGEanalyse.pyc | Bin 0 -> 8122 bytes lib/FUDGEheader.py | 813 ++++++++++++++++++++ lib/FUDGEheader.pyc | Bin 0 -> 11662 bytes lib/__init__.py | 4 + lib/__init__.pyc | Bin 0 -> 183 bytes lib/__pycache__/FUDGEanalyse.cpython-37.pyc | Bin 0 -> 13226 bytes lib/__pycache__/FUDGEheader.cpython-37.pyc | Bin 0 -> 11208 bytes lib/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 196 bytes supply/strings.txt | 30 + 13 files changed, 1729 insertions(+), 1 deletion(-) create mode 100644 ReleaseNotes_0.5.2.txt create mode 100755 ffudger.py create mode 100755 lib/FUDGEanalyse.py create mode 100644 lib/FUDGEanalyse.pyc create mode 100755 lib/FUDGEheader.py create mode 100644 lib/FUDGEheader.pyc create mode 100755 lib/__init__.py create mode 100644 lib/__init__.pyc create mode 100644 lib/__pycache__/FUDGEanalyse.cpython-37.pyc create mode 100644 lib/__pycache__/FUDGEheader.cpython-37.pyc create mode 100644 lib/__pycache__/__init__.cpython-37.pyc create mode 100644 supply/strings.txt diff --git a/README.md b/README.md index 194e3ca..df5c021 100755 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This code is for automated analysis of files you may find around the internet. P The original version had been built in 2007. In the last weeks, i had started to fix a lot of code, add features or clean some parts up. -FirmwareFudger is far from what it's goal is, but it is already handy :) +FirmwareFudger is far from what it's in my head, but it is already handy :) ## How to use the tool: diff --git a/ReleaseNotes_0.5.2.txt b/ReleaseNotes_0.5.2.txt new file mode 100644 index 0000000..abe7be0 --- /dev/null +++ b/ReleaseNotes_0.5.2.txt @@ -0,0 +1,24 @@ +Release Notes 0.5.2 +=================== + +After quite some time, i decided to spend some dedication to this old tool i had written. There +lies over a decade between v0.3 and the current version 0.5.2. + +Improvements: + +* FF has been ported to python3 +* speed, 0.3 had a naive approach of parsing files, with a lot of extra requests, + this had been changed, the speed improovements are over 300 times, so analysis of a firmware + currently takes some seconds +* bugs been fixed, one long term bug, was the always wrong suffix of a file type, this is + normally no problem, however tools like gzip do not like it if .gz is missing, so i fixed + the issue +* code cleanups and refactory, had been done a lot, and there is still a lot to add +* using argparse over getopt, as argparse is simply the better and more supportive alternative +* there is an experimental method called strings, while the normal version is known to + everyone, this version also does an analysis to dig up some more information about the file + more code and explanations will come +* FF is now working with threads, which has some extra time improvements during analysis +* FF is now using Queues at several stages + + diff --git a/ffudger.py b/ffudger.py new file mode 100755 index 0000000..0732d4b --- /dev/null +++ b/ffudger.py @@ -0,0 +1,261 @@ +#!/usr/bin/env python3 +# +# by dash in 2019 +############################ + + +import os +import sys +import argparse +import threading + +from lib.FUDGEanalyse import * +from lib.FUDGEheader import * + +#maybe put that later somewhere else +extractit=0 +fileReport=0 + +def fudge_banner(): + ff=ANALYSE() + print ("[+] FirmareFudger %s by dash" % ff.__version__) + print ("[+] tool for firmware analyses, May 2019") + print ("[+] ") + print ("[+] contact: d4shmail@gmail.com") + print + ff=[] + +def ff_fudge_routine(ff): + ''' the classic ffudger routine + patched and fixed up a bit :) + ''' + #print the banner :D + #fudge_banner() + + + # print some basic information + ff.printargs() + + true=0 + # check if only one plugin shall be tested + if ff.lonelyplugin!=None: + lonely=ff.lonelyplugin + for category in FUDGEheader.TYPES.keys(): + for plugin in FUDGEheader.TYPES[category].keys(): + if ff.lonelyplugin == FUDGEheader.TYPES[category][plugin][FUDGEheader.NAME]: + print ('[+] Analyzing for %s' % ff.lonelyplugin) + # checkheader etc.pp. + ff.fftype=lonely + + # fill the targetqueue + ff.ff_fill_targetqueue(category,plugin) + + # run the check routine + ff.checkheader() + + # check if only a group shall be tested + elif ff.ff_cat!=None: + + if ff.ff_cat in FUDGEheader.TYPES_DEF: + #print (ff.ff_cat) + category=FUDGEheader.TYPES_DEF[ff.ff_cat] + + #only check for the asked TYPE + print ("[+] Testing only for %s plugins" % (ff.ff_cat)) + for plugin in range(len(TYPES[category])): + # fill the targetqueue + ff.ff_fill_targetqueue(category,plugin) + + # check the file + ff.checkheader() + + else: + print ("[-] Unkown plugin class %s !" % ff.plugin) + sys.exit(1) + + + # if not one_plugin is choosen and also not the ff_cat + # we do check for all FF plugins + elif ff.ff_cat == None and ff.lonelyplugin==None: + print ('[+] Checking for all FF plugins') + + #check for all TYPES + for category in range(len(TYPES)): + #print (len(TYPES)) + + #print (TYPES[category]) + for plugin in range(len(TYPES[category])): + #print (len(TYPES[testtype])) + + # set the fftype + ff.fftype=TYPES[category][plugin][3] + + # fill the targetqueue + ff.ff_fill_targetqueue(category,plugin) + + # call the check_routine + #ff_check_routine(ff) + ff.checkheader() + + # ok, something went wrong + else: + + # was not able to find the named plugin + print ('[-] Sorry, something went wrong.\n[?] Execute tool with -Fl to see a complete fudger plugin list.') + return 1; + + # If a report needs to be created, create it now + #generateFilereport(ff) + + #print (ff.result_queue.qsize()) + + # hm, should work :> + while True: + if len(ff.thread_list)>0: + # print ('[.] Waiting for threads to finish %d' % (len(ff.thread_list))) + time.sleep (0.1); + for entry in ff.thread_list: + if (entry.isAlive())==False: + entry.join() + ff.thread_list.remove(entry) + else: + break + + # If extract data has been choosen, extract it now + ff.extractdata() + + # do strings analysis + if ff.str_analysis: + ff.strings_search() + + return 0; + + +def run(args): + ''' main run function, let the fun begin''' + + # instanciate FUDGEanalysis Class + ff=ANALYSE() + + # Preparations before we can analyse a file + + # set threads if given + if args.threads: + ff.thread_cnt=args.threads + + # show FirmwareFudgers Plugin Database, and return + if args.fudgelist: + ff.showplugins() + return 0; + + # show FirmwareFudgers Plugin Database, and return + #print (args.fudgelist_cat) + if args.fudgelist_cat: + ff.showplugins(cat=args.fudgelist_cat) + return 0; + + # set the file to analyse + if args.infile: + ff.infile=args.infile + + + # set the prefix for every extracted file + # default: FF-Extract + if args.outprefix: + ff.outprefix=args.outprefix + + # set the output directory + if args.outdir: + ff.outdir=args.outdir + # check if directory exists, if not create it + # if an error comes along, we abort the complete operation + if not ff.create_dir(): + print ('[-] Abort.') + + # create a report + if args.create_report: + ff.create_report=args.create_report + + # use only one specific plugin + ff.lonelyplugin=args.lonelyplugin + + # use only specific plugin group + ff.ff_cat=args.ff_cat + + # set extract to true + if args.extract: + ff.extract=True + ff.create_dir() + + + # set verbose mode + if args.verbose: + ff.verbose=True + + # set debug mode + if args.debug: + ff.debug=True + + # show version + #print (args.version) + if args.version==True: + fudge_banner() + return 0; + + # add flag if strings analysis is wanted + if args.strings == True: + ff.str_analysis=True + ff.str_minlen=args.str_minlen + ff.str_filter=args.str_filter + + # try to open our target file + if ff.openfile()==-1: + sys.exit(1) + + # use ff method + if args.fudge: + ff_fudge_routine(ff) + + +def main(): + ''' yay, we got a main :)''' + + ff=ANALYSE() + __tool__ = ff.__tool__ + __version__ = ff.__version__ + __author__ = ff.__author__ + __date__ = ff.__date__ + + parser_desc = 'FirmwareFudger, written to do better firmware analysis' + prog_desc = __tool__ + ' v' + __version__ + ' by ' + __author__ + ' (' + __date__ + ')' + parser = argparse.ArgumentParser(prog = prog_desc, description=parser_desc) + parser.add_argument('-f','--input-file',action="store",dest='infile',required=False,help='define the file to analyze') + parser.add_argument('-o','--outdir',action="store",dest='outdir',required=False,help='define the directory to save extracted files and logs to') + parser.add_argument('-O','--output-prefix',action="store",dest='outprefix',required=False,help='define the prefix for the extracted name (default: FF-Extract)') + parser.add_argument('-x','--extract',action="store_true",dest='extract',required=False,help='flag if you want to extract found files', default=False) + parser.add_argument('-F','--fudge',action="store_true",dest='fudge',required=False,help='use fudge mode (FirmwareFudgers own database)',default=True) + parser.add_argument('-S','--strings',action="store_true",dest='strings',required=False,help='run strings on file and conduct analysis',default=False) + parser.add_argument('-Sl','--strings-len',action="store",dest='str_minlen',required=False,help='the minimum length for string check',default=4, type=int) + parser.add_argument('-Sf','--strings-filter',action="store",dest='str_filter',required=False,help='the strings check filter, use regular expressions',default="([a-zA-Z0-9 \.\-]{7,})") + parser.add_argument('-M','--magic',action="store_true",dest='magic',required=False,help='use magic mode (libmagic database)',default=True) + parser.add_argument('-Fp','--fudge-plugin',action="store",dest='lonelyplugin',required=False,help='run only one specified plugin test',default=None) + parser.add_argument('-Fc','--fudge-category',action="store",dest='ff_cat',required=False,help='run only a section of possible plugins (e.g. EXEC)',default=None) + parser.add_argument('-Fl','--fudge-list',action="store_true",dest='fudgelist',required=False,help='show plugins of FirmwareFudger',default=False) + parser.add_argument('-Flc','--fudge-list-cat',action="store",dest='fudgelist_cat',required=False,help='show plugins of defined group',default=False) + parser.add_argument('--threads',action="store",dest='threads',required=False,help='define the value of maximum threads used, default is 20',default=None,type=int) + parser.add_argument('-v','--verbose',action="store_true",dest='verbose',required=False,help='run in verbose mode',default=False) + parser.add_argument('--debug',action="store_true",dest='debug',required=False,help='run in debug mode',default=False) + parser.add_argument('-r','--report',action="store_true",dest='create_report',required=False,help='create a report for the session',default=False) + parser.add_argument('-V','--version',action="store_true",dest='version',required=False,help='show version and tool information',default=False) + + args = parser.parse_args() + + # if no argument is given help is printed + if len(sys.argv)<2: + parser.print_help() + sys.exit(1) + + run(args) + +if __name__ == "__main__": + main() diff --git a/lib/FUDGEanalyse.py b/lib/FUDGEanalyse.py new file mode 100755 index 0000000..ea0388b --- /dev/null +++ b/lib/FUDGEanalyse.py @@ -0,0 +1,596 @@ + +import re +import os +import sys +import queue +import time +import struct +import binascii +import threading + +import lib.FUDGEheader as FUDGEheader +from lib.FUDGEheader import TYPES + +nor=0x1 +ver=0x2 +dbg=0x3 + +def dbgprint(): + print ("nothing") + +class ANALYSE(object): + + def __init__(self): + + """ + infile - the file to analyse + stat - os.stat results of self.infile + fftype - the current type of pattern test + plugin - choosen pluginclass to test for + lonelyplugin- choosen lonely plugin for test + fd - the filedescriptor of open and close + search - the search string/bytes + string - for convert2hex + data - the binary data field, where the bytes are filled in + offset - the offset delivered back for writing to self.cut + extract_cnt - number of the found files in file + extract - shall we extract anything? + cut - dict for offsets for the extractfile method + outdir - output directory for putting files + outname - name of the output files part + reportfile - name of the status report + files - list with paths of extracted files + """ + + # not in use yet + # self.threads = [] # list of overall threads + # self.thread_cnt = 20 # amount of threads + + # erm, check what those are used for :> + self.string="" + self.data=[] + + # ff magic search variables + # FIXME must get cleaned up + self.target_queue=queue.Queue() # all plugins to test + self.result_queue=queue.Queue() # all results from the test + self.offset=0 # offset for self.cut, does not need to be global + self.cut={} # this will be replaced with result_queue + self.set_offset=0 # ?? + self.set_string=0 # ?? + self.length=0 # does not need to be global, can also migrate to local + + # category and plugin variables + # what a MESS, this *must* get cleaned up + # FIXME + self.fftype=None # type of the pattern test, see header files + self.ff_cat=None # category to test for + self.plugin=None # pluginclass to test for + self.lonelyplugin=None # one plugin test only, named lonely plugin + self.search=None + + # threading variables + self.thread_cnt=20 # default value of concurrent threads is 20 + self.thread_alive=0 # variable for actually running threads + self.thread_list=[] # list for all active threads, not active gets removed + + # file variables + self.fd=None # filedescriptor of target file + self.instat=None # results of os.stat against infile + self.infile=None # the file to analyze + + # output variables + self.outdir=None # output directory + self.outprefix="FF-Extract" # prefix of every file written to output directory + + # reporting variables + self.report=False # generate report if variable is True + self.reportname=None # name of the status report file + self.reportfiles=[] # list of files extracted files, for reporting + + # logging options + self.debug=False + self.verbose=False + + # extraction variables + self.extract=False # extract found files if variable is True + self.extract_cnt=0 # number of files found + + + # variables for strings search mechanism + self.str_analysis=False + self.str_minlen=4 + self.str_filter="([a-zA-Z0-9 \.\-]{"+str(self.str_minlen)+",})" + self.str_resdict={} + self.str_anadict={} + + # misc tool variables + self.__author__="dash" + self.__version__="0.5.2" + self.__tool__="FirmwareFudger" + self.__date__="May of 2019" + + + def privileges(self): + if self.stat.st_uid != os.getuid(): + print ("[!] Attention file owner is %d" % self.stat.st_uid) + return False; + else: + return True; + + def ffprint(self,data,level): + ''' printing wrapper for: + * normal + * verbose + * debug + output + ''' + + if self.verbose==True and level==ver: + print (data) + elif self.debug==True and level==dbg: + print (data) + elif level==nor: + print (data) + + return 0; + + def printargs(self): + ''' output information about the file + ''' + + size=self.instat.st_size + Kilo=1024.0 + Mega=1048576.0 + + print ("[+] Fudger Version %s - Fileinformation" % self.__version__) + print ("[+] Filename %s" % self.infile) + + if size<=Mega: + sizeK=size/Kilo + print ("[+] Size %.2fK - %dB" % (sizeK,size)) + + elif size>=Mega: + sizeM=size/Mega + sizeK=size/Kilo + print ("[+] Size %.2fM - %.2fK - %dB" % (sizeM,sizeK,size)) + else: + print ("[+] Size %d" % size) + + print ("[+] User %d" % self.instat.st_uid) + print ("[+] Group %d" % self.instat.st_gid) + #print "[+] Search for %s" % self.search + + def openfile_fd(self): + ''' simple open file operation and return fd + ''' + + try: + self.instat=os.stat(self.infile) + #print ("[+] Open %s" % (self.infile)) + fd=open(self.infile,"rb") + except PermissionError as e: + print ('[-]',e) + return -1 + except IsADirectoryError as e: + print ('[-]',e) + return -1 + except FileNotFoundError as e: + print ('[-]',e) + return -1 + + return fd; + + def openfile(self): + ''' simple open file operation + ''' + + try: + self.instat=os.stat(self.infile) + print ("[+] Open %s" % (self.infile)) + self.fd=open(self.infile,"rb") + except PermissionError as e: + print ('[-]',e) + return -1 + except IsADirectoryError as e: + print ('[-]',e) + return -1 + except FileNotFoundError as e: + print ('[-]',e) + return -1 + + def closefile(self): + ''' simple closefile operaiton''' + + print ("[+] Close %s" % self.infile) + self.fd.close() + + def create_dir(self): + ''' method for checking outdir and properties + and order creation + ''' + if self.outdir != None: + try: + result=os.stat(self.outdir) + return 0; + + except FileNotFoundError as e: + self.__create_dir() + elif self.outdir==None and self.extract==True: + # self.outdir is not specified, but it has been asked to extract data + # let us generate a directory for that usecase + dirname = self.infile.replace('/','_') + dirname = dirname.replace('..','_') + dirname = dirname.replace('.','_') + dirname = dirname.replace('!','_') + dirname = dirname.replace('-','_') + dirname = dirname.lower() + self.outdir=dirname + try: + result=os.stat(self.outdir) + return 0; + + except FileNotFoundError as e: + self.__create_dir() + return 0; + + + + + + + def __create_dir(self): + ''' this function tests if the output directory does exist, if not a new + one is created. if the name exists but it is not a directory + an error is thrown and the process is aborted. + ''' + + try: + print ("[+] Creating directory %s" % (self.outdir)) + os.mkdir(self.outdir) + return(0) + except OSError as e: + print ("[-] Error %d %s" % (e.args[0], e.args[1])) + return(1) + + def convert2array(self): + + for byte in range(len(self.string)): + print ("\'%c\'," % self.string[byte],) + + def ff_fill_targetqueue(self,category,plugin): + ''' here starts the calls for the magic behind the scenes + category and plugin type are delivered and the target queue is build up + + self.target_queue - consists of all information necessary for finding magic and + later extraction + ''' + # print (TYPES[testtype][plugin]) + header1=TYPES[category][plugin][1] + header2=TYPES[category][plugin][2] + name=TYPES[category][plugin][3] + desc=TYPES[category][plugin][4] + suffix=TYPES[category][plugin][5] + + # now fill up the target queue + self.target_queue.put({ 'category':category,\ + 'plugin':plugin,\ + 'Header1':header1,\ + 'Header2':header2,\ + 'name':name,\ + 'desc':desc,\ + 'suffix':suffix}) + + #print (self.target_queue.qsize()) + return 0; + + + def __checkheader(self,target): + ''' new version of checkheader, that time with impressive speed + as a simpler search algorithm is used and an awesome class: re + and not working :( + ''' + + fd=self.openfile_fd() + + #print ('[d] __checkheader') + + header1=target['Header1'] + category=target['category'] + plugin=target['plugin'] + suffix=target['suffix'] + name=target['name'] + hh='' + cut={} + + # due a not sooo good design of ff database this has to be done + # obviously ff database needs a redesign :) + for i in header1: + hh = hh+i + + # lets create our re pattern + hh = re.escape(hh) + hh=bytes(hh,'latin-1') + re_comp=re.compile(hh) + + #print ('[v] Checking %s' % target['name']) + #print ('[d] Header1:',header1) + #print ('[d] HH:',hh) + + for match in re.finditer(re_comp, fd.read()): + #print('match', match.span()) + #print('match.group',match.group()) + + offstart, offend = match.span() + print ("[+] FOUND %s at Offset %d to %d" % (target['name'],offstart,offend)) + #print(match.span(), match.group()) + dataend=self.instat.st_size + cut={'offstart':offstart,'offend':offend,'dataend':dataend,'category':category,'plugin':plugin,'suffix':suffix} + #print ('checkheader:',cut) + self.result_queue.put(cut) +# self.str_resdict[match.span()]=match.group() + + cut={} + self.extract_cnt+=1 + + fd.close() + + def checkheader(self): + ''' threaded checkheader wrapper + ''' + + while self.target_queue.qsize()>0: + # set current thread count + self.thread_alive=len(self.thread_list) + # check if we have crossed the limit of maximum threads + if self.thread_alive0: + + # place result in target variable + target = self.result_queue.get() + + #cut[self.extract_cnt]=(offstart,offend,dataend,category,plugin,suffix) + +# print (target) +# print (len(target)) + + offstart=target['offstart'] + suffix=target['suffix'] + if suffix==None: + suffix='' + + # go to start of file + exo_file.seek(0,0) + exo_file.seek(offstart,0) + + #print (self.cut) + #print (self.cut[part]) + + FILENAME=self.outdir+"/"+self.outprefix+"-"+str(self.extract)+"-"+str(offstart)+"." + suffix + print ("[+] FILENAME: %s" % FILENAME) + try: + exw_file=open(FILENAME,"wb") + + except PermissionError as e: + print ('[-] ',e) + return -1; + + # data to write to the extracted file + # please note that currently the end of the + # original file is the end - for reasons ;) + TOWRITE=(self.instat.st_size)-offstart + + # depending on the file size this might get problematic + buf = exo_file.read() + exw_file.write(buf) + + # close the file + exw_file.close() + + #lets add it to files if reportfile shall be written + self.reportfiles.append(FILENAME) + + def generateReport(self): + print ("[+] Found %d extracted files" % len(self.files)) + print + print ("file Report") + print ("="*11) + for extracted in self.files: + #print "[+] %s " % extracted + os.spawnl(os.P_WAIT,"/usr/bin/file","file",extracted) + + def __print_categories(self): + ''' sub-method for printing all categories + ''' + print ('[+] Categories') + for cat in FUDGEheader.TYPES_DEF: + print ('\t%s' % cat) + + return 0; + + def showplugins(self,cat=None): + """ all plugins currently supported by FF own database + + """ + i=0 + if cat!=None: + if cat in FUDGEheader.TYPES_DEF: + print ("[+] Plugins:") +# print ('[v] FOUND %s' % cat) + catid = FUDGEheader.TYPES_DEF[cat] + + print ("[+] %s:" % cat) + for plugin in range(len(FUDGEheader.TYPES[catid])): + print ("\t\t- %s - %s" % (FUDGEheader.TYPES[catid][plugin][FUDGEheader.NAME],FUDGEheader.TYPES[catid][plugin][FUDGEheader.DESC])) + i+=1 + + else: + print ('[-] Category "%s" does not exist' % cat); + self.__print_categories() + return -1; + + # show all plugins supported + else: + for fftype in range(len(FUDGEheader.TYPES)): + if fftype==0: + stringtype="FS" + elif fftype==1: + stringtype="EXEC" + elif fftype==2: + stringtype="PACKERS" + elif fftype==3: + stringtype="DOCS" + elif fftype==4: + stringtype="BOOT" + elif fftype==5: + stringtype="ASM" + elif fftype==6: + stringtype="PICTURES" + elif fftype==7: + stringtype="DEVICES" + elif fftype==8: + stringtype="CRYPTO" + # elif fftype==9: + # stringtype="CRYPTO" + print ("%s:" % stringtype) + for plugin in range(len(FUDGEheader.TYPES[fftype])): + print ("\t\t- %s - %s" % (FUDGEheader.TYPES[fftype][plugin][FUDGEheader.NAME],FUDGEheader.TYPES[fftype][plugin][FUDGEheader.DESC])) + i+=1 + + print ("\n[+] Found %d plugins." % i) + print ("[+] Done") + +##################################### +# # +# strings analysis section # +# # +##################################### + + def strings_analysis(self): + ''' method for analysing and giving hints to the analyst + + self.str_resdict, the result dictionary of self.string_search method + self.str_anadict, the reuslt dictionary for self.string_analysis method + ''' + + ana = open('supply/strings.txt','r') + # read strings supply file + for line in ana.readlines(): + # is it a comment, no? then proceed + if not line.startswith('#'): + a = line.split(';') + needle=a[0] + desc=a[1] + tools=a[2] + for k in self.str_resdict.keys(): + if self.str_resdict[k].find(needle)!=-1: + self.str_anadict[needle]=(needle,desc,tools) + print ('[+] %s - %s - %s' % (needle,desc,tools)) + + print ('[+] Found %d interesting string(s) during analysis.' % len(self.str_anadict)) + + ana.close() + return 0; + + def strings_output(self): + ''' method for writing results of strings search + self.str_resdict, the result dictionary of self.string_search method + ''' + + return 0; + + def strings_search(self): + ''' this method does the work of the unix userland tool "strings" + it searches a binary for possible strings, for later manual + analysis. in this particular case an automatic analysis is added + as well to hint the analyst on something interesting found + + self.str_minlen, the default is 4 + self.str_filter, the regular expression filter + self.str_resdict, the result dictionary with string, start/end position + ''' + # variables for strings search mechanism + + self.openfile() + # print (re.findall(re_filter, str(self.fd.read()))) + + for match in re.finditer(self.str_filter, str(self.fd.read())): + #print('match', match.span()) + #print('match.group',match.group()) +# print(match.span(), match.group()) + self.str_resdict[match.span()]=match.group() + + # place this somewhere else later + self.strings_analysis() diff --git a/lib/FUDGEanalyse.pyc b/lib/FUDGEanalyse.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a72bf4af2f66b8fe5ee1760ad07cb757568da236 GIT binary patch literal 8122 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 UqUT#iv$wgLc;<+8%XV`A1!LHO0ssI2 literal 0 HcmV?d00001 diff --git a/lib/FUDGEheader.py b/lib/FUDGEheader.py new file mode 100755 index 0000000..8ff60c3 --- /dev/null +++ b/lib/FUDGEheader.py @@ -0,0 +1,813 @@ +#complete list +TYPES = 0x00 + +#categories +FS = 0x00 +EXEC = 0x01 +PACKERS = 0x02 +DOCS = 0x03 +BOOT = 0x04 +ASM = 0x05 +PICTURES = 0x06 +DEVICES = 0x07 +#ROUTERS = 0x08 +CRYPTO = 0x08 + +#Filesystem Type Definitions +MSDOS = 0x00 +CRAMFS1 = 0x01 +CRAMFS2 = 0x02 #difference is another searchstring +ROM1FS = 0x03 +SQUASHFS1 = 0x04 #difference is another searchstring +SQUASHFS2 = 0x05 +FAT32 = 0x06 +CDUNIX = 0x07 +ADF = 0x08 +SGI = 0x09 +SGIXFS = 0x0a +ST40 = 0x0b +CBM = 0x0c +WINIMAGE = 0x0d +COB = 0x0e +UFS1 = 0x0f +QEMU1 = 0x10 +JFFSL = 0x11 +JFFSB = 0x12 +JFFS2L = 0x13 +JFFS2B = 0x14 +FAT12 = 0x15 +FAT16 = 0x16 + +#Executeable File Definitions +ELF = 0x00 +BFLT = 0x01 +PE = 0x02 +MSDOSCOM = 0x03 +DOSCOM = 0x04 +SPSSPORTABLE= 0x05 +SPSSSYSTEM = 0x06 +PPCPEF = 0x07 + +#Packing Specific definitions +ZIP1 = 0x00 +ZIP2 = 0x01 +BZIP = 0x02 +GZIP = 0x03 +ACE = 0x04 +TAR = 0x05 +TRX1 = 0x06 +TRX2 = 0x07 +LZMA = 0x08 +UPX = 0x09 +GNUTAR = 0x0A +CRUSH = 0x0B +HLSQZ = 0x0B +SQWEZ = 0x0C +HPAK = 0x0D +LZOP = 0x0E +MDCD = 0x0F +MSCOMPRESS = 0x10 +INSTALLSHIELD = 0x11 +PAQ = 0x12 +JARARJ = 0x13 +STUFFIT = 0x14 +VAX3 = 0x15 +VAX5 = 0x16 +ARCHIVE = 0x17 +ARCHIVEFILE = 0x18 +HRB = 0x19 +RISCOS = 0x1a +HAP = 0x1b +LIM = 0x1c +FREEZE = 0x1d +ZOO = 0x1e +RAR = 0x1f +EET = 0x20 +RZIP = 0x21 +SQSH = 0x22 +ISC = 0x23 +NWFILE = 0x24 +DSIGDCC = 0x25 +ARJ = 0x26 + +#Document Fileformats +PDF = 0x00 +DOC = 0x01 +RTF = 0x02 + +#Bootloader Definitions +UBOOT = 0x00 + +#Assembler object codes +AVR = 0x00 + +#Image Files(pictures etc.) +GIMPXCF = 0x00 + +#Devices Specific Firmware characteristics +LTRX1 = 0x00 +LTRX2 = 0x01 +WGR614BOOT = 0x02 +WGR614 = 0x03 + +#Router Specific Firmware characteristics specifications + +#Crypto stuff, certificates, keys, typical indications of crypto +DSAPRIV = 0x00 #-----BEGIN DSA PRIVATE KEY----- -----END DSA PRIVATE KEY----- +RSAPRIV = 0x01 #-----BEGIN RSA PRIVATE KEY----- -----END RSA PRIVATE KEY----- +SSHPUB = 0x02 # ssh-dss +CACERT = 0x03 #-----BEGIN CERTIFICATE----- -----END CERTIFICATE----- +CERTREQ = 0x04 #-----BEGIN CERTIFICATE REQUEST----- -----END CERTIFICATE REQUEST----- +PGPMSG = 0x05 #-----BEGIN PGP MESSAGE----- -----END PGP MESSAGE----- + +#Header definitions +HEADER1 = 0x01 #start header +HEADER2 = 0x02 #stop trailer/header +NAME = 0x03 #the format name +DESC = 0x04 #teh description +SUFFIX = 0x05 #the ending of the file, some tools want to have a proper ending, gzip for instance +CHANCE = 0x06 #chance calculator, if at least "chance" bytes are correct print out possibility... +TOOLS = 0x07 #tools of trade to work with that kind of files + + +#Filesystem Specifications +# +#still much too add +########################################### +TYPES = { FS: { \ + MSDOS:{ \ + HEADER1: ('M','Z','H','H'),\ + HEADER2: None,\ + NAME: 'MSDOS',\ + DESC: "MSDOS - Filesystem",\ + SUFFIX: 'img',\ + CHANCE: 2}, + CRAMFS1:{ \ + HEADER1: ('\x45','\x3d','\xcd','\x28'),\ + HEADER2: None,\ + NAME: 'CRAMFS1',\ + DESC: "CRAMFS - Compressed ROMFS",\ + SUFFIX: 'img',\ + CHANCE: 2}, + + CRAMFS2:{ \ + HEADER1: ('C','o','m','p','r','e','s','s','e','d','\x20','R','O','M','F','S'),\ + HEADER2: None,\ + NAME: 'CRAMFS2',\ + DESC: "CRAMFS2 - Compressed ROMFS",\ + SUFFIX: 'img',\ + CHANCE: 8}, + + ROM1FS:{ \ + HEADER1: ('-','r','o','m','1','f','s'),\ + HEADER2: None,\ + NAME: 'ROM1FS',\ + DESC: "ROM1FS - ROM FILE SYSTEM",\ + SUFFIX: 'img',\ + CHANCE: 3}, + + SQUASHFS1:{ \ + HEADER1: ('h','s','q','s'),\ + HEADER2: None,\ + NAME: 'SQUASHFS',\ + DESC: "SQUASHFS - Big Endian",\ + SUFFIX: 'img',\ + CHANCE: 2}, + + SQUASHFS2:{ \ + HEADER1: ('s','q','s','h'),\ + HEADER2: None,\ + NAME: 'SQUASHFS2',\ + DESC: "SQUASHFS - Little Endian",\ + SUFFIX: 'img',\ + CHANCE: 2}, + + FAT32:{ \ + HEADER1: ('\x46','\x41','\x54','\x33','\x32'),\ + HEADER2: None,\ + NAME: 'FAT32',\ + DESC: "FAT32 - Filessystem",\ + SUFFIX: 'img',\ + CHANCE: 2}, + + FAT12:{ \ + HEADER1: ('\x46','\x41','\x54','\x31','\x32'),\ + HEADER2: None,\ + DESC: "FAT12 - Filessystem",\ + NAME: 'FAT12',\ + SUFFIX: 'img',\ + CHANCE: 2}, + + FAT16:{ \ + HEADER1: ('\x46','\x41','\x54','\x31','\x36'),\ + HEADER2: None,\ + NAME: 'FAT16',\ + DESC: "FAT16 - Filessystem",\ + SUFFIX: 'img',\ + CHANCE: 2}, + + CDUNIX:{ \ + HEADER1: ('\x01','\x43','\x44','\x30','\x30','\x31','\x01'),\ + HEADER2: None,\ + NAME: 'CDUNIX',\ + DESC: "CDUNIX - Filessystem",\ + SUFFIX: 'img',\ + CHANCE: 2}, + + ADF:{ \ + HEADER1: ('D','O','S','\x00'),\ + HEADER2: None,\ + NAME: 'ADF',\ + DESC: "ADF - Amiga Filessystem",\ + SUFFIX: 'img',\ + CHANCE: 2}, + + SGI:{ \ + HEADER1: ('\x0B','\xE5','\xA9','\x41'),\ + HEADER2: None,\ + NAME: 'SGI',\ + DESC: "SGI - SGI disk label (volume header)",\ + SUFFIX: 'img',\ + CHANCE: 2}, + + SGIXFS:{ \ + HEADER1: ('\x58','\x46','\x53','\x42'),\ + HEADER2: None,\ + NAME: 'SGIXFS',\ + DESC: "SGI XFS - filesystem data",\ + SUFFIX: 'img',\ + CHANCE: 2}, + + ST40:{ \ + HEADER1: ('\x13','\xa9','\xf1','\x7e'),\ + HEADER2: None,\ + NAME: 'ST40',\ + DESC: "ST40 - component image format",\ + SUFFIX: 'img',\ + CHANCE: 2}, + CBM:{ \ + HEADER1: ('C','B','M'),\ + HEADER2: None,\ + NAME: 'POWER64',\ + DESC: "Power 64 - C64 Emulator Snapshot",\ + SUFFIX: 'img',\ + CHANCE: 2}, + + WINIMAGE:{ \ + HEADER1: ('W','I','N','I','M','A','G','E'),\ + HEADER2: None,\ + NAME: 'WinImage',\ + DESC: "WinImage - WinImage Archive data",\ + SUFFIX: 'img',\ + CHANCE: 2}, + COB:{ \ + HEADER1: ('C','o','B','1'),\ + HEADER2: None,\ + NAME: 'COB1',\ + DESC: "CoB1 - lantronix html/webserver filesystem",\ + SUFFIX: 'img',\ + CHANCE: 2}, + UFS1:{ \ + HEADER1: ('\x00','\x01','\x19','\x54'),\ + HEADER2: None,\ + NAME: 'UFS1',\ + DESC: "UFS1 - Unix Fast File system [v1] (little-endian)",\ + SUFFIX: 'img',\ + CHANCE: 2}, + QEMU1:{ \ + HEADER1: ('\x51','\x46','\x49','\xfb'),\ + HEADER2: None,\ + NAME: 'QEMU1',\ + DESC: "QEMU1 - Qemu Image, Format: Qcow",\ + SUFFIX: 'img',\ + CHANCE: 2}, + JFFSL:{ \ + HEADER1: ('\x31','\x39','\x38','\x34'),\ + HEADER2: None,\ + NAME: 'JFFS1_LE',\ + DESC: "JFFS1 - version 1, little endian",\ + TOOLS: "mtd-tools, mkfs.jffs etc.",\ + SUFFIX: 'img',\ + CHANCE: 2}, + + JFFSB:{ \ + HEADER1: ('\x34','\x38','\x39','\x31'),\ + HEADER2: None,\ + NAME: 'JFFS1_BE',\ + DESC: "JFFS1 - version 1, big endian",\ + SUFFIX: 'img',\ + TOOLS: "mtd-tools, mkfs.jffs etc.",\ + CHANCE: 2}, + + JFFS2L:{ \ + HEADER1: ('\x85','\x19','\x03','\x20'),\ + HEADER2: None,\ + NAME: 'JFFS2_LE',\ + DESC: "JFFS2 - version 2, little endian",\ + SUFFIX: 'img',\ + TOOLS: "mtd-tools, mkfs.jffs etc.",\ + CHANCE: 2}, + + JFFS2B:{ \ + HEADER1: ('\x19','\x85','\x20','\x03'),\ + HEADER2: None,\ + NAME: 'JFFS2_BE',\ + DESC: "JFFS2 - version 2, big endian",\ + SUFFIX: 'img',\ + TOOLS: "mtd-tools, mkfs.jffs etc.",\ + CHANCE: 2} + }, + + EXEC: { + ELF:{ \ + HEADER1: ('\x7f','E','L','F'),\ + HEADER2: None,\ + NAME: 'ELF',\ + DESC: "ELF - File Format",\ + SUFFIX: 'elf',\ + CHANCE: 2}, + BFLT:{ \ + HEADER1: ('b','F','L','T'),\ + HEADER2: None,\ + NAME: 'BFLT',\ + DESC: "bFLT - File Format",\ + SUFFIX: 'bflf',\ + CHANCE: 2}, + PE:{ \ + HEADER1: ('P','E','\x00','\x00'),\ + HEADER2: None,\ + NAME: 'PE',\ + DESC: "PE - File Format",\ + SUFFIX: 'exe',\ + CHANCE: 2}, + MSDOSCOM:{ \ + HEADER1: ('\xfc','\x57','\xf3','\xa5','\xc3'),\ + HEADER2: None,\ + NAME: 'COM',\ + DESC: "COM executable for MS-DOS",\ + SUFFIX: 'com',\ + CHANCE: 2}, + DOSCOM:{ \ + HEADER1: ('\xfc','\x57','\xf3','\xa4','\xc3'),\ + HEADER2: None,\ + NAME: 'COMDOS',\ + DESC: "COM executable for DOS",\ + SUFFIX: 'com',\ + CHANCE: 2}, + SPSSPORTABLE:{ \ + HEADER1: ('\xc1','\xe2','\xc3','\xc9'),\ + HEADER2: None,\ + NAME: 'SPSS',\ + DESC: "SPSS Portable File",\ + SUFFIX: None,\ + CHANCE: 2}, + SPSSSYSTEM:{ \ + HEADER1: ('$','F','L','2'),\ + HEADER2: None,\ + NAME: 'SPSS2',\ + DESC: "SPSS System File",\ + SUFFIX: None,\ + CHANCE: 2}, + PPCPEF:{ \ + HEADER1: ('J','o','y','!','p','e','f','f','p','w','p','c'),\ + HEADER2: None,\ + NAME: 'PPC_PEF',\ + DESC: "header for PowerPC PEF executable",\ + SUFFIX: None,\ + CHANCE: 2} + }, + + PACKERS: { + ZIP1:{ \ + HEADER1: ('\x50','\x4b','\x03','\x04'),\ + HEADER2: None,\ + NAME: 'ZIP1',\ + DESC: "ZIP1 - Phil Katz ",\ + SUFFIX: 'zip',\ + CHANCE: 2}, + ZIP2:{ \ + HEADER1: ('\x50','\x4b','\x01','\x02'),\ + HEADER2: None,\ + NAME: 'ZIP2',\ + DESC: "ZIP2 - Phil Katz ",\ + SUFFIX: 'zip',\ + CHANCE: 2}, + BZIP:{ \ + HEADER1: ('\x42','\x5a','\x68'),\ + HEADER2: None,\ + NAME: 'BZIP',\ + DESC: "BZIP - a block-sorting file compressor",\ + SUFFIX: 'bz2',\ + CHANCE: 2}, + GZIP:{ \ + HEADER1: ('\x1f','\x8b'),\ + HEADER2: None,\ + NAME: 'GZIP',\ + DESC: "GZIP - Lempel-Ziv coding (LZ77)",\ + SUFFIX: 'gz',\ + CHANCE: 2}, + ACE:{ \ + HEADER1: ('*','*','A','C','E','*','*'),\ + HEADER2: None,\ + NAME: 'ACE',\ + DESC: "ACE - e-merge GmbH - winace.com",\ + SUFFIX: 'ace',\ + CHANCE: 2}, + TAR:{ \ + HEADER1: ('\x00','u','s','t','a','r','\x00'),\ + HEADER2: None,\ + NAME: 'TAR',\ + DESC: "TAR - tape archiver",\ + SUFFIX: 'tar',\ + CHANCE: 2}, + TRX1:{ \ + HEADER1: ('\x30','\x52','\x44','\x48'),\ + HEADER2: None,\ + NAME: 'TRX1',\ + DESC: "TRX1 - ",\ + SUFFIX: None,\ + CHANCE: 2}, + TRX2:{ \ + HEADER1: ('H','D','R','0'),\ + HEADER2: ('0','R','D','H'),\ + NAME: 'TRX2',\ + DESC: "TRX2 - ",\ + SUFFIX: None,\ + CHANCE: 2}, + LZMA:{ \ + HEADER1: ('\x5d','\x00','\x00','\x80'),\ + HEADER2: None,\ + NAME: 'LZMA',\ + DESC: "LZMA - Lempel-Ziv-Markov chain-Algorithm",\ + SUFFIX: 'lzma',\ + CHANCE: 2}, + UPX:{ \ + HEADER1: ('U','P','X','!'),\ + HEADER2: None,\ + NAME: 'UPX',\ + DESC: "UPX - Ultimate Packer for eXecuteables",\ + SUFFIX: 'upx',\ + CHANCE: 2}, + GNUTAR:{ \ + HEADER1: ('u','s','t','a','r','\x20','\x20','\x00'),\ + HEADER2: None,\ + NAME: 'GNUTAR',\ + DESC: "GNUTAR - tar == teer + tape archiver",\ + SUFFIX: 'tar',\ + CHANCE: 2}, + CRUSH:{ \ + HEADER1: ('C', 'R', 'U', 'S', 'H'),\ + HEADER2: None,\ + NAME: 'CRUSH',\ + DESC: "CRUSH - Crush archive data",\ + SUFFIX: None,\ + CHANCE: 2}, + + HLSQZ:{ \ + HEADER1: ('H', 'L', 'S', 'Q', 'Z'),\ + HEADER2: None,\ + NAME: 'HLSQZ',\ + DESC: "HLSQZ - Squeeze It archive data",\ + SUFFIX: None,\ + CHANCE: 2}, + + SQWEZ:{ \ + HEADER1: ('S', 'Q', 'W', 'E', 'Z'),\ + HEADER2: None,\ + NAME: 'SQWEZ',\ + DESC: "SQWEZ - archive data",\ + SUFFIX: None,\ + CHANCE: 2}, + HPAK:{ \ + HEADER1: ('H', 'P', 'A', 'K'),\ + HEADER2: None,\ + NAME: 'HPAK',\ + DESC: "HPAK - archive data",\ + SUFFIX: None,\ + CHANCE: 2}, + LZOP:{ \ + HEADER1: ('\x89','\x4c','\x5a','\x4f','\x00','\x0d','\x0a','\x1a','\x0a'),\ + HEADER2: None,\ + NAME: 'LZOP',\ + DESC: "LZOP - lzop comrpressed data",\ + SUFFIX: None,\ + CHANCE: 2}, + MDCD:{ \ + HEADER1: ('M', 'D', 'm', 'd'),\ + HEADER2: None,\ + NAME: 'MDCD',\ + DESC: "MDCD - archive data",\ + SUFFIX: None,\ + CHANCE: 2}, + MSCOMPRESS:{ \ + HEADER1: ('\x88','\xf0','\x27'),\ + HEADER2: None,\ + NAME: 'MS',\ + DESC: "MS Compress archive data",\ + SUFFIX: None,\ + CHANCE: 2}, + INSTALLSHIELD:{ \ + HEADER1: ('\x13','\x5d','\x65','\x8c'),\ + HEADER2: None,\ + NAME: 'MSIS',\ + DESC: "InstallShield - Z archive Data",\ + SUFFIX: None,\ + CHANCE: 2}, + PAQ:{ \ + HEADER1: ('\xaa','\x40','\x5f','\x77','\x1f','\xe5','\x82','\x0d'),\ + HEADER2: None,\ + NAME: 'PAQ',\ + DESC: "PAQ - archive data",\ + SUFFIX: None,\ + CHANCE: 2}, + JARARJ:{ \ + HEADER1: ('\x1a','J','a','r','\x1b'),\ + HEADER2: None,\ + NAME: 'JAR',\ + DESC: "JAR (ARJ Software, Inc.) archive data",\ + SUFFIX: 'arj',\ + CHANCE: 2}, + STUFFIT:{ \ + HEADER1: ('S','t','u','f','f','I','t'),\ + HEADER2: None,\ + NAME: 'STUFFIT',\ + DESC: "StuffIt Archive",\ + SUFFIX: 'stuffit',\ + CHANCE: 2}, + VAX3:{ \ + HEADER1: ('\x65','\xff','\x00','\x00'),\ + HEADER2: None,\ + NAME: 'VAX3',\ + DESC: "VAX 3.0 archive",\ + SUFFIX: None,\ + CHANCE: 2}, + VAX5:{ \ + HEADER1: ('\x3c','\x61','\x72','\x3e'),\ + HEADER2: None,\ + NAME: 'VAX5',\ + DESC: "VAX 5.0 archive",\ + SUFFIX: None,\ + CHANCE: 2}, + ARCHIVE:{ \ + HEADER1: ('=','<','a','r','>'),\ + HEADER2: None,\ + NAME: 'AR',\ + DESC: "archive",\ + SUFFIX: 'ar',\ + CHANCE: 2}, + ARCHIVEFILE:{ \ + HEADER1: ('21','3c','61','72'),\ + HEADER2: None,\ + NAME: 'ARfile',\ + DESC: "archive file",\ + SUFFIX: 'ar',\ + CHANCE: 2}, + HRB:{ \ + HEADER1: ('\xc0','H','R','B'),\ + HEADER2: None,\ + NAME: 'HRB',\ + DESC: "Harbour HRB file",\ + SUFFIX: 'hrb',\ + CHANCE: 2}, + RISCOS:{ \ + HEADER1: ('A','r','c','h','i','v','e'),\ + HEADER2: None,\ + NAME: 'RISCOS',\ + DESC: "RISC OS archive (ArcFS format)",\ + SUFFIX: None,\ + CHANCE: 2}, + HAP:{ \ + HEADER1: ('\x91','\x33','H','F'),\ + HEADER2: None,\ + NAME: 'HAP',\ + DESC: "HAP archive data",\ + SUFFIX: None,\ + CHANCE: 2}, + LIM:{ \ + HEADER1: ('L','I','M','\x1a'),\ + HEADER2: None,\ + NAME: 'LIM',\ + DESC: "LIM archive data",\ + SUFFIX: None,\ + CHANCE: 2}, + FREEZE:{ \ + HEADER1: ('\x1f','\x9f','\x4a','\x10','\x0a'),\ + HEADER2: None,\ + NAME: 'FREEZE',\ + DESC: "Freeze archive data",\ + SUFFIX: None,\ + CHANCE: 2}, + ZOO:{ \ + HEADER1: ('\xfd','\xc4','\xa7','\xdc'),\ + HEADER2: None,\ + NAME: 'ZOO',\ + DESC: "Zoo archive data",\ + SUFFIX: None,\ + CHANCE: 2}, + RAR:{ \ + HEADER1: ('R','a','r','!'),\ + HEADER2: None,\ + NAME: 'RAR',\ + DESC: "RAR archive data",\ + SUFFIX: 'rar',\ + CHANCE: 2}, + EET:{ \ + HEADER1: ('\x1e','\xe7','\xff','\x00'),\ + HEADER2: None,\ + NAME: 'EET',\ + DESC: "EET archive",\ + SUFFIX: None,\ + CHANCE: 2}, + RZIP:{ \ + HEADER1: ('R','Z','I','P'),\ + HEADER2: None,\ + NAME: 'RZIP',\ + DESC: "rzip compressed data",\ + SUFFIX: None,\ + CHANCE: 2}, + SQSH:{ \ + HEADER1: ('S','Q','S','H'),\ + HEADER2: None,\ + NAME: 'SQUISH',\ + DESC: "squished archive data (Acorn RISCOS)",\ + SUFFIX: None,\ + CHANCE: 2}, + ISC:{ \ + HEADER1: ('I','S','c','('),\ + HEADER2: None,\ + NAME: 'CAB',\ + DESC: "InstallShield CAB",\ + SUFFIX: None,\ + CHANCE: 2}, + NWFILE:{ \ + HEADER1: ('P','a','c','k','e','d','\\',' ','F','i','l','e','\\'),\ + HEADER2: None,\ + NAME: 'NETWARE',\ + DESC: "Personal NetWare Packed File",\ + SUFFIX: None,\ + CHANCE: 2}, + DSIGDCC:{ \ + HEADER1: ('D','S','I','G','D','C','C'),\ + HEADER2: None,\ + NAME: 'CROSSEPAC',\ + DESC: "CrossePAC archive data",\ + SUFFIX: None,\ + CHANCE: 2}, + ARJ:{ \ + HEADER1: ('\x60','\xea'),\ + HEADER2: None,\ + NAME: 'ARJ',\ + DESC: "ARJ",\ + SUFFIX: 'arj',\ + CHANCE: 2} + }, + + DOCS: { \ + PDF:{ \ + HEADER1: ('\x25','\x50','\x44','\x46','\x2e'),\ + HEADER2: None,\ + NAME: 'PDF',\ + DESC: "PDF - Portable Document Format",\ + SUFFIX: 'pdf',\ + CHANCE: 2}, + DOC:{ \ + HEADER1: ('\xd0','\xcf','\x11','\xe0','\xa1','\xb1','\x1a','\xe1'),\ + HEADER2: None,\ + NAME: 'DOC',\ + DESC: "DOC - Microsoft Document Format",\ + SUFFIX: 'doc',\ + CHANCE: 2}, + RTF:{ \ + HEADER1: ('{','\\','\\','r','t','f'),\ + HEADER2: None,\ + NAME: 'RTF',\ + DESC: "RTF - Rich Text Format data",\ + SUFFIX: 'rtf',\ + CHANCE: 2} + }, + + BOOT: { \ + UBOOT:{ \ + HEADER1: ('\x27','\x05','\x19','\x56'),\ + HEADER2: None,\ + NAME: 'UBOOT',\ + DESC: "UBOOT - PPCBoot Image - maybe bootloader",\ + SUFFIX: 'uboot',\ + CHANCE: 2} + +}, + ASM: { \ + AVR:{ \ + HEADER1: ('a','v','a','o','b','j'),\ + HEADER2: None,\ + NAME: 'AVR',\ + DESC: "AVR assembler object code",\ + SUFFIX: 'avr',\ + CHANCE: 2} +}, + PICTURES: { \ + GIMPXCF:{ \ + HEADER1: ('g','i','m','p','\\',' ','x','c','f'),\ + HEADER2: None,\ + NAME: 'GIMP_XCF',\ + DESC: "GIMP XCF image data",\ + SUFFIX: 'xcf',\ + CHANCE: 2} +}, + + DEVICES: { \ + LTRX1:{ \ + HEADER1: ('D','S','T','-','L','T','R','X'),\ + HEADER2: None,\ + NAME: 'LTRX1',\ + DESC: "LTRX1 - Lantronics Firmware Part detected",\ + SUFFIX: None,\ + CHANCE: 2}, + + LTRX2:{ \ + HEADER1: ('L','T','R','X'),\ + HEADER2: None,\ + NAME: 'LTRX2',\ + DESC: "LTRX2 - Lantronics Firmware Part detected",\ + SUFFIX: None,\ + CHANCE: 2}, + + WGR614BOOT:{ \ + HEADER1: ('*','#','$','^'),\ + HEADER2: None,\ + NAME: 'NETGEAR_BOOT',\ + DESC: "NETGEAR WGR614v9 Bootware - unknown bootloader maybe",\ + SUFFIX: None,\ + CHANCE: 2}, + + WGR614:{ \ + HEADER1: ('@','U','1','2','H','0','9','4','T'),\ + HEADER2: None,\ + NAME: 'NETGEAR_FW',\ + DESC: "NETGEAR WGR614v9 Firmware",\ + SUFFIX: None,\ + CHANCE: 2} + + }, + + CRYPTO: { + DSAPRIV:{ \ + HEADER1: ('-----BEGIN DSA PRIVATE KEY-----'),\ + HEADER2: ('-----END DSA PRIVATE KEY-----'),\ + NAME: 'DSAPRIV',\ + DESC: "DSAPRIV - Private Key in DSA Format",\ + SUFFIX: None,\ + CHANCE: 2}, + + RSAPRIV:{ \ + HEADER1: ('-----BEGIN RSA PRIVATE KEY-----'),\ + HEADER2: ('-----END RSA PRIVATE KEY-----'),\ + NAME: 'RSAPRIV',\ + DESC: "RSAPRIV - Private Key in RSA Format",\ + SUFFIX: None,\ + CHANCE: 2}, + + SSHPUB:{ \ + HEADER1: ('ssh-dss'),\ + HEADER2: None,\ + NAME: 'SSHDSS',\ + DESC: "SSHDSS - Public ssh key",\ + SUFFIX: None,\ + CHANCE: 2}, + + CACERT:{ \ + HEADER1: ('-----BEGIN CERTIFICATE-----'),\ + HEADER2: ('-----END CERTIFICATE-----'),\ + NAME: 'CACERT',\ + DESC: "CACERT - Certificate Format",\ + SUFFIX: None,\ + CHANCE: 2}, + + CERTREQ:{ \ + HEADER1: ('-----BEGIN CERTIFICATE REQUEST-----'),\ + HEADER2: ('-----END CERTIFICATE REQUEST-----'),\ + NAME: 'CERTREQ',\ + DESC: "CERTREQ - Certificate Request Format",\ + SUFFIX: None,\ + CHANCE: 2}, + + PGPMSG:{ \ + HEADER1: ('-----BEGIN PGP MESSAGE-----'),\ + HEADER2: ('-----END PGP MESSAGE-----'),\ + NAME: 'PGPMSG',\ + DESC: "PGPMSG - Pretty Good Privacy Message Format",\ + SUFFIX: None,\ + CHANCE: 2}, +} +} + +TYPES_DEF = { 'FS':FS,\ + 'EXEC':EXEC,\ + 'PACKERS':PACKERS,\ + 'DOCS':DOCS,\ + 'BOOT':BOOT,\ + 'ASM':ASM,\ + 'PICTURES':PICTURES,\ + 'DEVICES':DEVICES,\ + 'CRYPTO':CRYPTO + } diff --git a/lib/FUDGEheader.pyc b/lib/FUDGEheader.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20a4f59a224517b0e060fb92ca3b736051c847d8 GIT binary patch literal 11662 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 literal 0 HcmV?d00001 diff --git a/lib/__pycache__/FUDGEanalyse.cpython-37.pyc b/lib/__pycache__/FUDGEanalyse.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac32424911ecbcc12b442eabf4cc663c6251d028 GIT binary patch literal 13226 zcmb7LON<=HdG6QD^gQ;#)$&bIElRYwHo2tehh$2YMU%UtENMxLlx2-5PiCijc4s*| zv#jpn?zU$I1C*V_u>)P=_@Ed|zyaa}2y*ZtNbWfVL688sq>r`_VcsyWH?)x6||YC&>;y;v>U9K&h*>!oT5Ww%;ZUbUif)d`hXh4-xL zZ?yEJZ(Mz8q28&tx1(@=V>|WL@``+DhEw9)EL#_m z?wKz$ct)9 zO(QR<18N3&Sv{g=kyq3~bqM){I;@T$pHxTHG2~O~xH^G+T0N>xB0r!WQvvcB^|(5P z{1NqpdJ_4pI;}o~{GfVDok4y`T~wb{pL@@)9#&`7IkX&6^XfeEqw0cs8u>9bhh6`C zTDZJ)`PH{>E;L9r{xRp0g-pLiLD_w)X02FA?bz9MAF$*NOF8ABo^P?WAZ1_DqNF9| zqP;9>MbZgLCncRy9-dB1&ja#>8EJn+(pgClN_t4;(DSg$BRwK5N2UKUX*n+G2}vK7 z(N0SFF;zhSK>6|GeQPbaZyyHh9_CC>$$X!X^hs4h%V||c`WaP0`jnbLdPcd}qRINB z*p8rBDi({aPP5exi^X$6ycz~92XQyZI7)g%aXk*$p&QLhS%*=t9Y;a88AM^bId6st zI9jtAZ*PRso5MAFT8EuDkVbUhsK;@rJ3$;qF}rWHdn>ID$8M~4yHVH)OiQC(k0Q>4 z9fM|9vva%K3ESJI!{`Omn!U%a!@-*h^B7x~3ZsT@ZN%t;sdP8+wR%SdjW#~a@uRS= z8>_>)XVoZ%*sh#k-o{j+(dgG3)aZ|TqOp9A2Bxv+x zKf|q9*Bf!I(TTC}PH%lVl*I~ZyFILy1di}ADe#NgEBs`%T5q?5%`nK?uszU6_{>+? z5uZSME>Nw8kOZ5^xkhHsv83!RVWIU9icHChda-J0zS!->8@(9s>#zYLY|D#ik%h>- zvi_a=x-cYjl8Up>UN+O;sB2lS4mY|wmJf2dW8KN8y(lmpIIMY*(`dKAQk$)KmE5>0 zT%64u8$6swYH4bXrH8XflhWeixdpSgiw|k4l9@Aa*Uu%F&s8s+`*Lu5{`R?dzLU6T zcjllr>(OeGyD7WRiKUQIFHSk)X6PE>D<> zaDQ5gS9Mrdq}jfO#`L?)smL^ z5Sv9VH5I%kl~S!%?}00It(KN+HGDB@bvvl|wOZWmwoM6?2x~Q%Ymag@venuL)!DVx zX~nhLVbr2mkyyv`cHS%5B^$Zz%h0}^7^@aO54xKjsE1Y*oK}ms z=5koFkTZ&Fy_QOyZj|Q1fhg-dzBp&=FCg2$B_((p_dx!YFw%G&ai1)E8vi^enHel- z)Tu#3%s=Ks--%nv=_~t=qfhjmSn}BEi#OFdi)XGpdlt_;^m%spY#J>&v|JmsC@g)> zO|Avv2T@CI>iWh8mI{S^iOS%!L8q(N>upocc+XUXAH|dzjf2)(q6y~m`YB8@b7UK_MK#Euv=NbA@#Za zIsC+b`AZZGrFU#)?>NfYabt)6!m}sQacc)&4*U+UZ#NzD zyz{K|$mxVRdgR7>=KH2^Hgczew+sD@$58fVCb{8vi&D>d50c|%Wd+l2w^ z%h=zc%Yj{9!Lso8{)<`R!sXL8FI2|Rk zY`EDz$y|$lGiozz) z|IrWdc61Vn<@vS;k(#t89WaYMBdKrCIu&QyP7VxMXZYrjc_?R$yM%duhQjvA*xIGC za30VNc4IONy#M>oj{T<9`L1U{o=W=bwzcEfRx%mq;eI;zQ5J>b?Ktl`-?HAaI^RUk z0(!=u+Vd=W`sn%7Pwk13`;5SH$=M)kt#7~`6;F*8h9#Y`4_p}y-2`8P^m5|9 zeeRuy|BJt+xuSj+E7f0OB2*EQc^oR8x)JL2Rz&T;pmo^J=YVEZh$T^rgLKA89vM(=?7bn~s2hiL z`vof7^ZV&0DnAqB7{LUn2s(kL`HYUFP8EBOh!k}6pd>(L(9wfo$nDtg+Cw^$I`4gI zPtvi7o`3zRJwZp610D0p$=^XoP|k?jb$a8cBYhR?6guh!l$Pd7A4f+caf|4n112vq zxyIyWBy)~Y#9wDaVVD^EJ=o~KMf-?4_9T?>e)^28@!+K)Y4*gEComMler$iH7fA@l zl-OuvijEJqKw^y%E4Siq$7q);3_`F0OLLxCoiJ^Hq0^ka^a<9tI2`dGxgxrzmODA7 z4~EZ&lV_hHU;PIvm@e(EtFLWO$M!DxXU%$G!?7fXU6k`<<-%CmA1fD!W&eT2_rTNEC(iDd z`)9|Qk7m~)FAA!YTnS8IBB4iPHEi6Wdtm%l2?jSbh^^z6iS-yBcC~^PXuv6^-2%fd zi92xXL;HMc*Ai!bKDFmRv>*S_J_oJy1om@`Wyz1}Y%LS>T1|!tYw(iw8TKTV(*hjg zcD)h8Yv^u<8XO=t1HLoEY)w8+3wVXbZ9hYoMkD?U`b8Yof;;Ni6&Qghqt9wE~>_iqnsxCs=a0~IsS@r?c7Sw}IxCtT^ zffdlRH7ir|gE7QGkXNE$84gGb|3%rGqk%acGMR;`RP)_-F#<6P!pcgP0NY$xnwc3^A9JMSU)(8HX^5L;(rD;6i=K3wpt z);ouG>=-UMNDim`sAW97yVFnv2I@?5Pn~W&b#~56bGqJH2~!vDn5LI2>&xb!dIUc9 zbMeFgMX76D-zHpVG?_DNCkF@f-qU9oMNq8|wb=$LF}MvJ2d-1d;Tn(XXoLGZs=KJ? z#_HhEo%~omhdRKvHR?n-&(K01+~Is5I_!gj@O=Rm&QE?52&{wXNrPL-Aq@a2BVc5` zzS3$0%ORi?C6A*<*fAOg0eQGWEQG4Y1m*@TBQi9?efX5)8cZM%On~gS+-tQ}(Ay}H zV+9BrM-AZMV$&kXhoBq;cgDPCND8@_5EX%}CMHK^XeNsfa>zEOrw71}QGddZhw&;bQ6u0^kvegNxj20wXZNm8+?6^Z~x+0u!3^ zjS;8j$p%{;3}y-!wc@joNt4MwQ~wCy4p`cD&FqM5S&p0~KABH{O#Zn(RN~-Y$xV(A z`0ZofZ*Y?Qo5qivCqDIWP=H;uV5z_-hU<_Ew;>lULoVEfT)0Z*cl?&MQ{0;EyAZC* zPO0zrONXr;|LzNKLh!(GK5VcbyM133;F--TVXVfrIzIy;JE| zb_)-z+s@9!&SdN(Fq1F!C;F4kEO-Ma7uQNVQ*7%`8D3V!5Ar(<+RD3?evv%QS@mJ} z*Ctfy83gFHiM#K<33=L`6o;k^j;|y?p_&ac2!()_*iu87v*1(!#&HYaoxp7H;$g}n zU`Ro<5dvfr^$OjYc|{r!rXa8NHbeoy+&WnshayU(0*bi48AdoL0aOcE_!1m{838gu zmA~25V#P0=L91?n7<$MxhBJgw%6O0zz|gJExu=uIsH+#Rzp-?c5hbR3UBFB#W?jyvz;r0=AK&QlDg6pQfj|UE82G!s0fR0mZ(3+{*EbLqq`n9l zVJeRx8^XekddH~$$2sp8negC7GYHmmOkP7WH=VfzhL?@kyUM2@v(l=wx~gaKFtuA% z_!c#O9Y&=dH&*o;U%JDj&1A34R3~5?=oA@329y?JO;+AV^f~y4C=?Dx#U67G?m94NCT{+4_uJvinjj);)vRXtqqcG6tNi$t=>i>>Igz_Q+2 zx7A7EEUtI&g_-XtQ>uFShDvil{PgxdN~h(qCB05MAHmctZyIO30dX3;1q3U3kTitYnMpQeTs@hIzCME4wMG{g!I=xQ9> zapJDo9KsVZVUxaQ%Y%lQj>ge)Zf_;d=?KB2#E7T(G(bX3 zH@u?{vQDg06U9w^p3_8)7K8PF%L|grV#2SjY^PIO%N9SR@k@M)2lZ#- z=+DiS$9DT2JT?)|dn{2KnH?8bmntpI@dzu_hfvPAwpw0?<4Ev?y3*qzPqoNTnC;)U z^Ci=neYb`T40fHykBBN6m+Z^HeiZ49UK&z?QcNBeDp2|EjdQXP{<)wX#!p2CQx3!$ zjr&r2;Y>NY%nmT1D3ar1ZRGq724ETzeUzZN*G^-a!9R<=#862eK?ZXS@!^-U(f znTVnn_i2;WEheIWo?z)oCUj?vsJ+C}<4j&;BKF`9S)z8*r8fyRev#?C%bH#E??&xg3%(TLni75$ch)Tx zjIZUnP++->ct!+Sj*4;>2uaEH-H!+gMHcv_Tu zXtk>givuLZ#c=yJpgL2l$rpLhPh0`xrrZRYYYUtM^XMeU1{?6v&P&4xm)tYtpufWy z@ah=dI>3p4*2ntS>^ph=0~`Sg=de!PMUEn&C=HKowPTzv;aRSaC^!R>o;OF3gVsLc zVw9nD_q9O%4)8)MrZP(WRt7<~@xtp1k@=WgHTIpM#-nx^%KG1+LXF4RZO7jFKAZ}8iLeO?ouggI`vW_gMG5W|;|(}L zM6?idh~orF7sn~Tbq%(}+s(`+@sdDuVgF!WUj!@`(&*O|(6$fn@ zzaR_w4sUm=EnIKD3_`i5$aW7x2XROqn2xG$x{{^x9VayQjbHWcD zRVX==aFpc63;dO}Q$iT4ee|^a5kSw@8nQf<$&tjjgaqb!1tu!29uDOcf zMj22E3K_5+-x=IB8xZ^d$ux$P6uwW0l_m#kG1rlv;YQHAvvMOxW^b3_V%d!fO&pnP zC=3F>t&7<)ueth>BaD0>k&JQtAn=HcAZm;!iVv*kt-GfH1X!84Nw8S|bL=9-1ipRd z?1+k|+z^f(T-xk)T3ZBibz2TFK<(gEHrZ3$M4Y}EDMC8R6pvwuob^5{M-=8X z0wH|QT;0mVJi_T7t`u`J#0*-EUb~JHTbMBLb$~m6UEVor44GWc?G$i5PN9zYCxmT; zUZ8>)R}KO0$uSZ#q0PAbG#Se8)g9(OqTvD+HtW|^9FcuT$SwSF2UE$9^twS<@7wjL2e(e2bF508{zd%CDChl>|D0d+i=|8GQ;@gk4j2kNive(Jbvx1o3saBBB)l!AdyQCs z4^5i41Jfz|za8^q{=e~2DA6Y9eU7E)nH1SQ-(6nAp}2hM;}($m&zoH)os1?O!1-AY OC!yj1+D8cLyZ;B5{?xz# literal 0 HcmV?d00001 diff --git a/lib/__pycache__/FUDGEheader.cpython-37.pyc b/lib/__pycache__/FUDGEheader.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e1a209a2a379ecb6b4180181f4d2966eaee73b6b GIT binary patch literal 11208 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 z z!bzs@WqvbdVh?*U%P0$wU8mC`%uTV^iDnJYbG74QPjR(N6>S6G zLj|SnibrZf&s^<-V6K+Kijaz)P8l(0gIuCSrN!cqit2doX%7qxF1$~O5mC|nuAjwI z9nKh0ak51u1BWT70Yn50R!elHqCw~{hI$KC# znbk)M#oW2}5}GGJIVRQwvFy>lUF5EB#OVw-Rq{?XJVumw-@r0 z*_^!t1BCgI2#3U=bhT-sh%@a^@>%IoeEYVuz>< zVt=Azj_c2)hdZ1QDcoR0PbC+Y1u_Dix$0wM-;7ni#ajEfB;RER?bFlx;ng z&7Gqa8#Biqrio+y1Zh#5z*I?(>8LYM@{-}tOVErj)4Jo1&oZ=Ks z7hXN*%7cYC707W!$54JicqY4}-Oduy`uu$||2K%s<)n_WjK$&^XQ5f19v#I%QO6(_D$#>{8I!lx5SKqS zGeKSd$;j|-XCYMIy<=Ck05pvcdrN*)819vZdp(8$-cH5%2uZD`{8`+x#z@)?sgdV4 z(kjCmJ3Cn8?i_2}hwJ`M7L26nnB~l?4p6$QZd!N9DIz8qt21o*U?q)H>*`eNAMgD!0Pa-GJo+Q z-b+!$Oc8G)zTQX*nMYDJM>Uwmq&P#ybl4Rg@A?w)q|*YKZ5$BeNDi;}i_uZhTI9qD z#?pqt47_o0G1;pPA~TUA{{?xuTLySS-aYL4Sbx9M!ivS&nfbYBxtUIp>t`ARumv*9 znE5#MPeofNgFeBZrn-n#(PtPLXD+1bfw3eai-kZ`oR$*TpQf#EDaRgM+amF5t6FJh zsS+t_R~E$EUmWY?rqXTG`O-Lyy|qTi?xKfxQVd@-(&a3gJ)`j` zJYQ}cqV{C%YUkvkgch5McC$QVQqS^_3tW)+3%FsSJ|i;}NgK|Z6pqrx$!tOIv&%!6 zsB){ZTy?w)<1q^3%t| zfr_?SmR|&uZ^ApU+~7TN9R$lw?ZDPu%;8lJ?_lyRf%&GaQMH6!eo#hzprS33sYk@+ z#Yg4k88KKTdDQiF_eY)e5JXSrb0{hnC%rDb>$8fvaM0CW#i^yFh)E|_C|aCcUpfy=> zPh>BR*!l<_6^is?%L3DM6AsFi=Mz*Q(cztxnxe93XVIc3(@rZy&!X`sa4_I{apbI> zE7PIO#xi@Jhnrn(Obi6^VMLx`MFr_};A$7S7;#iR*3=VErcMt>W6lyv=}4*fU%6^u z%%S}~BSUO*IU^<4Er7%%7sFiUZLxO2tXUGMb{4c(*)DDQXBdr`QunQuxU! zG%L6CwR5`~c0+h7b5n_z=SF@iw@|4x-fopjSPsqJZoHALkXLe=9x3E=It=u4_9bVv zsHAL#sFBXZWAP|jLAF3^tp(~4zDVH}BAszo%PRs$ZM@Hn=5thkRg8^S53Z+;fkDH} z$V@g|Et6R$_!x<-;N)yliMs64HjcCOokV=pm{q4#PfAZ3rWxsZS)J;U8^n{O+2d@J zSJA?DxqM0QDHbi+x4BDt60d3WWlCm-S>6~kUB5AGMBNaU;S)w0w^;I^H0H5Zm`S%O z6^~{H(*{1Qb{i+-Q6z%V^r=*)Kb&_1nNumldY_3Ys*J}PT;?I%1 zK=MhFPm_FxCw07(Hyx@(m-NCndaXn`W>P)yHkT*{JZqzdnDhd$Dv*$(ru(Ws)
dte_qIs?2HX|_Zae<%#Nu`Zqwn3qBUKxk2WHH z>3RGY8^6!5&H8J6*^}A4{;uA?{=VM+m%W$qd?*$DN$-HC#MNh#gc`L*PY%J^ca5{y z|J}Q5m3lUN#dm%!IIF(@^L>-=nr-=tbFS-yYxw=|=lIIYR%bb@EoZlEo#i#^`~TfC zh?yG1A8Dhy{&IJ6#8l%@teH6VclEX}yGNgTy=QYdU#dA>`>x3^BT)&22O`V#%w4VK QS7BH8ng`k0-x5KF)~258lvMqK%94!y zJbky!qTKStqExV15UJ$EvQ%_&p!xdo@gS?>