#!/bin/bash

################################################################
#Title: volnalysis.sh
#What it does: Run several standard volatility plugins 
#Author: Sheldon Johnson
#Sponsor: Selex-ES
#Usage: volnalysis.sh $PROFILE $FILE
#Notes: none yet!
#Expected Output: Will create a folder in your home directory called 
#"volnalysis", this will populate with a folder a little html sctructure which
#will allow you to easily navigate your analysis outputs.
#Also friends, there is MINIMAL input validation since im not expecting you
#to be using this anywhere except for your own lab!
#USE AT YOUR OWN RISK!
################################################################

################################################################
#INIT
################################################################

################################
#USER SPECIFIED PLUGINS
################################

#Put your plugins you want to run here :D
PLUGINS=("PSSCAN" "PSLIST" "PSXVIEW" "PSTREE" "DLLLIST" "FILESCAN" "MUTANTSCAN" "CONNECTIONS" "CONNSCAN" "SOCKETS" "SOCKSCAN" "NETSCAN" "VADWALK" "MALFIND" "LDRMODULES" \
"HIVESCAN" "HIVELIST" "SVCSCAN" "SHIMCACHE" "CLIPBOARD" "CMDSCAN" "CONSOLES" "MBRPARSER" "MFTPARSER" "IDT" "SSDT" "MODULES" "MODSCAN" "DEVICETREE" "DRIVERIRP" \
"DRIVERSCAN" "UNLOADEDMODULES" "SESSIONS" "WNDSCAN" "DESKSCAN" "EVENTHOOKS" "KDBGSCAN")

#the following have been omitted for speed reasons:
#VERINFO Takes 2-5 mins to run.
#HANDLES Takes 2-5 mins to run.
#HANDLES Takes 2-5 mins to run.
#VADINFO Takes 2-5 mins to run.
#MEMMAP Takes 2-5 mins to run.
#USERASSIST Takes 2-5 mins to run.
#SHELLBAGS Was broken previously.
#TIMELINER Takes well over 5 minutes to run
#ENUMFUNC Takes well over 10 minutes to run
#APIHOOKS Takes well over 10 minutes to run

################################
#CONTSTANTS
################################



DATE=$(date +%y_%m_%d)
#determine the directory volnalysis resides in:
SCRIPTDIR="$(dirname "$0")"
#keeping this static as its likely this will be a set once use many thing.
PLUGINDIR="/usr/share/pyshared/volatility/plugins/"


#Text Colour
TEXT_NO_COLOUR='\033[0m'
TEXT_RED='\e[0;31m'

#auto incrementing array of all of the vol profiles - be careful with this
#as this is kinda dangerous ;)
#right now it works as of  2.3.1
for i in $(vol.py --info | grep "A Profile" | cut -d " " -f 1 | cut -d " " -f 1); do PROFILE[${#PROFILE[@]}]=$i; done

################################
#USER INPUT  VARIABLES
################################

FILENAME="$1"

################################
#FUNCTIONS
################################

#list all the profiles available.
#take user input to select the correct profile.
function ListProfiles(){
	local X=1
	local RETVAL=1
	while [ $X == 1 ]; do
		echo "Please select a Profile from the list below:"
		for i in $(seq 0 $[${#PROFILE[@]}-1]); do
			echo "$i - ${PROFILE[$i]}"; 
		done
		echo "Please prove a profile number,"
		echo "or type \"QUIT\" to exit the script."
		read PROFILENUMBER

		if [[ $PROFILENUMBER == "quit" || $PROFILENUMBER == "QUIT" ]]; then
			exit
		fi

		#sanity check to make sure that the $PROFILENUMBER matches one of the index values
		#weird bash bug - if you enter 222 it will see this as ok...
		#so added a a final check to make sure it only takes 2 numeric characters	
		if [[ $PROFILENUMBER -ge 0 ]] && [[ $PROFILENUMBER -lt "${#PROFILE[@]}" ]] && [[ ${#PROFILENUMBER} < 3 ]]; then
			clear
			RETVAL=0
			X=0
		else
			echo -e $TEXT_RED"You have entered an invalid number." $TEXT_NO_COLOUR
			echo "Please enter a number between 0 and $[${#PROFILE[@]}-1]"
		fi
	done
	return $RETVAL
}

function createDirStructure(){
	local RETVAL=1
	mkdir -p "$1"
	local RETVAL=$?
	#yeah yeah, i remember what jake said about md5ing a memory image 
	#I just thought this was an easy way to link an analysis output
	#with a file if all else fails...
	if [[ $RETVAL != 0 ]]; then
		echo "there was a problem with the directory creation."
	fi
	
	return $RETVAL
}

function createWebOutputIndex(){
	#$1=OUTPUTINDEX $2=FILENAME $3=${PROFILE[$PROFILENUMBER]}
	echo -e "<html>" > $1
	echo -e "\t<body>" >> $1
	echo -e "\t\t<h1>Volatility analysis of: $2 </h1>" >> $1
	echo -e "\t\t\t<h3>Profile selected: $3</h3>" >> $1
}

function closeWebOutputIndex(){
	#$1=OUTPUTINDEX
	echo -e "\t</body>" >> $1
	echo -e "</html>" >> $1
}

function runPlugin(){
	#$1=OUTPUTINDEX $2=OUTPUTDIR $3=FILENAME $4={PROFILE[$PROFILENUMBER]} $5=PARENTDIR $6=PLUGINDIR $7=PLUGINNAME $8=TEXT1 $9=TEXT2
	local OUTPUTINDEX=$1
	local OUTPUTDIR=$2
	local FILENAME=$3
	local PROFILENUMBER=$4
	local PARENTDIR=$5
	local PLUGINDIR=$6
	local PLUGINNAME=$7
	local TEXT1=$8
	local TEXT2=$9

	local TIMESTAMPSTART="$(date) - Started running $PLUGINNAME plugin."
	echo -e "$TIMESTAMPSTART\n"

	local OUTFILE="$OUTPUTDIR""/""$PLUGINNAME"".txt"
	local RUNCOMMAND="vol.py --plugins $PLUGINDIR -f \"$FILENAME\" --profile=\"$PROFILENUMBER\" \"$PLUGINNAME\""
	local VOLOUTPUT=$(eval $RUNCOMMAND)
	local OUTPUTLINES=$(echo "$VOLOUTPUT" | wc -l)
	local TIMESTAMPSTOP="$(date) - Finished running $PLUGINNAME plugin."

	echo -e "\t\t<p><a href=./$PLUGINNAME.txt>$PLUGINNAME</a></p>" >> "$OUTPUTINDEX"
	echo -e "Output for "$PLUGINNAME" on memory image: $PARENTDIR\n" > $OUTFILE
	echo -e "$TIMESTAMPSTART" >> $OUTFILE
	echo -e "$TIMESTAMPSTOP\n" >> $OUTFILE
	echo -e "Command used to execute:\n" >> $OUTFILE
	echo -e "$RUNCOMMAND\n" >> $OUTFILE
	echo -e "Number of lines of output:\n" >> $OUTFILE
	echo -e "$OUTPUTLINES\n" >> $OUTFILE
	echo -e "Plug-in Overview:\n" >> $OUTFILE
	echo -e "$TEXT1\n" >> $OUTFILE
	echo -e "Additional Resources:\n" >> $OUTFILE
	echo -e "$TEXT2\n" >> $OUTFILE
	echo -e "$PLUGINNAME Output:\n" >> $OUTFILE
	echo "$VOLOUTPUT" >> $OUTFILE
	echo -e "\n$TIMESTAMPSTOP\n"
}

function echoWarning(){
	IFS=$'\n'
	echo -e $TEXT_RED"$1"$TEXT_NO_COLOUR
	unset IFS
}

################################
#STARTUP VALIDATION
################################

#Check that the user has  given at least 2 inputs
RETVAL=1 #this is our bad.
clear
if [[ $# == 1 ]]; then
	RETVAL=0; 
else	
	echo "Usage: volnalysis.sh [FILENAME]"
fi

if [[ $RETVAL == 0 ]]; then
	if [[ 1 == 2 ]]; then
	RETVAL=1
	echo "This code is written by Sheldon Johnson and sponsored by Selex-ES"
	echo "Should you choose to run this program you accept that either Sheldon Johnson or Selex-ES"
	echo "cannot be held resposible for any damage it may cause to your system(s)."
	echo "This code is still in early development and does not reflect a finished product."
	echo "Use at your own risk."
	echoWarning  "Type \"YES\" to accept and continue."
	read CONFIRM
	if [[ $CONFIRM == "yes" || $CONFIRM == "YES" ]]; then
		RETVAL=0
	else
		exit
	fi
	fi
fi

#Check that the file location is valid.
#create a few variables, based on the file given
if [[ $RETVAL == 0 ]]; then
	if [[ -e  $FILENAME ]]; then
		MD5=$(md5sum "$FILENAME" | cut -d " " -f 1)
		FILE=$(echo $FILENAME | cut -d "/" -f $[$(echo $FILENAME| grep -o "/" | wc -l)+1])
		PARENTDIR="$DATE""_""$MD5""_""$FILE"
		#assigning output the name of "DATA_MD5_FILENAME"
		OUTPUTDIR=$HOME"/work/volnalysis/"$PARENTDIR
		OUTPUTINDEX=$OUTPUTDIR"/index.html"
	else
		echoWarning "Target memory image does not exist."
		echoWarning "Please re-run the script and supply a valid image location."
		RETVAL=1;
	fi
fi


################################
#BODY
################################

IFS=$'\n'
if [[ $RETVAL == 0 ]]; then 
	ListProfiles
	createDirStructure $OUTPUTDIR
	
	RETVAL=$? 
fi

if [[ $RETVAL == 0 ]]; then 
	createWebOutputIndex $OUTPUTINDEX $FILENAME ${PROFILE[$PROFILENUMBER]}

	#pull the text files with plugin explanations
	#must be called here, otherwise variables passed to it wont have been instansiated yet.
	source "$SCRIPTDIR""/plugin_explanation.txt"

	for i in ${PLUGINS[@]}; do
		DYNARG1=$(eval echo \${$i[0]})
		DYNARG2=$(eval echo \${$i[1]})
		DYNARG3=$(eval echo \${$i[2]})

		runPlugin $OUTPUTINDEX $OUTPUTDIR $FILENAME ${PROFILE[$PROFILENUMBER]} $PARENTDIR $PLUGINDIR $DYNARG1 $DYNARG2 $DYNARG3
	done
	closeWebOutputIndex $OUTPUTINDEX
	firefox $OUTPUTINDEX & &> /dev/null
fi
unset IFS

################################
#FOOTER
################################

#Throw any clean up here.
unset  PROFILE	
