mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-08-20 21:52:21 +02:00
121 lines
5.9 KiB
Python
121 lines
5.9 KiB
Python
import statistics
|
|
import sys
|
|
|
|
# TODO: Rather nasty hack, when volatility's actually installed this would be unnecessary
|
|
sys.path += ".."
|
|
|
|
from volatility.framework import contexts
|
|
from volatility.framework import interfaces
|
|
from volatility.framework import layers
|
|
from volatility.framework.automagic import windows
|
|
|
|
if __name__ == '__main__':
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("filenames", metavar = "FILE", nargs = "+", action = "store", help = "FILE to read for testing")
|
|
parser.add_argument("--32bit", action = "store_false", dest = "bit32", help = "Disable 32-bit scanning")
|
|
parser.add_argument("--64bit", action = "store_false", dest = "bit64", help = "Disable 64-bit scanning")
|
|
parser.add_argument("--pae", action = "store_false", dest = "pae", help = "Disable pae scanning")
|
|
parser.add_argument("-l", "--lime", action = "store_true", dest = "lime", help = "All files are LIME format")
|
|
parser.add_argument("-s", "--selfref", action = "store_true", dest = "selfref",
|
|
help = "Run more generic self-referential tests scanner")
|
|
parser.add_argument("-v", "--verbose", action = "count", default = 0,
|
|
help = "Increase the verbosity of the information returned")
|
|
|
|
args = parser.parse_args()
|
|
|
|
ctx = contexts.Context()
|
|
for filename in args.filenames:
|
|
ctx.config[
|
|
interfaces.configuration.path_join('config' + str(args.filenames.index(filename)), "filename")] = filename
|
|
data = layers.physical.FileLayer(ctx,
|
|
'config' + str(args.filenames.index(filename)),
|
|
'data' + str(args.filenames.index(filename)))
|
|
ctx.memory.add_layer(data)
|
|
if args.lime:
|
|
ctx.config[interfaces.configuration.path_join('lime-config' + str(args.filenames.index(filename)),
|
|
"base_layer")] = 'data' + str(args.filenames.index(filename))
|
|
data = layers.lime.LimeLayer(ctx,
|
|
'lime-config' + str(args.filenames.index(filename)),
|
|
'lime-data' + str(args.filenames.index(filename)))
|
|
ctx.memory.add_layer(data)
|
|
|
|
layername = 'data'
|
|
if args.lime:
|
|
layername = 'lime-data'
|
|
|
|
tests = []
|
|
if args.selfref:
|
|
if args.bit32:
|
|
tests.append(windows.DtbSelfRef32bit())
|
|
if args.bit64:
|
|
tests.append(windows.DtbSelfRef64bit())
|
|
else:
|
|
if args.bit32:
|
|
tests.append(windows.DtbTest32bit())
|
|
if args.bit64:
|
|
tests.append(windows.DtbTest64bit())
|
|
if args.pae:
|
|
tests.append(windows.DtbTestPae())
|
|
|
|
if tests:
|
|
for i in range(len(args.filenames)):
|
|
print("[*] Scanning " + args.filenames[i] + "...")
|
|
scan_results = ctx.memory[layername + str(i)].scan(ctx, windows.PageMapScanner(tests))
|
|
|
|
# Self-referential tests need post-processing to gather the most likely offset
|
|
if args.selfref:
|
|
selfref_results = dict([(test, dict()) for test in tests])
|
|
for test, result in scan_results:
|
|
dtb, refs = result
|
|
test_dict = selfref_results[test]
|
|
for ref in refs:
|
|
# Initialize the value if necessary
|
|
tmp = test_dict.get(ref, set())
|
|
tmp.add(dtb)
|
|
test_dict[ref] = tmp
|
|
selfref_results[test] = test_dict
|
|
scan_results = []
|
|
print(" Self-referential data")
|
|
# Sort by largest pointer size, since the self-ref finder will find larger sizes when searching for smaller)
|
|
for test in sorted(selfref_results, key = lambda x: -x.ptr_size):
|
|
best_found = None
|
|
print(" " + test.layer_type.__name__ + ": ")
|
|
|
|
test_dict = selfref_results[test]
|
|
for ref in sorted(test_dict, key = lambda x: -len(test_dict[x])):
|
|
# Most self-referential DTBs should turn up multiple times because multiple processes should have their own DTB
|
|
if len(test_dict[ref]) < 2:
|
|
continue
|
|
if best_found is None:
|
|
# Most processes are spread out across significantly different pages
|
|
# Therefore the standard deviation should be significant
|
|
if statistics.stdev(test_dict[ref]) > 0x100000:
|
|
for dtb in test_dict[ref]:
|
|
scan_results.append((test, dtb))
|
|
best_found = ref
|
|
if args.verbose > 1 or best_found == ref:
|
|
print(" " + hex(ref) + ": " + ", ".join([hex(x) for x in sorted(test_dict[ref])]))
|
|
print(" Results")
|
|
|
|
# Populate the guesses based on the scan_results
|
|
guesses = dict([(test.layer_type.__name__, set()) for test in tests])
|
|
for test, dtb in scan_results:
|
|
guesses[test.layer_type.__name__].add(dtb)
|
|
|
|
# Guesses should be a dictionary mapping tests to sets of most likely dtbs, the lowest of which is then chosen
|
|
arch = None
|
|
for guess_arch in sorted(guesses, key = lambda x: -len(guesses[x])):
|
|
if not arch and len(guesses[guess_arch]) > 0:
|
|
arch = guess_arch
|
|
if args.verbose:
|
|
print(" " + guess_arch + ": " + ", ".join([hex(x) for x in sorted(guesses[guess_arch])]))
|
|
if arch:
|
|
print("[!] OS Guess:", arch, "with DTB", hex(list(guesses[arch])[0]))
|
|
else:
|
|
print("[X] No DTBs found")
|
|
print()
|
|
else:
|
|
print("[X] No tests selected")
|