Standardized on speed, rewrote statsgen, many bug fixes.

This commit is contained in:
iphelix 2013-07-24 18:15:46 -07:00
parent e041e7906d
commit 4ed47777b5
4 changed files with 215 additions and 159 deletions

View File

@ -46,12 +46,15 @@ class MaskGen:
self.minlength = None
self.maxlength = None
self.mintime = None
self.maxtime = None
self.mincomplexity = None
self.maxcomplexity = None
self.minoccurrence = None
self.maxoccurrence = None
# PPS (Passwords per Second) Cracking Speed
self.pps = 10000000000
self.pps = 1000000000
self.showmasks = False
# Counter for total masks coverage
@ -81,20 +84,25 @@ class MaskGen:
mask_occurrence = int(occurrence)
mask_length = len(mask)/2
mask_complexity = self.getcomplexity(mask)
mask_time = mask_complexity/self.pps
self.total_occurrence += mask_occurrence
# Apply filters based on occurrence, length, complexity and time
if (not self.minoccurrence or mask_occurrence >= self.minoccurrence) and \
(not self.maxcomplexity or mask_complexity <= self.maxcomplexity) and \
(not self.maxlength or mask_length <= self.maxlength) and \
(not self.minlength or mask_length >= self.minlength):
if (self.minoccurrence == None or mask_occurrence >= self.minoccurrence) and \
(self.maxoccurrence == None or mask_occurrence <= self.maxoccurrence) and \
(self.mincomplexity == None or mask_complexity <= self.mincomplexity) and \
(self.maxcomplexity == None or mask_complexity <= self.maxcomplexity) and \
(self.mintime == None or mask_time <= self.mintime) and \
(self.maxtime == None or mask_time <= self.maxtime) and \
(self.maxlength == None or mask_length <= self.maxlength) and \
(self.minlength == None or mask_length >= self.minlength):
self.masks[mask] = dict()
self.masks[mask]['length'] = mask_length
self.masks[mask]['occurrence'] = mask_occurrence
self.masks[mask]['complexity'] = 1 - mask_complexity
self.masks[mask]['time'] = mask_complexity/self.pps
self.masks[mask]['time'] = mask_time
self.masks[mask]['optindex'] = 1 - mask_complexity/mask_occurrence
def generate_masks(self,sorting_mode):
@ -132,13 +140,18 @@ class MaskGen:
print " Masks runtime: %s" % time_human
def getmaskscoverage(self, checkmasks):
sample_count = 0
sample_time = 0
sample_occurrence = 0
total_complexity = 0
if self.showmasks: print "[L:] Mask: [ Occ: ] [ Time: ]"
for mask in checkmasks:
mask = mask.strip()
mask_complexity = self.getcomplexity(mask)
total_complexity += mask_complexity
if mask in self.masks:
@ -150,17 +163,18 @@ class MaskGen:
self.output_file.write("%s\n" % mask)
sample_occurrence += self.masks[mask]['occurrence']
sample_time += self.masks[mask]['time']
sample_count += 1
if self.target_time and sample_time > self.target_time:
print "[!] Target time exceeded."
break
if self.target_time and total_complexity/self.pps > self.target_time:
print "[!] Target time exceeded."
break
# TODO: Something wrong here, complexity and time doesn't match with estimated from policygen
total_time = total_complexity/self.pps
time_human = ">1 year" if total_time > 60*60*24*365 else str(datetime.timedelta(seconds=total_time))
print "[*] Finished matching masks:"
print " Masks matched: %s" % sample_count
print " Masks coverage: %d%% (%d/%d)" % (sample_occurrence*100/self.total_occurrence,sample_occurrence,self.total_occurrence)
time_human = ">1 year" if sample_time > 60*60*24*365 else str(datetime.timedelta(seconds=sample_time))
print " Masks runtime: %s" % time_human
@ -182,11 +196,14 @@ if __name__ == "__main__":
parser.add_option("-o", "--outputmasks", dest="output_masks", metavar="masks.hcmask", help="Save masks to a file")
filters = OptionGroup(parser, "Individual Mask Filter Options")
filters.add_option("--minlength", dest="minlength", type="int", metavar="8", help="Minimum password length")
filters.add_option("--maxlength", dest="maxlength", type="int", metavar="8", help="Maximum password length")
filters.add_option("--maxtime", dest="maxtime", type="int", metavar="3600", help="Maximum runtime (seconds)")
filters.add_option("--maxcomplexity", dest="maxcomplexity", type="int", metavar="", help="Maximum complexity")
filters.add_option("--minoccurrence", dest="minoccurrence", type="int", metavar="", help="Minimum occurrence")
filters.add_option("--minlength", dest="minlength", type="int", metavar="8", help="Minimum password length")
filters.add_option("--maxlength", dest="maxlength", type="int", metavar="8", help="Maximum password length")
filters.add_option("--mintime", dest="mintime", type="int", metavar="3600", help="Minimum mask runtime (seconds)")
filters.add_option("--maxtime", dest="maxtime", type="int", metavar="3600", help="Maximum mask runtime (seconds)")
filters.add_option("--mincomplexity", dest="mincomplexity", type="int", metavar="1", help="Minimum complexity")
filters.add_option("--maxcomplexity", dest="maxcomplexity", type="int", metavar="100", help="Maximum complexity")
filters.add_option("--minoccurrence", dest="minoccurrence", type="int", metavar="1", help="Minimum occurrence")
filters.add_option("--maxoccurrence", dest="maxoccurrence", type="int", metavar="100", help="Maximum occurrence")
parser.add_option_group(filters)
sorting = OptionGroup(parser, "Mask Sorting Options")
@ -228,15 +245,20 @@ if __name__ == "__main__":
maskgen.output_file = open(options.output_masks, 'w')
# Filters
if options.minlength: maskgen.minlength = options.minlength
if options.maxlength: maskgen.maxlength = options.maxlength
if options.maxtime: maskgen.maxtime = options.maxtime
if options.minlength: maskgen.minlength = options.minlength
if options.maxlength: maskgen.maxlength = options.maxlength
if options.mintime: maskgen.mintime = options.mintime
if options.maxtime: maskgen.maxtime = options.maxtime
if options.mincomplexity: maskgen.mincomplexity = options.mincomplexity
if options.maxcomplexity: maskgen.maxcomplexity = options.maxcomplexity
if options.minoccurrence: maskgen.minoccurrence = options.minoccurrence
if options.maxoccurrence: maskgen.maxoccurrence = options.maxoccurrence
# Misc
if options.pps: maskgen.pps = options.pps
if options.showmasks: maskgen.showmasks = options.showmasks
print "[*] Using {:,d} keys/sec for calculations.".format(maskgen.pps)
# Load masks
for arg in args:

View File

@ -35,13 +35,6 @@ import itertools
VERSION = "0.0.2"
# PPS (Passwords per Second) Cracking Speed
pps = 1000000000
# Global Variables
sample_time = 0
total_time = 0
class PolicyGen:
def __init__(self):
self.output_file = None
@ -57,7 +50,6 @@ class PolicyGen:
self.maxupper = None
self.maxspecial = None
# PPS (Passwords per Second) Cracking Speed
self.pps = 1000000000
self.showmasks = False
@ -82,7 +74,7 @@ class PolicyGen:
sample_count = 0
# NOTE: It is better to collect total complexity
# in order not to lose precision when dividing by pps
# not to lose precision when dividing by pps
total_complexity = 0
sample_complexity = 0
@ -92,6 +84,7 @@ class PolicyGen:
total_length_count = 0
sample_length_count = 0
total_length_complexity = 0
sample_length_complexity = 0
@ -177,11 +170,11 @@ if __name__ == "__main__":
group = OptionGroup(parser, "Password Policy", "Define the minimum (or maximum) password strength policy that you would like to test")
group.add_option("--minlength", dest="minlength", type="int", metavar="8", default=8, help="Minimum password length")
group.add_option("--maxlength", dest="maxlength", type="int", metavar="8", default=8, help="Maximum password length")
group.add_option("--mindigit", dest="mindigit", type="int", metavar="1", help="Minimum number of digits")
group.add_option("--mindigit", dest="mindigit", type="int", metavar="1", help="Minimum number of digits")
group.add_option("--minlower", dest="minlower", type="int", metavar="1", help="Minimum number of lower-case characters")
group.add_option("--minupper", dest="minupper", type="int", metavar="1", help="Minimum number of upper-case characters")
group.add_option("--minspecial",dest="minspecial",type="int", metavar="1", help="Minimum number of special characters")
group.add_option("--maxdigit", dest="maxdigit", type="int", metavar="3", help="Maximum number of digits")
group.add_option("--maxdigit", dest="maxdigit", type="int", metavar="3", help="Maximum number of digits")
group.add_option("--maxlower", dest="maxlower", type="int", metavar="3", help="Maximum number of lower-case characters")
group.add_option("--maxupper", dest="maxupper", type="int", metavar="3", help="Maximum number of upper-case characters")
group.add_option("--maxspecial",dest="maxspecial",type="int", metavar="3", help="Maximum number of special characters")
@ -202,6 +195,7 @@ if __name__ == "__main__":
print "[*] Saving generated masks to [%s]" % options.output_masks
policygen.output_file = open(options.output_masks, 'w')
# Password policy
if options.minlength != None: policygen.minlength = options.minlength
if options.maxlength != None: policygen.maxlength = options.maxlength
@ -218,6 +212,8 @@ if __name__ == "__main__":
if options.pps: policygen.pps = options.pps
if options.showmasks: policygen.showmasks = options.showmasks
print "[*] Using {:,d} keys/sec for calculations.".format(policygen.pps)
# Print current password policy
print "[*] Password policy:"
print " Pass Lengths: min:%d max:%d" % (policygen.minlength, policygen.maxlength)

View File

@ -5,7 +5,7 @@
#
# This tool is part of PACK (Password Analysis and Cracking Kit)
#
# VERSION 0.0.1
# VERSION 0.0.2
#
# Copyright (C) 2013 Peter Kacherginsky
# All rights reserved.
@ -44,8 +44,7 @@ from optparse import OptionParser, OptionGroup
VERSION = "0.0.1"
# Testing rules with hashcat --stdout
import subprocess
HASHCAT_PATH = "hashcat-0.42/"
HASHCAT_PATH = "hashcat/"
# Rule Generator class responsible for the complete cycle of rule generation
class RuleGen:
@ -713,7 +712,6 @@ class RuleGen:
if self.verbose: print "[*] Analyzing password: %s" % password
if self.verbose: start_time = time.clock()
# Skip all numeric passwords
if password.isdigit():
if self.verbose: print "[!] %s => {skipping numeric} => %s" % (password,password)
@ -836,7 +834,7 @@ class RuleGen:
if __name__ == "__main__":
header = " _ \n"
header += " RuleGen 0.0.1 | |\n"
header += " RuleGen %s | |\n" % VERSION
header += " _ __ __ _ ___| | _\n"
header += " | '_ \ / _` |/ __| |/ /\n"
header += " | |_) | (_| | (__| < \n"

View File

@ -30,79 +30,157 @@
import sys
import re, operator, string
from optparse import OptionParser
from optparse import OptionParser, OptionGroup
VERSION = "0.0.2"
password_counter = 0
class StatsGen:
def __init__(self):
self.output_file = None
# Constants
chars_regex = list()
chars_regex.append(('numeric',re.compile('^[0-9]+$')))
chars_regex.append(('loweralpha',re.compile('^[a-z]+$')))
chars_regex.append(('upperalpha',re.compile('^[A-Z]+$')))
chars_regex.append(('mixedalpha',re.compile('^[a-zA-Z]+$')))
chars_regex.append(('loweralphanum',re.compile('^[a-z0-9]+$')))
chars_regex.append(('upperalphanum',re.compile('^[A-Z0-9]+$')))
chars_regex.append(('mixedalphanum',re.compile('^[a-zA-Z0-9]+$')))
chars_regex.append(('special',re.compile('^[^a-zA-Z0-9]+$')))
chars_regex.append(('loweralphaspecial',re.compile('^[^A-Z0-9]+$')))
chars_regex.append(('upperalphaspecial',re.compile('^[^a-z0-9]+$')))
chars_regex.append(('mixedalphaspecial',re.compile('^[^0-9]+$')))
chars_regex.append(('loweralphaspecialnum',re.compile('^[^A-Z]+$')))
chars_regex.append(('upperalphaspecialnum',re.compile('^[^a-z]+$')))
chars_regex.append(('mixedalphaspecialnum',re.compile('.*')))
# Filters
self.minlength = None
self.maxlength = None
self.simplemasks = None
self.charsets = None
masks_regex = list()
masks_regex.append(('alldigit',re.compile('^\d+$', re.IGNORECASE)))
masks_regex.append(('allstring',re.compile('^[a-z]+$', re.IGNORECASE)))
masks_regex.append(('stringdigit',re.compile('^[a-z]+\d+$', re.IGNORECASE)))
masks_regex.append(('digitstring',re.compile('^\d+[a-z]+$', re.IGNORECASE)))
masks_regex.append(('digitstringdigit',re.compile('^\d+[a-z]+\d+$', re.IGNORECASE)))
masks_regex.append(('stringdigitstring',re.compile('^[a-z]+\d+[a-z]+$', re.IGNORECASE)))
masks_regex.append(('allspecial',re.compile('^[^a-z0-9]+$', re.IGNORECASE)))
masks_regex.append(('stringspecial',re.compile('^[a-z]+[^a-z0-9]+$', re.IGNORECASE)))
masks_regex.append(('specialstring',re.compile('^[^a-z0-9]+[a-z]+$', re.IGNORECASE)))
masks_regex.append(('stringspecialstring',re.compile('^[a-z]+[^a-z0-9]+[a-z]+$', re.IGNORECASE)))
masks_regex.append(('stringspecialdigit',re.compile('^[a-z]+[^a-z0-9]+\d+$', re.IGNORECASE)))
masks_regex.append(('specialstringspecial',re.compile('^[^a-z0-9]+[a-z]+[^a-z0-9]+$', re.IGNORECASE)))
# Constants
self.chars_regex = list()
self.chars_regex.append(('numeric',re.compile('^[0-9]+$')))
self.chars_regex.append(('loweralpha',re.compile('^[a-z]+$')))
self.chars_regex.append(('upperalpha',re.compile('^[A-Z]+$')))
self.chars_regex.append(('mixedalpha',re.compile('^[a-zA-Z]+$')))
self.chars_regex.append(('loweralphanum',re.compile('^[a-z0-9]+$')))
self.chars_regex.append(('upperalphanum',re.compile('^[A-Z0-9]+$')))
self.chars_regex.append(('mixedalphanum',re.compile('^[a-zA-Z0-9]+$')))
self.chars_regex.append(('special',re.compile('^[^a-zA-Z0-9]+$')))
self.chars_regex.append(('loweralphaspecial',re.compile('^[^A-Z0-9]+$')))
self.chars_regex.append(('upperalphaspecial',re.compile('^[^a-z0-9]+$')))
self.chars_regex.append(('mixedalphaspecial',re.compile('^[^0-9]+$')))
self.chars_regex.append(('loweralphaspecialnum',re.compile('^[^A-Z]+$')))
self.chars_regex.append(('upperalphaspecialnum',re.compile('^[^a-z]+$')))
self.chars_regex.append(('mixedalphaspecialnum',re.compile('.*')))
def length_check(password):
return len(password)
self.masks_regex = list()
self.masks_regex.append(('alldigit',re.compile('^\d+$', re.IGNORECASE)))
self.masks_regex.append(('allstring',re.compile('^[a-z]+$', re.IGNORECASE)))
self.masks_regex.append(('stringdigit',re.compile('^[a-z]+\d+$', re.IGNORECASE)))
self.masks_regex.append(('digitstring',re.compile('^\d+[a-z]+$', re.IGNORECASE)))
self.masks_regex.append(('digitstringdigit',re.compile('^\d+[a-z]+\d+$', re.IGNORECASE)))
self.masks_regex.append(('stringdigitstring',re.compile('^[a-z]+\d+[a-z]+$', re.IGNORECASE)))
self.masks_regex.append(('allspecial',re.compile('^[^a-z0-9]+$', re.IGNORECASE)))
self.masks_regex.append(('stringspecial',re.compile('^[a-z]+[^a-z0-9]+$', re.IGNORECASE)))
self.masks_regex.append(('specialstring',re.compile('^[^a-z0-9]+[a-z]+$', re.IGNORECASE)))
self.masks_regex.append(('stringspecialstring',re.compile('^[a-z]+[^a-z0-9]+[a-z]+$', re.IGNORECASE)))
self.masks_regex.append(('stringspecialdigit',re.compile('^[a-z]+[^a-z0-9]+\d+$', re.IGNORECASE)))
self.masks_regex.append(('specialstringspecial',re.compile('^[^a-z0-9]+[a-z]+[^a-z0-9]+$', re.IGNORECASE)))
def masks_check(password):
for (name,regex) in masks_regex:
if regex.match(password):
return name
else:
return "othermask"
# Stats dictionaries
self.stats_length = dict()
self.stats_simplemasks = dict()
self.stats_advancedmasks = dict()
self.stats_charactersets = dict()
def chars_check(password):
for (name,regex) in chars_regex:
if regex.match(password):
return name
else:
return "otherchar"
self.hiderare = False
def advmask_check(password):
advmask = list()
for letter in password:
if letter in string.digits: advmask.append("?d")
elif letter in string.lowercase: advmask.append("?l")
elif letter in string.uppercase: advmask.append("?u")
else: advmask.append("?s")
return "".join(advmask)
self.filter_counter = 0
self.total_counter = 0
def main():
password_length = dict()
masks = dict()
advmasks = dict()
chars = dict()
filter_counter = 0
total_counter = 0
def simplemasks_check(self, password):
for (name,regex) in self.masks_regex:
if regex.match(password):
return name
else:
return "othermask"
def characterset_check(self, password):
for (name,regex) in self.chars_regex:
if regex.match(password):
return name
else:
return "otherchar"
def advancedmask_check(self, password):
mask = list()
for letter in password:
if letter in string.digits: mask.append("?d")
elif letter in string.lowercase: mask.append("?l")
elif letter in string.uppercase: mask.append("?u")
else: mask.append("?s")
return "".join(mask)
def generate_stats(self, filename):
f = open(filename,'r')
for password in f:
password = password.rstrip('\r\n')
self.total_counter += 1
pass_length = len(password)
characterset = self.characterset_check(password)
simplemask = self.simplemasks_check(password)
advancedmask = self.advancedmask_check(password)
if (self.charsets == None or characterset in self.charsets) and \
(self.simplemasks == None or simplemask in self.simplemasks) and \
(self.maxlength == None or pass_length <= self.maxlength) and \
(self.minlength == None or pass_length >= self.minlength):
self.filter_counter += 1
if pass_length in self.stats_length:
self.stats_length[pass_length] += 1
else:
self.stats_length[pass_length] = 1
if characterset in self.stats_charactersets:
self.stats_charactersets[characterset] += 1
else:
self.stats_charactersets[characterset] = 1
if simplemask in self.stats_simplemasks:
self.stats_simplemasks[simplemask] += 1
else:
self.stats_simplemasks[simplemask] = 1
if advancedmask in self.stats_advancedmasks:
self.stats_advancedmasks[advancedmask] += 1
else:
self.stats_advancedmasks[advancedmask] = 1
f.close()
def print_stats(self):
print "[+] Analyzing %d%% (%d/%d) of passwords" % (self.filter_counter*100/self.total_counter, self.filter_counter, self.total_counter)
print " NOTE: Statistics below is relative to the number of analyzed passwords, not total number of passwords"
print "\n[*] Line Count Statistics..."
for (length,count) in sorted(self.stats_length.iteritems(), key=operator.itemgetter(1), reverse=True):
if self.hiderare and not count*100/self.filter_counter > 0: continue
print "[+] %25d: %02d%% (%d)" % (length, count*100/self.filter_counter, count)
print "\n[*] Charset statistics..."
for (char,count) in sorted(self.stats_charactersets.iteritems(), key=operator.itemgetter(1), reverse=True):
if self.hiderare and not count*100/self.filter_counter > 0: continue
print "[+] %25s: %02d%% (%d)" % (char, count*100/self.filter_counter, count)
print "\n[*] Simple Mask statistics..."
for (simplemask,count) in sorted(self.stats_simplemasks.iteritems(), key=operator.itemgetter(1), reverse=True):
if self.hiderare and not count*100/self.filter_counter > 0: continue
print "[+] %25s: %02d%% (%d)" % (simplemask, count*100/self.filter_counter, count)
print "\n[*] Advanced Mask statistics..."
for (advancedmask,count) in sorted(self.stats_advancedmasks.iteritems(), key=operator.itemgetter(1), reverse=True):
if count*100/self.filter_counter > 0:
print "[+] %25s: %02d%% (%d)" % (advancedmask, count*100/self.filter_counter, count)
if self.output_file:
self.output_file.write("%s,%d\n" % (advancedmask,count))
if __name__ == "__main__":
header = " _ \n"
header += " StatsGen 0.0.2 | |\n"
header += " StatsGen %s | |\n" % VERSION
header += " _ __ __ _ ___| | _\n"
header += " | '_ \ / _` |/ __| |/ /\n"
header += " | |_) | (_| | (__| < \n"
@ -111,11 +189,18 @@ def main():
header += " |_| iphelix@thesprawl.org\n"
header += "\n"
parser = OptionParser("%prog [options] passwords.txt", version="%prog "+VERSION)
parser.add_option("-l", "--length", dest="length_filter",help="Password length filter.",metavar="8")
parser.add_option("-c", "--charset", dest="char_filter", help="Password charset filter.", metavar="loweralpha")
parser.add_option("-m", "--mask", dest="mask_filter",help="Password mask filter", metavar="stringdigit")
parser.add_option("-o", "--masksoutput", dest="mask_output",help="Generate and save masks db to a file", metavar="masks.csv")
parser = OptionParser("%prog [options] passwords.txt\n\nType --help for more options", version="%prog "+VERSION)
filters = OptionGroup(parser, "Password Filters")
filters.add_option("--minlength", dest="minlength", type="int", metavar="8", help="Minimum password length")
filters.add_option("--maxlength", dest="maxlength", type="int", metavar="8", help="Maximum password length")
filters.add_option("--charset", dest="charsets", help="Password charset filter (comma separated)", metavar="loweralpha,numeric")
filters.add_option("--simplemask", dest="simplemasks",help="Password mask filter (comma separated)", metavar="stringdigit,allspecial")
parser.add_option_group(filters)
parser.add_option("-o", "--output", dest="output_file",help="Save masks and stats to a file", metavar="password.masks")
parser.add_option("--hiderare", action="store_true", dest="hiderare", default=False, help="Hide statistics covering less than 1% of the sample")
parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Don't show headers.")
(options, args) = parser.parse_args()
@ -126,66 +211,21 @@ def main():
if len(args) != 1:
parser.error("no passwords file specified")
exit(1)
print "[*] Analyzing passwords: %s" % args[0]
f = open(args[0],'r')
print "[*] Analyzing passwords in [%s]" % args[0]
for password in f:
password = password.strip()
total_counter += 1
pass_len = length_check(password)
mask_set = masks_check(password)
char_set = chars_check(password)
advmask = advmask_check(password)
statsgen = StatsGen()
if (not options.length_filter or str(pass_len) in options.length_filter.split(',')) and \
(not options.char_filter or char_set in options.char_filter.split(',')) and \
(not options.mask_filter or mask_set in options.mask_filter.split(',')):
filter_counter += 1
if not options.minlength == None: statsgen.minlength = options.minlength
if not options.maxlength == None: statsgen.maxlength = options.maxlength
if not options.charsets == None: statsgen.charsets = [x.strip() for x in options.charsets.split(',')]
if not options.simplemasks == None: statsgen.simplemasks = [x.strip() for x in options.simplemasks.split(',')]
try: password_length[pass_len] += 1
except: password_length[pass_len] = 1
if options.hiderare: statsgen.hiderare = options.hiderare
try: masks[mask_set] += 1
except: masks[mask_set] = 1
if options.output_file:
print "[*] Saving advanced masks and occurrences to [%s]" % options.output_file
statsgen.output_file = open(options.output_file, 'w')
try: chars[char_set] += 1
except: chars[char_set] = 1
try: advmasks[advmask] += 1
except: advmasks[advmask] = 1
f.close()
print "[+] Analyzing %d%% (%d/%d) passwords" % (filter_counter*100/total_counter, filter_counter, total_counter)
print " NOTE: Statistics below is relative to the number of analyzed passwords, not total number of passwords"
print "\n[*] Line Count Statistics..."
for (length,count) in sorted(password_length.iteritems(), key=operator.itemgetter(1), reverse=True):
if count*100/filter_counter > 0:
print "[+] %25d: %02d%% (%d)" % (length, count*100/filter_counter, count)
print "\n[*] Charset statistics..."
for (char,count) in sorted(chars.iteritems(), key=operator.itemgetter(1), reverse=True):
print "[+] %25s: %02d%% (%d)" % (char, count*100/filter_counter, count)
print "\n[*] Mask statistics..."
for (mask,count) in sorted(masks.iteritems(), key=operator.itemgetter(1), reverse=True):
print "[+] %25s: %02d%% (%d)" % (mask, count*100/filter_counter, count)
print "\n[*] Advanced Mask statistics..."
for (advmask,count) in sorted(advmasks.iteritems(), key=operator.itemgetter(1), reverse=True):
if count*100/filter_counter > 0:
print "[+] %25s: %02d%% (%d)" % (advmask, count*100/filter_counter, count)
if options.mask_output:
print "\n[*] Saving Mask statistics to %s" % options.mask_output
fmask = open(options.mask_output, "w")
for (advmask,count) in sorted(advmasks.iteritems(), key=operator.itemgetter(1), reverse=True):
fmask.write("%s,%d\n" % (advmask,count))
fmask.close()
if __name__ == "__main__":
main()
statsgen.generate_stats(args[0])
statsgen.print_stats()