findSymbols: added option to colorize output.
This commit is contained in:
parent
ad18cf327a
commit
c9681b2ae7
|
@ -31,6 +31,7 @@ optional arguments:
|
||||||
Extensions of files to scan. By default will scan all files. Can be repeated: -E exe -E dll
|
Extensions of files to scan. By default will scan all files. Can be repeated: -E exe -E dll
|
||||||
-o PATH, --output PATH
|
-o PATH, --output PATH
|
||||||
Write output to file.
|
Write output to file.
|
||||||
|
-C, --color Add colors to text output. May uglify table text output
|
||||||
|
|
||||||
Output sorting:
|
Output sorting:
|
||||||
-u, --unique Return unique symbols only. The first symbol with a name that occurs in results, will be returned.
|
-u, --unique Return unique symbols only. The first symbol with a name that occurs in results, will be returned.
|
||||||
|
@ -42,7 +43,7 @@ Output sorting:
|
||||||
Output filtering:
|
Output filtering:
|
||||||
-i, --imports Filter only Imports.
|
-i, --imports Filter only Imports.
|
||||||
-e, --exports Filter only Exports.
|
-e, --exports Filter only Exports.
|
||||||
-s NAME, --name NAME Search for symbols with name matching this regular expression. Can be repeated, case insensitive, constructs: ".+VALUE.+"
|
-s NAME, --name NAME Search for symbols with name matching this regular expression. Can be repeated, case insensitive
|
||||||
-S NOT_NAME, --not-name NOT_NAME
|
-S NOT_NAME, --not-name NOT_NAME
|
||||||
Search for symbols with name NOT matching this regular expression.
|
Search for symbols with name NOT matching this regular expression.
|
||||||
-m MODULE, --module MODULE
|
-m MODULE, --module MODULE
|
||||||
|
|
|
@ -48,6 +48,41 @@ headers = [
|
||||||
|
|
||||||
symbol_idx = headers.index('symbol')
|
symbol_idx = headers.index('symbol')
|
||||||
|
|
||||||
|
class Logger:
|
||||||
|
colors_map = {
|
||||||
|
'red': 31,
|
||||||
|
'green': 32,
|
||||||
|
'yellow': 33,
|
||||||
|
'blue': 34,
|
||||||
|
'magenta': 35,
|
||||||
|
'cyan': 36,
|
||||||
|
'white': 37,
|
||||||
|
'grey': 38,
|
||||||
|
}
|
||||||
|
|
||||||
|
colors_dict = {
|
||||||
|
'error': colors_map['red'],
|
||||||
|
'trace': colors_map['magenta'],
|
||||||
|
'info ': colors_map['green'],
|
||||||
|
'debug': colors_map['grey'],
|
||||||
|
'other': colors_map['grey'],
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def with_color(c, s):
|
||||||
|
return "\x1b[%dm%s\x1b[0m" % (c, s)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def end_color(s):
|
||||||
|
return "%s\x1b[0m" % (s)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def colored(args, txt, col):
|
||||||
|
if not args.color:
|
||||||
|
return txt
|
||||||
|
|
||||||
|
return Logger.with_color(Logger.colors_map[col], txt)
|
||||||
|
|
||||||
def out(x):
|
def out(x):
|
||||||
sys.stderr.write(x + '\n')
|
sys.stderr.write(x + '\n')
|
||||||
|
|
||||||
|
@ -107,18 +142,35 @@ def verifyCriterias(args, regexes, infos, uniqueSymbols):
|
||||||
|
|
||||||
regexesVerified = sum([len(v) for k, v in regexes.items()])
|
regexesVerified = sum([len(v) for k, v in regexes.items()])
|
||||||
|
|
||||||
|
regexes_name = len(regexes['name'])
|
||||||
|
regexes_not_name = len(regexes['not-name'])
|
||||||
|
regexes_module = len(regexes['module'])
|
||||||
|
regexes_not_module = len(regexes['not-module'])
|
||||||
|
|
||||||
for name, rex in regexes['not-name']:
|
for name, rex in regexes['not-name']:
|
||||||
match = rex.search(infos['symbol'])
|
match = rex.search(infos['symbol'])
|
||||||
if match:
|
if match:
|
||||||
|
matched = match.group(1)
|
||||||
|
infos['symbol'] = infos['symbol'].replace(matched, Logger.colored(args, matched, 'red'))
|
||||||
verbose(args, f'(-) Skipping symbol {infos["module"]}.{infos["symbol"]} as it DID satisfy not-name ({name}) regex.')
|
verbose(args, f'(-) Skipping symbol {infos["module"]}.{infos["symbol"]} as it DID satisfy not-name ({name}) regex.')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if regexes_not_module+regexes_module+regexes_name == 0:
|
||||||
|
verbose(args, f'(+) Symbol {infos["module"]}.{infos["symbol"]} satisfied all criterias.')
|
||||||
|
return True
|
||||||
|
|
||||||
for name, rex in regexes['not-module']:
|
for name, rex in regexes['not-module']:
|
||||||
match = rex.search(infos['module'])
|
match = rex.search(infos['module'])
|
||||||
if match:
|
if match:
|
||||||
|
matched = match.group(1)
|
||||||
|
infos['module'] = infos['module'].replace(matched, Logger.colored(args, matched, 'red'))
|
||||||
verbose(args, f'(-) Skipping symbol\'s module {infos["module"]}.{infos["symbol"]} as it DID satisfy not-module ({name}) regex.')
|
verbose(args, f'(-) Skipping symbol\'s module {infos["module"]}.{infos["symbol"]} as it DID satisfy not-module ({name}) regex.')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if regexes_module+regexes_name == 0:
|
||||||
|
verbose(args, f'(+) Symbol {infos["module"]}.{infos["symbol"]} satisfied all criterias.')
|
||||||
|
return True
|
||||||
|
|
||||||
satisifed = False
|
satisifed = False
|
||||||
carryOn = False
|
carryOn = False
|
||||||
|
|
||||||
|
@ -126,16 +178,24 @@ def verifyCriterias(args, regexes, infos, uniqueSymbols):
|
||||||
for name, rex in regexes['module']:
|
for name, rex in regexes['module']:
|
||||||
match = rex.search(infos['module'])
|
match = rex.search(infos['module'])
|
||||||
if match:
|
if match:
|
||||||
|
matched = match.group(1)
|
||||||
|
infos['module'] = infos['module'].replace(matched, Logger.colored(args, matched, 'green'))
|
||||||
verbose(args, f'(+) Symbol\'s module {infos["module"]}.{infos["symbol"]} satisfied module ({name}) regex.')
|
verbose(args, f'(+) Symbol\'s module {infos["module"]}.{infos["symbol"]} satisfied module ({name}) regex.')
|
||||||
carryOn = True
|
carryOn = True
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
carryOn = True
|
carryOn = True
|
||||||
|
|
||||||
|
if regexes_name == 0:
|
||||||
|
verbose(args, f'(+) Symbol {infos["module"]}.{infos["symbol"]} satisfied all criterias.')
|
||||||
|
return True
|
||||||
|
|
||||||
if carryOn:
|
if carryOn:
|
||||||
for name, rex in regexes['name']:
|
for name, rex in regexes['name']:
|
||||||
match = rex.search(infos['symbol'])
|
match = rex.search(infos['symbol'])
|
||||||
if match:
|
if match:
|
||||||
|
matched = match.group(1)
|
||||||
|
infos['symbol'] = infos['symbol'].replace(matched, Logger.colored(args, matched, 'green'))
|
||||||
verbose(args, f'(+) Symbol {infos["module"]}.{infos["symbol"]} satisfied name ({name}) regex.')
|
verbose(args, f'(+) Symbol {infos["module"]}.{infos["symbol"]} satisfied name ({name}) regex.')
|
||||||
satisifed = True
|
satisifed = True
|
||||||
break
|
break
|
||||||
|
@ -195,9 +255,15 @@ def processFile(args, regexes, path, results, uniqueSymbols, filesProcessed, sym
|
||||||
if args.format == 'text':
|
if args.format == 'text':
|
||||||
appendRow = verifyCriterias(args, regexes, infos, uniqueSymbols)
|
appendRow = verifyCriterias(args, regexes, infos, uniqueSymbols)
|
||||||
|
|
||||||
|
if args.color:
|
||||||
|
if infos['symbol type'] == 'import':
|
||||||
|
infos['symbol type'] = Logger.colored(args, infos['symbol type'], 'cyan')
|
||||||
|
else:
|
||||||
|
infos['symbol type'] = Logger.colored(args, infos['symbol type'], 'yellow')
|
||||||
|
|
||||||
if appendRow:
|
if appendRow:
|
||||||
row = []
|
row = []
|
||||||
MaxWidth = 80
|
MaxWidth = 40
|
||||||
|
|
||||||
for h in headers:
|
for h in headers:
|
||||||
obj = None
|
obj = None
|
||||||
|
@ -208,6 +274,9 @@ def processFile(args, regexes, path, results, uniqueSymbols, filesProcessed, sym
|
||||||
obj = infos[h]
|
obj = infos[h]
|
||||||
|
|
||||||
if type(obj) == str and len(obj) > MaxWidth:
|
if type(obj) == str and len(obj) > MaxWidth:
|
||||||
|
if h == 'path':
|
||||||
|
obj = '\n'.join(textwrap.wrap(obj, width = 2 * MaxWidth))
|
||||||
|
else:
|
||||||
obj = '\n'.join(textwrap.wrap(obj, width = MaxWidth))
|
obj = '\n'.join(textwrap.wrap(obj, width = MaxWidth))
|
||||||
|
|
||||||
row.append(obj)
|
row.append(obj)
|
||||||
|
@ -223,6 +292,12 @@ def processFile(args, regexes, path, results, uniqueSymbols, filesProcessed, sym
|
||||||
elif args.format == 'json':
|
elif args.format == 'json':
|
||||||
appendRow = verifyCriterias(args, regexes, infos, uniqueSymbols)
|
appendRow = verifyCriterias(args, regexes, infos, uniqueSymbols)
|
||||||
|
|
||||||
|
if args.color:
|
||||||
|
if infos['symbol type'] == 'import':
|
||||||
|
infos['symbol type'] = Logger.colored(args, infos['symbol type'], 'cyan')
|
||||||
|
else:
|
||||||
|
infos['symbol type'] = Logger.colored(args, infos['symbol type'], 'yellow')
|
||||||
|
|
||||||
if appendRow:
|
if appendRow:
|
||||||
results.append(infos)
|
results.append(infos)
|
||||||
uniqueSymbols.append(symbolName)
|
uniqueSymbols.append(symbolName)
|
||||||
|
@ -236,7 +311,7 @@ def processFile(args, regexes, path, results, uniqueSymbols, filesProcessed, sym
|
||||||
symbolsProcessed.value += len(symbols)
|
symbolsProcessed.value += len(symbols)
|
||||||
|
|
||||||
def trap_handler(signum, frame):
|
def trap_handler(signum, frame):
|
||||||
out('[-] CTRL-C pressed. Wait a minute until all processes wrap up.')
|
out('[-] CTRL-C pressed. Wait a minute until all processes wrap up or manually terminate python\'s child processes tree.')
|
||||||
|
|
||||||
def init_worker():
|
def init_worker():
|
||||||
signal.signal(signal.SIGINT, trap_handler)
|
signal.signal(signal.SIGINT, trap_handler)
|
||||||
|
@ -300,6 +375,7 @@ def opts(argv):
|
||||||
params.add_argument('-f', '--format', choices=['text', 'json'], default='text', help='Output format. Text or JSON.')
|
params.add_argument('-f', '--format', choices=['text', 'json'], default='text', help='Output format. Text or JSON.')
|
||||||
params.add_argument('-E', '--extension', default=[], action='append', help='Extensions of files to scan. By default will scan all files. Can be repeated: -E exe -E dll')
|
params.add_argument('-E', '--extension', default=[], action='append', help='Extensions of files to scan. By default will scan all files. Can be repeated: -E exe -E dll')
|
||||||
params.add_argument('-o', '--output', metavar='PATH', help='Write output to file.')
|
params.add_argument('-o', '--output', metavar='PATH', help='Write output to file.')
|
||||||
|
params.add_argument('-C', '--color', default=False, action='store_true', help='Add colors to text output. May uglify table text output')
|
||||||
|
|
||||||
sorting = params.add_argument_group('Output sorting')
|
sorting = params.add_argument_group('Output sorting')
|
||||||
sorting.add_argument('-u', '--unique', action='store_true', help = 'Return unique symbols only. The first symbol with a name that occurs in results, will be returned.')
|
sorting.add_argument('-u', '--unique', action='store_true', help = 'Return unique symbols only. The first symbol with a name that occurs in results, will be returned.')
|
||||||
|
@ -310,7 +386,7 @@ def opts(argv):
|
||||||
filters = params.add_argument_group('Output filtering')
|
filters = params.add_argument_group('Output filtering')
|
||||||
filters.add_argument('-i', '--imports', action='store_true', help = 'Filter only Imports.')
|
filters.add_argument('-i', '--imports', action='store_true', help = 'Filter only Imports.')
|
||||||
filters.add_argument('-e', '--exports', action='store_true', help = 'Filter only Exports.')
|
filters.add_argument('-e', '--exports', action='store_true', help = 'Filter only Exports.')
|
||||||
filters.add_argument('-s', '--name', action='append', default=[], help = 'Search for symbols with name matching this regular expression. Can be repeated, case insensitive, constructs: ".+VALUE.+"')
|
filters.add_argument('-s', '--name', action='append', default=[], help = 'Search for symbols with name matching this regular expression. Can be repeated, case insensitive')
|
||||||
filters.add_argument('-S', '--not-name', action='append', default=[], help = 'Search for symbols with name NOT matching this regular expression.')
|
filters.add_argument('-S', '--not-name', action='append', default=[], help = 'Search for symbols with name NOT matching this regular expression.')
|
||||||
filters.add_argument('-m', '--module', action='append', default=[], help = 'Search for symbols exported in/imported from this module matching regular expression.')
|
filters.add_argument('-m', '--module', action='append', default=[], help = 'Search for symbols exported in/imported from this module matching regular expression.')
|
||||||
filters.add_argument('-M', '--not-module', action='append', default=[], help = 'Search for symbols NOT exported in/NOT imported from this module matching regular expression.')
|
filters.add_argument('-M', '--not-module', action='append', default=[], help = 'Search for symbols NOT exported in/NOT imported from this module matching regular expression.')
|
||||||
|
@ -321,7 +397,7 @@ def opts(argv):
|
||||||
out('[!] --imports and --exports are mutually exclusive. Pick only one of them!')
|
out('[!] --imports and --exports are mutually exclusive. Pick only one of them!')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
accomodate_rex = lambda x: x
|
accomodate_rex = lambda x: f'({x})'
|
||||||
|
|
||||||
regexes = {
|
regexes = {
|
||||||
'name': [],
|
'name': [],
|
||||||
|
@ -418,15 +494,15 @@ def main():
|
||||||
print(table)
|
print(table)
|
||||||
|
|
||||||
if args.first > 0:
|
if args.first > 0:
|
||||||
out(f'\n[+] Found {len(resultsList)} symbols meeting all the criterias (but shown only first {args.first} ones).\n')
|
out(f'\n[+] Found {Logger.colored(args, len(resultsList), "green")} symbols meeting all the criterias (but shown only first {Logger.colored(args, args.first, "magenta")} ones).\n')
|
||||||
else:
|
else:
|
||||||
out(f'\n[+] Found {len(resultsList)} symbols meeting all the criterias.\n')
|
out(f'\n[+] Found {Logger.colored(args, len(resultsList), "green")} symbols meeting all the criterias.\n')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
out(f'[-] Did not find symbols meeting specified criterias.')
|
out(f'[-] Did not find symbols meeting specified criterias.')
|
||||||
|
|
||||||
out(f'[.] Processed {filesProcessed.value} files and {symbolsProcessed.value} symbols.')
|
out(f'[.] Processed {Logger.colored(args, filesProcessed.value, "green")} files and {Logger.colored(args, symbolsProcessed.value, "green")} symbols.')
|
||||||
out('[.] Time elapsed: {}'.format(time_elapsed))
|
out('[.] Time elapsed: {}'.format(Logger.colored(args, time_elapsed, "magenta")))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
freeze_support()
|
freeze_support()
|
||||||
|
|
Loading…
Reference in New Issue