├── 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 |
--------------------------------------------------------------------------------