├── imports ├── __init__.py ├── error_handler.py ├── report_manager.py └── vol_handler.py ├── Manual.pdf ├── setup.py ├── README.md └── muninn.py /imports/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Manual.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytisf/muninn/HEAD/Manual.pdf -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | try: 3 | from setuptools import setup 4 | except ImportError: 5 | from distutils.core import setup 6 | 7 | 8 | def read(fname): 9 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 10 | 11 | setup( 12 | name = "Muninn", 13 | version = "0.0.3", 14 | author = "Yuval tisf Nativ", 15 | author_email = "yuval [at ] morirt *dot* com", 16 | description = ("A tool that uses Volatility to get basic analysis of a " 17 | "memory image and provide a simple report."), 18 | license = "GPLv3", 19 | keywords = "volatility memory analyzer forensics", 20 | url = "http://ytisf.github.io/Muninn", 21 | packages=['optparse', 'prettytable'], 22 | long_description=read('README.md'), 23 | classifiers=[ 24 | "Development Status :: 3 - Alpha", 25 | "Topic :: Utilities", 26 | "License :: GNU :: GPLv3", 27 | ], 28 | ) -------------------------------------------------------------------------------- /imports/error_handler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | 5 | # This is the file which handles the shit and what happens when shit happens 6 | 7 | class Shit_Handler(): 8 | def __init__(self): 9 | self._errorlogger = "" 10 | 11 | def error_log(self, code, message): 12 | ''' 13 | This function is just used to document things. 14 | Maybe later we'll add a write to file options or 15 | actually consider the verbosity option seriously... 16 | Codes: 17 | # 0 = good 18 | # 1 = information 19 | # 2 = warning 20 | # 3 = error 21 | # 4 = critical 22 | :param code: Code of the error (from 0-good to 4-critical). 23 | :param message: The message to be logged along with error code. 24 | :return:Nothing 25 | ''' 26 | 27 | class bcolors: 28 | HEADER = '\033[95m' 29 | OKBLUE = '\033[94m' 30 | OKGREEN = '\033[92m' 31 | WARNING = '\033[93m' 32 | FAIL = '\033[91m' 33 | ENDC = '\033[0m' 34 | 35 | 36 | if code == 0: 37 | # This system cannot handle good feedback 38 | print bcolors.OKGREEN + "[+]\t" + bcolors.ENDC + str(message) 39 | 40 | elif code == 1: 41 | # Give out information: 42 | print bcolors.OKBLUE + "[+]\t" + bcolors.ENDC + str(message) # Avoid too much information 43 | 44 | 45 | elif code == 2: 46 | # Enter warning code here 47 | pass 48 | 49 | elif code == 3: 50 | # Enter error code here 51 | print bcolors.WARNING + "[*]\t" + bcolors.ENDC + message 52 | 53 | elif code == 4: 54 | print bcolors.FAIL + "\n\n[!]\t" + bcolors.ENDC + message 55 | sys.exit(0) 56 | 57 | else: 58 | ''' For those who can't even handle a shit_handler... ''' 59 | print bcolors.FAIL + "\n\n[!]\t" + bcolors.ENDC + "You fucked up the shit handler! You're fucking terrible!" 60 | sys.exit(1) 61 | 62 | -------------------------------------------------------------------------------- /imports/report_manager.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import random 6 | 7 | try: 8 | from prettytable import PrettyTable 9 | except ImportError, e: 10 | print "The module 'prettytable' is not installed." 11 | print "Please use: " 12 | print "\tsudo pip install prettytable" 13 | sys.exit(1) 14 | 15 | import imports.error_handler 16 | 17 | 18 | class Secretary(): 19 | def __init__(self): 20 | self._shit_handler = imports.error_handler.Shit_Handler() 21 | self._casenumber = "" 22 | self._title = "" 23 | self._file_handler = "" 24 | self._br = "\n###########################################################################\n\n" 25 | 26 | def get_file_name(self, case_num): 27 | if os.path.isfile("CS%s.txt" % case_num) is None: 28 | return "CS%s.txt" % case_num 29 | else: 30 | number = random.randint(1, 30) 31 | return "CS%s-%s.txt" % (case_num, number) 32 | 33 | def InitiateDocument(self, casenumber, date, filename, md5, imagetype): 34 | ''' 35 | This function will create the basic file and template of the report. 36 | It will generate the heading and the basic information of the forensics 37 | investigation. 38 | :param casenumber: integer, case number 39 | :param date: current date 40 | :param filename: filename of the memory image 41 | :param md5: md5sum to document 42 | :param imagetype: which type has the image been deteced as 43 | :return: 44 | ''' 45 | self._title = "Memory Report - CS-%s" % casenumber 46 | curr_filename = self.get_file_name(casenumber) 47 | if curr_filename == ("CS-%s.txt" % casenumber): 48 | self._shit_handler.error_log(1, "Starting to create document at CS-%s.txt" % casenumber) 49 | else: 50 | self._shit_handler.error_log(1, "A file named 'CS-"+str(casenumber)+".txt' already exists.\n\tCreating file as '" + curr_filename + "'") 51 | self._file_handler = open(curr_filename, 'wb') 52 | self._file_handler.write("###########################################################################\n") 53 | self._file_handler.write("###########################################################################\n") 54 | self._file_handler.write("\t\t\t#%s\n" % self._title) 55 | self._file_handler.write("###########################################################################\n") 56 | self._file_handler.write("###########################################################################\n") 57 | self._file_handler.write("\n##Background:\n") 58 | self._file_handler.write("\tCase Number:\t%s\n" % casenumber) 59 | self._file_handler.write("\tCase Date:\t%s\n" % date) 60 | self._file_handler.write("\tImage Name:\t%s\n" % filename) 61 | self._file_handler.write("\tImage Type:\t%s\n" % imagetype) 62 | self._file_handler.write("\tMD5 Digest:\t%s\n" % md5) 63 | self._file_handler.write("\tGenerated using lazy_volatility by tisf\n") 64 | self._file_handler.write("%s" % self._br) 65 | 66 | def print_title(self, title, level): 67 | ''' 68 | :param title: Text to be in the header 69 | :param level: level of the header 70 | :return: nothing 71 | ''' 72 | 73 | printme = "|" 74 | printme += level * "#" 75 | printme += " %s |" % title 76 | 77 | lower = "+" 78 | lower += "-" * (len(printme) - 1) 79 | lower += "+" 80 | 81 | self._file_handler.write("\n%s\n" % lower) 82 | self._file_handler.write("%s\n" % printme) 83 | self._file_handler.write("%s\n\n" % lower) 84 | 85 | 86 | def print_table(self, headers, body, sort_by): 87 | ''' 88 | :param headers: will contain headers of the table to print 89 | :param body: actual array to print in the table 90 | :param sort_by: the main column which the table will be sorted by 91 | :return: returns nothing 92 | ''' 93 | x = PrettyTable(headers) 94 | x.sortby = sort_by 95 | x.align[sort_by] = "l" 96 | x.padding_width = 1 97 | 98 | for row in body: 99 | x.add_row(row) 100 | 101 | self._file_handler.write("%s\n" % x) 102 | self._file_handler.write("%s" % self._br) 103 | 104 | def save(self): 105 | self._file_handler.close() 106 | self._shit_handler.error_log(0, "Report CS-%s.txt is ready!" % self._casenumber) 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Muninn - The Volatility Reporter 2 | 3 | ##About 4 | Muninn was built to allow an easier approach to initial memory forensics on Windows 7 and Windows XP machines. Usually, when approaching a memory analysis we start by plotting out the basics and looking for the exceptions. This usually involves a lot of commandlining for each and every data set with Volatility. 5 | Muninn will take a case number and a memory image and will try to grab the basic pieces of data _**we**_ usually look for and export them into a readable txt file which will be 'nicer' to read by a human being. It does not try to lead the memory forensics from a to z but rather to help the auditor through the initial plotting. 6 | To check for updates or submit changes follow this repository at the [official repository](https://github.com/ytisf/Muninn) 7 | This program is licensed under GPLv3. 8 | 9 | ##Installation 10 | Clone this repository using: 11 | 12 | git clone https://www.github.com/ytisf/muninn 13 | 14 | Make sure you have all the dependencies installed: 15 | 16 | sudo pip install prettytable 17 | 18 | Make sure [Volatility](https://code.google.com/p/volatility/) is installed and linked to vol.py . 19 | 20 | sudo apt-get install subversion pcregrep libpcre++-dev python-dev build-essentia libgmp3-dev 21 | sudo apt-get install python-pycryptopp sqlite3 libsqlite3-dev 22 | wget https://volatility.googlecode.com/files/volatility-2.3.tar.gz 23 | tar xfv volatility-2.3.tar.gz 24 | cd volatility-2.3/ 25 | sudo python setup.py install 26 | 27 | ##How To 28 | The basic command line arguments for Muninn are: 29 | 30 | Options: 31 | -h, --help show this help message and exit 32 | -f FILENAME, --file=FILENAME The path to memory image to analyse 33 | -c CASENUMBER, --case=CASENUMBER Case number to use 34 | 35 | The image location and case number are mandatory. 36 | 37 | Muninn can be tested using the [memory dumps](https://code.google.com/p/volatility/wiki/PublicMemoryImages) which were published by the guys of Volatility here 38 | 39 | 40 | ##Documentation 41 | Basic structure of Muninn is: 42 | 43 | * **imports** 44 | * vol_handler.py 45 | * error_handler.py 46 | * report_manager.py 47 | * muninn.py 48 | * README.md 49 | 50 | ###muninn.py 51 | The main execution file. This file just calls other imports. This file manages the flow of the application and is a bit documented. Function names and calls are simple to understand. 52 | ###error_handler.py 53 | This manages errors in the program. It is very simple and not documented (since there is nothing to document). Every other python module in this application will call error_handler.py for output to the user (screen). 54 | ###report_manager.py 55 | Will be called to write the report file. It manages the functions: 56 | 57 | * **\_\_init___** - 58 | * **InitiateDocument** - Will create the first block of the document and create the file_handler. 59 | * **print_title** - Will add a header to the file. 60 | * **print_table** - Will add a table to the report (since we have many). 61 | * **save** - This will save the document properly and close the file_handler. 62 | 63 | ###vol_handler.py 64 | > Warning! Black magic regexing here! 65 | > You've been warned! 66 | 67 | * **\_\_init__** - This will initialize constructs. In general, all of the function will try to store the output in the main class as attributes to the class and not as a return option or anything like that. 68 | * **regex_search** - Just what it says. 69 | * **check_if_vol_is_installed** - Diddo. 70 | * **get_image_type** - First time we use Volatility, and we use it to get image type. 71 | * **document_image_details** - Generates basic image details such as MD5. 72 | * **get_process_list** - Takes the process list from the memory image. 73 | * **hive_list** - Gets all the hives. Used also at *find\_hashes* 74 | * **find_hashes** - Extract hashes (and users) from mem image. 75 | * **get_network_connections** - Extract all UDP and TCP connections. (black craft magic van-dam regex vodoo here) 76 | * **get_runkey_from_reg** - Gets the startup keys from the Registry. 77 | * **drivers** - creates the self._drivers object and fills it we the drivers' list. 78 | 79 | ###README.md 80 | Just this readme file. 81 | 82 | ##GPLv3 83 | Muninn - An Automatic Initial Memory Forensics Tool 84 | Copyright (C) 2014 Yuval tisf Nativ 85 | 86 | This program is free software: you can redistribute it and/or modify 87 | it under the terms of the GNU General Public License as published by 88 | the Free Software Foundation, either version 3 of the License, or 89 | (at your option) any later version. 90 | 91 | This program is distributed in the hope that it will be useful, 92 | but WITHOUT ANY WARRANTY; without even the implied warranty of 93 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 94 | GNU General Public License for more details. 95 | 96 | You should have received a copy of the GNU General Public License 97 | along with this program. If not, see . 98 | -------------------------------------------------------------------------------- /muninn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | __name__ = "Muninn" 4 | __authors__ = ["Yuval tisf Nativ", "Omree Benari"] 5 | __version__ = 0.3 6 | __version_name__ = "charlie" 7 | __license__ = "GPLv3" 8 | 9 | import sys 10 | import time 11 | import string 12 | import optparse 13 | import imports.error_handler 14 | import imports.vol_handler 15 | import imports.report_manager 16 | 17 | 18 | # Muninn - An Automatic Initial Memory Forensics Tool 19 | # Copyright (C) 2014 Yuval tisf Nativ 20 | #`` 21 | # This program is free software: you can redistribute it and/or modify 22 | # it under the terms of the GNU General Public License as published by 23 | # the Free Software Foundation, either version 3 of the License, or 24 | # (at your option) any later version. 25 | # 26 | # This program is distributed in the hope that it will be useful, 27 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29 | # GNU General Public License for more details. 30 | # 31 | # You should have received a copy of the GNU General Public License 32 | # along with this program. If not, see . 33 | 34 | 35 | class bcolors: 36 | HEADER = '\033[95m' 37 | OKBLUE = '\033[94m' 38 | OKGREEN = '\033[92m' 39 | WARNING = '\033[93m' 40 | FAIL = '\033[91m' 41 | ENDC = '\033[0m' 42 | 43 | 44 | def banner(): 45 | a = bcolors.WARNING + "\n _____ _\n" 46 | a+= "| |_ _ ___|_|___ ___\n" 47 | a+= "| | | | | | | | | |\n" 48 | a+= "|_|_|_|___|_|_|_|_|_|_|_|\n" + bcolors.ENDC 49 | a+= bcolors.OKGREEN + " the lazy way to analyze memory images \n\n" + bcolors.ENDC 50 | a+= "Muninn will take a memory image and a case number.\n" 51 | a+= "It will then output a text file with basic information\n" 52 | a+= "it extracted on the machine via the image via volatility.\n" 53 | a+= "Type " + sys.argv[0] + " -h to get help.\n" 54 | print a 55 | 56 | def print_license(): 57 | print bcolors.WARNING + "\n######################################################################" 58 | print bcolors.OKGREEN + " GPLv3" + bcolors.WARNING 59 | print "######################################################################" + bcolors.ENDC 60 | print "Muninn - An Automatic Initial Memory Forensics Tool" 61 | print "Copyright (C) 2014 Yuval tisf Nativ" 62 | print "" 63 | print "This program is free software: you can redistribute it and/or modify" 64 | print "it under the terms of the GNU General Public License as published by" 65 | print "the Free Software Foundation, either version 3 of the License, or" 66 | print "(at your option) any later version." 67 | print "" 68 | print "This program is distributed in the hope that it will be useful," 69 | print "but WITHOUT ANY WARRANTY; without even the implied warranty of" 70 | print "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" 71 | print "GNU General Public License for more details." 72 | print "" 73 | print "You should have received a copy of the GNU General Public License" 74 | print "along with this program. If not, see ." 75 | print "" 76 | 77 | 78 | ''' Get basic handlers ''' 79 | vol = imports.vol_handler.VolatiltyHandler() # Volatility Handler 80 | doc = imports.report_manager.Secretary() # Doc Manager 81 | shit = imports.error_handler.Shit_Handler() # I give you; Shit! 82 | vol.check_if_vol_is_installed() # Read the label 83 | 84 | ''' Argument Parsing is done here ''' 85 | parser = optparse.OptionParser() 86 | parser.add_option('-f', '--file', dest='filename', help="The path to memory image to analyse") 87 | parser.add_option('-c', '--case', dest='casenumber', help="Case number to use") 88 | parser.add_option('-v', '--version', action="store_true", dest="ver_flag", default=False, help="Print version information.") 89 | (options, args) = parser.parse_args() 90 | 91 | ''' Test for -v flag ''' 92 | ver_flag = options.ver_flag 93 | if ver_flag: 94 | banner() 95 | print_license() 96 | sys.exit(0) 97 | 98 | if options.filename is None: 99 | banner() 100 | sys.exit(0) 101 | 102 | if options.casenumber is None: 103 | banner() 104 | sys.exit(0) 105 | 106 | filename = options.filename 107 | casenumber = options.casenumber 108 | 109 | 110 | ''' 111 | ####################################### 112 | Real program starts here 113 | ####################################### 114 | ''' 115 | date = time.ctime(time.time()) 116 | 117 | banner() 118 | 119 | shit.error_log(1, "Starting to analyze " + filename + " at " + date) 120 | 121 | 122 | ''' Starting actual work and analysis ''' 123 | vol.document_image_details(filename) # Guess 124 | vol.get_image_type(filename) # I'm getting tired... 125 | vol.get_process_list() # Will you read the label?! 126 | vol.hive_list() # Getting hives from mem image 127 | vol.find_hashes() # Get Local hashes 128 | vol.get_network_connections() # Get all network connections 129 | vol.get_runkey_from_reg() # Get startup values 130 | vol.drivers() # Get all drivers in memory image 131 | 132 | 133 | doc.InitiateDocument(casenumber, date, filename, vol._md5sum, vol._image_type) # Generate document template 134 | 135 | ''' Print Process List ''' 136 | process_array = [] 137 | process_header = ['Offset', 'PID', 'Name', 'Parent PID'] 138 | for each in vol._all_processes: 139 | array = [each.offset, each.pid, each.name, each.ppid ] 140 | process_array.append(array) 141 | 142 | doc.print_title("Processes Found (%s)" % len(process_array), 3) 143 | doc.print_table(process_header, process_array, process_header[1]) 144 | 145 | 146 | ''' Print Hive List ''' 147 | hive_array = [] 148 | hive_header = ["Virtual Offset", "Hive Name"] 149 | for each in vol._hives: 150 | array = [each.vir_offset, each.name] 151 | hive_array.append(array) 152 | 153 | doc.print_title("Hive List (%s)" % len(hive_array), 3) 154 | doc.print_table(hive_header, hive_array, hive_header[1]) 155 | 156 | 157 | ''' Print Hashes ''' 158 | cred_array = [] 159 | cred_header = ["User Name", "UID", "LM", "NTLM"] 160 | for each in vol._creds: 161 | array = [each.username, each.uid, each.lm, each.ntlm] 162 | cred_array.append(array) 163 | 164 | doc.print_title("Hashes Found (%s)" % len(cred_array), 3) 165 | doc.print_table(cred_header, cred_array, cred_header[1]) 166 | 167 | 168 | ''' Startup Keys In Registry ''' 169 | startupkeys_array = [] 170 | startupkeys_header = ["Name"] 171 | for each in vol._startup_keys: 172 | a = filter(lambda x: x in string.printable, str(each)) 173 | startupkeys_array.append([a]) 174 | 175 | doc.print_title("Startup Keys In Registry (%s)" % len(startupkeys_array), 3) 176 | doc.print_table(startupkeys_header, startupkeys_array, startupkeys_header[0]) 177 | 178 | 179 | ''' Print TCP Connections ''' 180 | tcp_array = [] 181 | tcp_header = ["IP Version", "Bind Address", "Bind Port", "Remote Address", "Remote Port", "State", "PID"] 182 | for each in vol._connections_tcp: 183 | array = [each.ver, each.bind_add, each.bind_port, each.remote_addr, each.remote_port, each.state, each.pid] 184 | tcp_array.append(array) 185 | 186 | doc.print_title("TCP Connections List (%s)" % len(tcp_array), 3) 187 | doc.print_table(tcp_header, tcp_array, tcp_header[0]) 188 | 189 | 190 | ''' Print UDP Connections ''' 191 | udp_array = [] 192 | udp_header = ["Port Number", "Version", "Bind Address", "PID", "Process Name"] 193 | for each in vol._connections_udp: 194 | array = [each.bind_port, each.ver, each.bind_add, each.pid, each.p_name] 195 | udp_array.append(array) 196 | 197 | doc.print_title("UDP Connections List (%s)" % len(udp_array), 3) 198 | doc.print_table(udp_header, udp_array, udp_header[0]) 199 | 200 | 201 | ''' Print Drivers ''' 202 | driver_array = [] 203 | driver_header = ["Driver Name", "Short Name", "Offset", "Pointers", "Handlers", "Start", "Size" ] 204 | for each in vol._drivers: 205 | array = [each.full_name, each.name, each.offset, each.pointers, each.handlers, each.start, each.size] 206 | driver_array.append(array) 207 | 208 | doc.print_title("Drivers (%s)" % len(driver_array), 3) 209 | doc.print_table(driver_header, driver_array, driver_header[0]) 210 | -------------------------------------------------------------------------------- /imports/vol_handler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import re 5 | import sys 6 | import hashlib 7 | import commands 8 | 9 | import imports.error_handler 10 | 11 | 12 | class VolatiltyHandler(): 13 | ''' 14 | This class will run volatility with particular arguments and will 15 | manage the output from it. 16 | ''' 17 | 18 | def __init__(self): 19 | self._volatility_handler = "" 20 | self._shit_handler = imports.error_handler.Shit_Handler() 21 | self._is_vol_okay = 0 22 | 23 | # Analysis structure 24 | self._image_type = "" 25 | self._image_location = "" 26 | self._volname = "vol.py" 27 | self._all_processes = "" 28 | self._md5sum = "" 29 | self._imagesize = "" 30 | self._hives = [] 31 | self._creds = [] 32 | self._connections_udp = [] 33 | self._connections_tcp = [] 34 | self._startup_keys = [] 35 | self._drivers = [] 36 | 37 | 38 | def query_yes_no(self, question, default="no"): 39 | """Ask a yes/no question via raw_input() and return their answer. 40 | 41 | "question" is a string that is presented to the user. 42 | "default" is the presumed answer if the user just hits . 43 | It must be "yes" (the default), "no" or None (meaning 44 | an answer is required of the user). 45 | 46 | The "answer" return value is one of "yes" or "no". 47 | """ 48 | 49 | valid = {"yes": True, "y": True, "ye": True, 50 | "no": False, "n": False} 51 | if default is None: 52 | prompt = " [y/n] " 53 | elif default == "yes": 54 | prompt = " [Y/n] " 55 | elif default == "no": 56 | prompt = " [y/N] " 57 | else: 58 | raise ValueError("invalid default answer: '%s'" % default) 59 | 60 | while True: 61 | sys.stdout.write(question + prompt) 62 | choice = raw_input().lower() 63 | if default is not None and choice == '': 64 | return valid[default] 65 | elif choice in valid: 66 | return valid[choice] 67 | else: 68 | sys.stdout.write("Please respond with 'yes' or 'no' " 69 | "(or 'y' or 'n').\n") 70 | 71 | def regex_search(self, data, regex): 72 | """ 73 | This function runs regex search on data given 74 | and returns output. 75 | :data the data to run through the filter 76 | :regex the regex query 77 | :return the filtered result of the search 78 | """ 79 | results = re.search(regex, data) 80 | return results 81 | 82 | 83 | def check_if_vol_is_installed(self): 84 | ''' 85 | This function checks if volatility exists as vol.py. 86 | If it does not, it throws a type 4 error to shit_handler and exists 87 | After version 0.3 it tries to install Volatility for you. 88 | Only verified Ubuntu installation 89 | ''' 90 | status, output = commands.getstatusoutput("vol.py") 91 | if int(status) == 256: 92 | self._is_vol_okay = 1 93 | else: 94 | if self.query_yes_no("Should I try to install Volatility for you?"): 95 | if os.geteuid() != 0: 96 | self._shit_handler.error_log(5, "To install volatility run again with 'sudo'.") 97 | else: 98 | os.system("sudo add-apt-repository -y ppa:pi-rho/security") 99 | os.system("sudo apt-get update") 100 | os.system("sudo apt-get install -y volatility") 101 | status, output = commands.getstatusoutput("vol.py") 102 | if int(status) == 256: 103 | self._shit_handler.error_log(1, "Volatility was successfully installed.") 104 | self._is_vol_okay = 1 105 | else: 106 | self._shit_handler.error_log(5, "Volatility installation's failed!") 107 | self._is_vol_okay = 0 108 | else: 109 | self._shit_handler.error_log(5, "You need to install volatility...") 110 | self._is_vol_okay = 0 111 | 112 | 113 | def get_image_type(self, imagelocation): 114 | ''' 115 | This function will try to get the image_type of the memory image 116 | If the imagetype is not successfully extracted an exception will occur. 117 | The imagetype will be returned to class var self._imagetype 118 | 119 | Other functions depend on this function to create the parent attribute of the file name. 120 | 121 | This function will also match the complete image path in to the class var self._image_location 122 | :param imagelocation: The file location of the image to be analyzed 123 | :return: Will return a 1 in case of an error 124 | ''' 125 | 126 | self._shit_handler.error_log(1, "Getting image type...") 127 | 128 | self._image_location = str(imagelocation) 129 | 130 | command = self._volname + " -f " + str(imagelocation) + " imageinfo | grep -e \"\s*Sugg\"" 131 | status, output = commands.getstatusoutput(command) 132 | regi = "(Win[A-Za-z0-9]+)" 133 | status = 0 134 | 135 | if status == 0: 136 | jibbily = self.regex_search(output, regi) 137 | if jibbily is None: 138 | self._shit_handler.error_log(4, "Image type error!\n%s" % output) 139 | jibbily = str(jibbily.groups(1)) 140 | jibbily = jibbily[2:-3] 141 | self._image_type = jibbily 142 | else: 143 | self._shit_handler.error_log(4, "Did not detect imagetype.") 144 | 145 | self._shit_handler.error_log(0, "Image recognized as %s" % self._image_type) 146 | 147 | 148 | def document_image_details(self, imagelocation): 149 | ''' 150 | This function will get the MD5 sum of the image along with size and other types of information. 151 | The other functions are not dependent of it and it can run 'autonomously'. 152 | :param imagelocation: The file location of the image to be analyzed 153 | :return: Will return a 1 in case of an error 154 | ''' 155 | 156 | self._shit_handler.error_log(1, "Getting MD5 sum") 157 | 158 | fileHandle = open(imagelocation, "rb") 159 | m5Hash = hashlib.md5() 160 | while True: 161 | data = fileHandle.read(8192) 162 | if not data: 163 | break 164 | m5Hash.update(data) 165 | fileHandle.close() 166 | 167 | self._imagesize = (os.path.getsize(imagelocation) >> 20) # size is offseted in 20 to get size in MB 168 | self._md5sum = m5Hash.hexdigest() 169 | self._shit_handler.error_log(0, "MD5 sum generated and is: %s" % self._md5sum) 170 | 171 | 172 | def get_process_list(self): 173 | ''' 174 | This function will extract all processes listed in memory image using 175 | psscan method in vol.py 176 | ''' 177 | 178 | # Function vars 179 | class vol_proc(object): 180 | def __init__(self): 181 | pass 182 | 183 | self._shit_handler.error_log(1, "Getting all processes from file using 'psscan'") 184 | 185 | regi = "([0x].........)\s(.+.exe)\s+(\d+)\s+(\d+)\s+([0x].........)\s+(............................)" 186 | all_processes = [] 187 | 188 | # Execute command 189 | command = self._volname + " -f " + self._image_location + " --profile=" + self._image_type + " psscan" 190 | status, output = commands.getstatusoutput(command) 191 | 192 | output = output.split('\n') 193 | 194 | # Check if there is any output from command 195 | if len(output) == 0: 196 | self._shit_handler.error_log(3, "Finding process command returned 0 results") 197 | return 1 198 | 199 | for proc in output: 200 | temp = self.regex_search(proc, regi) 201 | try: 202 | temp = temp.groups() 203 | process = vol_proc() 204 | process.offset = temp[0] 205 | process.name = temp[1] 206 | process.pid = temp[2] 207 | process.ppid = temp[3] 208 | process.pdb = temp[4] 209 | process.timestamp_cr = temp[5] 210 | all_processes.append(process) 211 | 212 | except: 213 | # line not matching 214 | pass 215 | self._shit_handler.error_log(0, "Got " + str(len(all_processes)) + " processes using 'psscan'") 216 | self._all_processes = all_processes 217 | 218 | 219 | def hive_list(self): 220 | ''' 221 | This function will extract all hives listed in memory image using 222 | hivelist method in vol.py 223 | ''' 224 | 225 | class hive(object): 226 | def __init__(self): 227 | pass 228 | 229 | self._shit_handler.error_log(1, "Starting hivelist harvesting") 230 | 231 | regi = "(0x........)\s(0x........)\s(.+)" 232 | hives = [] 233 | 234 | command = self._volname + " -f " + self._image_location + " --profile=" + self._image_type + " hivelist" 235 | status, output = commands.getstatusoutput(command) 236 | 237 | output = output.split('\n') 238 | 239 | if len(output) == 0: 240 | self._shit_handler.error_log(1, "Finding hivelist returned 0 results") 241 | return 1 242 | 243 | for hive_i in output: 244 | temp = self.regex_search(hive_i, regi) 245 | try: 246 | temp = temp.groups() 247 | current_hive = hive() 248 | current_hive.vir_offset = temp[0] 249 | current_hive.phy_offset = temp[1] 250 | current_hive.name = temp[2] 251 | hives.append(current_hive) 252 | 253 | except: 254 | # no matches found 255 | pass 256 | 257 | self._shit_handler.error_log(0, "Got " + str(len(hives)) + " hives using 'hivelist'") 258 | self._hives = hives 259 | 260 | 261 | def find_hashes(self): 262 | ''' 263 | This function will find hashes in memory. After that it will build an object 264 | of credentials for each credentials found and turn it into a global object. 265 | ''' 266 | 267 | class hash(object): 268 | def __init__(self): 269 | pass 270 | 271 | self._shit_handler.error_log(1, "Starting hash harvesting") 272 | 273 | all_creds = [] 274 | 275 | sam_offset = "" 276 | sys_offset = "" 277 | 278 | regi = "(.+):(\d{3,5}):(.{32}):(.{32})" 279 | 280 | for hive in self._hives: 281 | i = hive.name.find("SAM") 282 | if i == -1: 283 | j = hive.name.find("SYSTEM") 284 | if j == -1: 285 | j = hive.name.find("system") 286 | if j == -1: 287 | pass 288 | else: 289 | sys_offset = hive.vir_offset 290 | else: 291 | sys_offset = hive.vir_offset 292 | else: 293 | sam_offset = hive.vir_offset 294 | 295 | command = self._volname + " -f " + self._image_location + " --profile=" + self._image_type + " -s " + sam_offset + " -y " + sys_offset + " hashdump" 296 | status, output = commands.getstatusoutput(command) 297 | 298 | output = output.split('\n') 299 | output = output[1:] 300 | 301 | if len(output) == 0: 302 | self._shit_handler.error_log(1, "Found 0 users.") 303 | return 1 304 | 305 | for creds in output: 306 | temp = self.regex_search(creds, regi) 307 | try: 308 | temp = temp.groups() 309 | current_creds = hash() 310 | current_creds.username = temp[0] 311 | current_creds.uid = temp[1] 312 | current_creds.lm = temp[2] 313 | current_creds.ntlm = temp[3] 314 | all_creds.append(current_creds) 315 | 316 | except: 317 | # no matches found 318 | pass 319 | 320 | self._creds = all_creds 321 | self._shit_handler.error_log(0, "Found %s hashes in memory" % len(all_creds)) 322 | 323 | 324 | def get_network_connections(self): 325 | ''' 326 | DO NOT start reading or changing this function! 327 | There is some black magic regex voodoo here and it's not nice. 328 | Basically it will give you a list of network connections splitted by TCP 329 | and by UDP but i don't think you want to go into this... 330 | ''' 331 | 332 | self._shit_handler.error_log(1, "Getting network traffic information'") 333 | 334 | class net_socket(object): 335 | def __init__(self): 336 | pass 337 | 338 | tcp_array = [] 339 | udp_array = [] 340 | 341 | tcp_regex = "(0x........)\s(TCPv\d)\s+(.+):(\d{1,5})\s+(.+):(\d{1,5})\s+(LISTENING | ESTABLISHED | CLOSED | CLOSE_WAIT)\s+([0-9-]+)\s+(.+)" 342 | udp_regex = "(0x........)\s(UDPv\d)\s+(.+):(\d{1,5})\s+[*:]{3}\s+([0-9-]+)\s+([a-zA-Z.-]+)\s+([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} UTC[0-9+]+)" 343 | 344 | xp = self._image_type.find("WinXP") 345 | sev = self._image_type.find("Win7") 346 | 347 | if xp == -1: 348 | # this is a windows7 machine 349 | command = self._volname + " -f " + self._image_location + " --profile=" + self._image_type + " netscan" 350 | self._shit_handler.error_log(1, "Network connections identified machine as Win7'") 351 | 352 | elif sev == -1: 353 | # This is a windowsXP machine 354 | command = self._volname + " -f " + self._image_location + " --profile=" + self._image_type + " connscan" 355 | self._shit_handler.error_log(1, "Network connections identified machine as WinXP'") 356 | 357 | else: 358 | # Da faq did you just do?! 359 | self._shit_handler.error_log(3, "Error in finding machine state in vol_handler.get_network_connections'") 360 | return 1 361 | 362 | status, output = commands.getstatusoutput(command) 363 | output = output.split('\n') 364 | 365 | if len(output) == 0: 366 | self._shit_handler.error_log(3, "Fuck this shit. I'ma Duck!") 367 | return 1 368 | 369 | for connection in output: 370 | b = connection.find("TCP") 371 | a = connection.find("UDP") 372 | 373 | if a == 11: 374 | # This is a UDP Connection 375 | temp = self.regex_search(connection, udp_regex) 376 | temp = temp.groups() 377 | current_conn = net_socket() 378 | current_conn.offset = temp[0] 379 | current_conn.ver = temp[1] 380 | current_conn.bind_add = temp[2] 381 | current_conn.bind_port = temp[3] 382 | current_conn.pid = temp[4] 383 | current_conn.p_name = temp[5] 384 | current_conn.time = temp[6] 385 | udp_array.append(current_conn) 386 | 387 | elif b == 11: 388 | # This is a TCP Connection 389 | try: 390 | temp = self.regex_search(connection, tcp_regex) 391 | temp = temp.groups() 392 | current_conn = net_socket() 393 | current_conn.offset = temp[0] 394 | current_conn.ver = temp[1] 395 | current_conn.bind_add = temp[2] 396 | current_conn.bind_port = temp[3] 397 | current_conn.remote_addr = temp[4] 398 | current_conn.remote_port = temp[5] 399 | current_conn.state = temp[6] 400 | current_conn.pid = temp[7] 401 | tcp_array.append(current_conn) 402 | except: 403 | continue 404 | 405 | else: 406 | # Item matched nothing 407 | pass 408 | 409 | self._connections_tcp = tcp_array 410 | self._connections_udp = udp_array 411 | self._shit_handler.error_log(0, "Found %s UDP Connections" % len(udp_array)) 412 | self._shit_handler.error_log(0, "Found %s TCP Connections" % len(tcp_array)) 413 | 414 | 415 | def get_runkey_from_reg(self): 416 | ''' 417 | This will take all registry keys (and values) in CurrentVersion\Run 418 | and add them to the global array of self._startup_keys . 419 | :return:nothing 420 | ''' 421 | startup_array = [] 422 | 423 | regex_for_keys = "(.+)\s+:\s\(S\)\s(.+)" 424 | 425 | self._shit_handler.error_log(1, "Getting what's in the CurrentVersion\Run in Registry'") 426 | 427 | command = self._volname + " -f " + self._image_location + " --profile=" + self._image_type + " -K \"Software\Microsoft\Windows\CurrentVersion\Run\"" + " printkey" 428 | status, output = commands.getstatusoutput(command) 429 | 430 | output = output.split('\n') 431 | 432 | if len(output) == 0: 433 | self._shit_handler.error_log(1, "Finding startup keys command returned 0 results") 434 | return 1 435 | 436 | for line in output: 437 | temp = self.regex_search(line, regex_for_keys) 438 | try: 439 | temp = temp.groups() 440 | startup_array.append(temp[1]) 441 | except: 442 | pass 443 | 444 | self._startup_keys = startup_array 445 | self._shit_handler.error_log(0, "Found %s startup keys" % len(self._startup_keys)) 446 | 447 | 448 | def drivers(self): 449 | ''' 450 | This function will return the list of all drivers found in memory image 451 | Regex here is shitty. Fix it if you dare. 452 | :return: nothing 453 | ''' 454 | 455 | class driver_obj(object): 456 | def __init__(self): 457 | pass 458 | 459 | drivers_array = [] 460 | regex_for_drivers = "(0x........)\s+(\d+)\s+(\d+)\s+(0x........)\s+([0-9xa-f]+)\s.....................([a-zA-Z0-9.]+)\s+(\\\\[A-Z0-9a-z\\\\-]+)" 461 | 462 | self._shit_handler.error_log(1, "Getting Drivers used by the system") 463 | 464 | command = self._volname + " -f " + self._image_location + " --profile=" + self._image_type + " driverscan" 465 | status, output = commands.getstatusoutput(command) 466 | 467 | output = output.split('\n') 468 | 469 | if len(output) == 0: 470 | return 1 471 | 472 | for driver in output: 473 | temp = self.regex_search(driver, regex_for_drivers) 474 | 475 | if temp is None: 476 | continue 477 | 478 | temp = temp.groups() 479 | current_driver = driver_obj() 480 | current_driver.offset = str(temp[0]) 481 | current_driver.pointers = str(temp[1]) 482 | current_driver.handlers = str(temp[2]) 483 | current_driver.start = str(temp[3]) 484 | current_driver.size = str(temp[4]) 485 | current_driver.name = str(temp[5]) 486 | current_driver.full_name = str(temp[6]) 487 | drivers_array.append(current_driver) 488 | 489 | self._drivers = drivers_array 490 | self._shit_handler.error_log(0, "Found %s loaded drivers" % len(drivers_array)) 491 | --------------------------------------------------------------------------------