#!/bin/bash
# Malware Expert Scanner
# https://malware.expert

BINARY="false"
VERSION="v1.0.6"
CLEANUP="0"

if [ -f "$HOME/.mescanner" ]; then
    source "$HOME/.mescanner"
fi

# Signatures
SIG_1="rfxn.hdb"
SIG_1_URL="http://cdn.rfxn.com/downloads/rfxn.hdb"
SIG_2="rfxn.ndb"
SIG_2_URL="http://cdn.rfxn.com/downloads/rfxn.ndb"
SIG_3="malware.expert.hdb"
SIG_3_URL="http://signatures.malware.expert/${KEY}/malware.expert.hdb"
SIG_4="malware.expert.ldb"
SIG_4_URL="http://signatures.malware.expert/${KEY}/malware.expert.ldb"
SIG_5="malware.expert.ndb"
SIG_5_URL="http://signatures.malware.expert/${KEY}/malware.expert.ndb"
SIG_6="malware.expert.fp"
SIG_6_URL="http://signatures.malware.expert/${KEY}/malware.expert.fp"
UPDATE="malware.expert.scanner.sh"
UPDATE_URL="http://cdn.malware.expert/malware.expert.scanner.sh"

# CPanel - clamav
if [ -f /usr/local/cpanel/3rdparty/bin/clamscan ];
then
	BINARY="/usr/local/cpanel/3rdparty/bin/clamscan"
fi

# Directadmin - clamav
if [ -f /usr/local/bin/clamscan ];
then
	BINARY="/usr/local/bin/clamscan"
fi

# Debian / Ubuntu - clamav
if [ -f /usr/bin/clamscan ];
then
	BINARY="/usr/bin/clamscan"
fi

# Check scanner
if [ "$BINARY" != "false" ]; then
	# Check Downloader
	if [ -f /usr/bin/wget ]; then
		DOWNLOAD="/usr/bin/wget"
		DOWNLOAD_FOUND="wget"
	fi
	if [ -f /usr/bin/curl ]; then
		DOWNLOAD="/usr/bin/curl"
		DOWNLOAD_FOUND="curl"
	fi
	if [ -f /usr/local/bin/curl ]; then
		DOWNLOAD="/usr/local/bin/curl"
		DOWNLOAD_FOUND="curl"
	fi
else
	echo "Not found Clamav Scanner binary!"
	exit 1
fi

if [ "$DOWNLOAD_FOUND" == "" ]; then
	echo "Not found any download binary (wget/curl) !"
	exit 1
fi

if ! [ -x "$(command -v sed)" ]; then
  echo 'Error: sed is not installed.' >&2
  exit 1
fi

if ! [ -x "$(command -v awk)" ]; then
  echo 'Error: awk is not installed.' >&2
  exit 1
fi

if ! [ -x "$(command -v tr)" ]; then
  echo 'Error: tr is not installed.' >&2
  exit 1
fi

if ! [ -x "$(command -v rev)" ]; then
  echo 'Error: rev is not installed.' >&2
  exit 1
fi

if ! [ -x "$(command -v find)" ]; then
  echo 'Error: find is not installed.' >&2
  exit 1
fi



# https://stackoverflow.com/questions/1955505/parsing-json-with-unix-tools
parse_json() {
    echo $1 | \
    sed -e 's/[{}]/''/g' | \
    sed -e 's/", "/'\",\"'/g' | \
    sed -e 's/" ,"/'\",\"'/g' | \
    sed -e 's/" , "/'\",\"'/g' | \
    sed -e 's/","/'\"---SEPERATOR---\"'/g' | \
    awk -F=':' -v RS='---SEPERATOR---' "\$1~/\"$2\"/ {print}" | \
    sed -e "s/\"$2\"://" | \
    tr -d "\n\t" | \
    sed -e 's/\\"/"/g' | \
    sed -e 's/\\\\/\\/g' | \
    sed -e 's/^[ \t]*//g' | \
    sed -e 's/^"//'  -e 's/"$//'
}


showHeader() {
	echo -e ""
        printf " +%-65s+\n" "---------------------------------------------------------------------"
        printf " | %-65s %-2s|\n" "Malware Expert - Malware Scanner & Removal ($(showVersion))"
        printf " | %-65s %-2s|\n" ""
        printf " | %-65s %-2s|\n" "https://malware.expert"
        printf " | %-65s %-2s|\n" "support@malware.expert"
        printf " | %-65s %-2s|\n" ""
        printf " +%-65s+\n" "---------------------------------------------------------------------"
}

showHelp() {
	showHeader
        printf " | %-65s %-2s|\n" "USAGE:"
        printf " | %-65s %-2s|\n" "$0 scan <path>"
        printf " | %-65s %-2s|\n" "$0 --exclude='regex_pattern' scan <path>"
        printf " | %-65s %-2s|\n" "$0 clean <path>"
        printf " | %-65s %-2s|\n" "$0 restore <path>"
        printf " | %-65s %-2s|\n" "$0 restore <path> force"
        printf " | %-65s %-2s|\n" "$0 delete <path>"
        printf " | %-65s %-2s|\n" "$0 update"
        printf " | %-65s %-2s|\n" "$0 update force"
        printf " | %-65s %-2s|\n" ""
        printf " | %-65s %-2s|\n" "EXAMPLE:"
        printf " | %-65s %-2s|\n" "$0 scan ./public_html"
        printf " | %-65s %-2s|\n" "$0 clean /home/user/"
        printf " | %-65s %-2s|\n" "$0 --exclude='\.(zip|tar\.gz)$' scan ./"
        printf " +%-65s+\n" "---------------------------------------------------------------------"
}

showVersion() {
        echo "${VERSION}"
}

doUpdateSignatures() {
		echo "Download Signatures - 1"
		`${DOWNLOAD_FIX} /tmp/$SIG_1 $SIG_1_URL`
		echo "Download Signatures - 2"
		`${DOWNLOAD_FIX} /tmp/$SIG_2 $SIG_2_URL`
		echo "Download Signatures - 3"
		`${DOWNLOAD_FIX} /tmp/$SIG_3 $SIG_3_URL`
		echo "Download Signatures - 4"
		`${DOWNLOAD_FIX} /tmp/$SIG_4 $SIG_4_URL`
		echo "Download Signatures - 5"
		`${DOWNLOAD_FIX} /tmp/$SIG_5 $SIG_5_URL`
		echo "Download Signatures - 6"
		`${DOWNLOAD_FIX} /tmp/$SIG_6 $SIG_6_URL`
		echo "Download Complette! "
}

doUpdate() {
	showHeader
	# Databases
	if [ "$DOWNLOAD_FOUND" = "wget" ]; then
		DOWNLOAD_FIX="${DOWNLOAD} --no-check-certificate -U malware.expert.scanner -O"
	fi
	if [ "$DOWNLOAD_FOUND" = "curl" ]; then
		DOWNLOAD_FIX="${DOWNLOAD} -s -A malware.expert.scanner -o"
	fi

	# Update program
	if [ "$COMMAND" == "update" ]; then
		echo -e ""
		echo -e "Update program ..."
		CURRENT_PATH=`pwd`
		`${DOWNLOAD_FIX} $CURRENT_PATH/$UPDATE.new $UPDATE_URL`
	        `/bin/mv -f $CURRENT_PATH/$UPDATE.new $CURRENT_PATH/$UPDATE`
        	`/bin/chmod 750 $CURRENT_PATH/$UPDATE`
	fi

	# Force Upgrade
	if [ "$INPUT_VALUE" == "force" ]; then
		echo -e ""
		echo "Downloading databases - start in 10s ..."
		sleep 10
		echo "Download Signatures - 1"
		`${DOWNLOAD_FIX} /tmp/$SIG_1 $SIG_1_URL`
		echo "Download Signatures - 2"
		`${DOWNLOAD_FIX} /tmp/$SIG_2 $SIG_2_URL`
		echo "Download Signatures - 3"
		`${DOWNLOAD_FIX} /tmp/$SIG_3 $SIG_3_URL`
		echo "Download Signatures - 4"
		`${DOWNLOAD_FIX} /tmp/$SIG_4 $SIG_4_URL`
		echo "Download Signatures - 5"
		`${DOWNLOAD_FIX} /tmp/$SIG_5 $SIG_5_URL`
		echo "Download Signatures - 6"
		`${DOWNLOAD_FIX} /tmp/$SIG_6 $SIG_6_URL`
		echo "Download Complette! "
	fi

	if [ ! -f "/tmp/$SIG_1" ]; then
		echo -e ""
		echo "Missing Signature - 1 (Downloading)"
		`${DOWNLOAD_FIX} /tmp/$SIG_1 $SIG_1_URL`
	fi
	if [ ! -f "/tmp/$SIG_2" ]; then
		echo -e ""
		echo "Missing Signature - 2 (Downloading)"
		`${DOWNLOAD_FIX} /tmp/$SIG_2 $SIG_2_URL`
	fi
	if [ ! -f "/tmp/$SIG_3" ]; then
		echo -e ""
		echo "Missing Signature - 3 (Downloading)"
		`${DOWNLOAD_FIX} /tmp/$SIG_3 $SIG_3_URL`
	fi
	if [ ! -f "/tmp/$SIG_4" ]; then
		echo -e ""
		echo "Missing Signature - 4 (Downloading)"
		`${DOWNLOAD_FIX} /tmp/$SIG_4 $SIG_4_URL`
	fi
	if [ ! -f "/tmp/$SIG_5" ]; then
		echo -e ""
		echo "Missing Signature - 5 (Downloading)"
		`${DOWNLOAD_FIX} /tmp/$SIG_5 $SIG_5_URL`
	fi
	if [ ! -f "/tmp/$SIG_6" ]; then
		echo -e ""
		echo "Missing Signature - 6 (Downloading)"
		`${DOWNLOAD_FIX} /tmp/$SIG_6 $SIG_6_URL`
	fi

	#Possible wrong subscription key!!!
	if [ -f "/tmp/$SIG_5" ]; then
		file_size_kb=`du -k /tmp/$SIG_5 | cut -f1`
		if [ "$file_size_kb" -lt "10" ]; then
			doUpdateSignatures
		fi
		file_size_kb=`du -k /tmp/$SIG_5 | cut -f1`
		if [ "$file_size_kb" -lt "10" ]; then
			echo -e ""
			echo -e "Possible invalid subscription key!!!\n"
		fi
	fi
}

doScan() {
		# Scan malware
		# Update if signatures missing
		doUpdate
		# Start scanning
		if [ "$INPUT_VALUE" == "" ]; then
			CURRENT_PATH=`pwd`
			echo -e ""
			echo -e "\e[1mStart scanning in ${CURRENT_PATH}\e[0m\n"
			if [ ! -z "${EXCLUDE}" ]; then
				echo -e "\e[1mExclude file pattern: ${EXCLUDE}\e[0m\n"
				OUTPUT=`${BINARY} --no-summary -i -r --exclude=${EXCLUDE} -d /tmp/$SIG_1 -d /tmp/$SIG_2 -d /tmp/$SIG_3 -d /tmp/$SIG_4 -d /tmp/$SIG_5 -d /tmp/$SIG_6 $CURRENT_PATH`
			else
				OUTPUT=`${BINARY} --no-summary -i -r -d /tmp/$SIG_1 -d /tmp/$SIG_2 -d /tmp/$SIG_3 -d /tmp/$SIG_4 -d /tmp/$SIG_5 -d /tmp/$SIG_6 $CURRENT_PATH`
			fi
		else
			CURRENT_PATH=$INPUT_VALUE
			if [ ! -d "${INPUT_VALUE}" ]; then
				if [ ! -f "${INPUT_VALUE}" ]; then
					echo -e ""
					echo -e "\e[1mInvalid File or Path!\e[0m"
					echo -e ""
					exit 1
				else
					echo -e ""
					echo -e "\e[1mInvalid Path!\e[0m"
					echo -e ""
					exit 1
				fi
			fi
			echo -e ""
			echo -e "\e[1mStart scanning in ${INPUT_VALUE}\e[0m\n"
			if [ ! -z "${EXCLUDE}" ]; then
				echo -e "\e[1mExclude file pattern: ${EXCLUDE}\e[0m\n"
				OUTPUT=`${BINARY} --no-summary -i -r --exclude=${EXCLUDE} -d /tmp/$SIG_1 -d /tmp/$SIG_2 -d /tmp/$SIG_3 -d /tmp/$SIG_4 -d /tmp/$SIG_5 -d /tmp/$SIG_6 $INPUT_VALUE`
			else
				OUTPUT=`${BINARY} --no-summary -i -r -d /tmp/$SIG_1 -d /tmp/$SIG_2 -d /tmp/$SIG_3 -d /tmp/$SIG_4 -d /tmp/$SIG_5 -d /tmp/$SIG_6 $INPUT_VALUE`
			fi
		fi
		echo "$OUTPUT"
                if [ -z "$OUTPUT" ]; then
                        INFECTED_FILES="0"
                else
                        INFECTED_FILES=`echo "$OUTPUT" | wc -l`
                fi

		# Cleaning result's
		scanArray=()
		CLEANED_FILES="0"
		DELETED_FILES="0"
		SKIPPED_FILES="0"
		MANUAL_FILES="0"

		if [[ "$CLEANUP" != "0" ]]; then
		echo ""
		echo -e "\e[1mStart cleaning ... (this may take for while)\e[0m"
		echo ""

		while IFS= read -r lines;
		do
		        LINE=`echo $lines | grep "UNOFFICIAL FOUND" | grep -v "^$"`
		        MALWARE=`echo $LINE | cut -d':' -f1`
			if [ ! -z "$MALWARE" ]; then
				if [[ $MALWARE != *".suspected"* ]]; then
	        			if [ "$DOWNLOAD_FOUND" = "curl" ]; then
				                DOWNLOAD_URL="$DOWNLOAD_FIX -d auth=demo -F sample=@${MALWARE} https://scanner.malware.expert/api/upload.php"
						RESULT=`${DOWNLOAD_URL}`
						CLEAN=`parse_json ${RESULT} clean`
						DELETE=`parse_json ${RESULT} malware`
						if [ ! -z "$CLEAN" ]; then
							if [[ "$CLEAN" != "0" ]]; then
								### CleanUP ###
								`cp -a ${MALWARE} ${MALWARE}.suspected`
								`$DOWNLOAD_FIX -x POST -d "auth=demo&clean=${CLEAN}" https://scanner.malware.expert/api/clean.php > ${MALWARE}`
							        ORGINAL=`echo $MALWARE | rev | cut -d'/' -f1 | rev`
								#scanArray+=("CLEANED_FILE: ${MALWARE} (ORGINAL_FILE: ${ORGINAL}.suspected)")
								echo -e "\e[32m[CLEANED] \e[0m${MALWARE} (${ORGINAL}.suspected)"
								CLEANED_FILES=$[CLEANED_FILES + 1]
							else
								### Delete ###
								if [ ! -z "$DELETE" ]; then
									if [[ "$DELETE" == "100" ]]; then
										`mv -f ${MALWARE} ${MALWARE}.suspected`
									        ORGINAL=`echo $MALWARE | rev | cut -d'/' -f1 | rev`
										#scanArray+=("DELETED_FILE: ${MALWARE} (ORGINAL_FILE: ${ORGINAL}.suspected)")
										echo -e "\e[31m[DELETED] \e[0m${MALWARE} (${ORGINAL}.suspected)"
										DELETED_FILES=$[DELETED_FILES + 1]
									else
										### Manual review ###
										echo -e "\e[1m[MANUAL_] \e[0m${MALWARE}"
										MANUAL_FILES=$[MANUAL_FILES + 1]
									fi
								fi
							fi
						fi
					fi
			        else
					echo "[SKIPPED] ${MALWARE}"
					SKIPPED_FILES=$[SKIPPED_FILES + 1]
				fi
			fi
		done < <(printf '%s\n' "$OUTPUT")
		fi

		# Print cleaning result's
		echo -e ""
		echo -e "\e[1m----------- SCAN SUMMARY -----------\e[0m"
		echo -e "Infected files: ${INFECTED_FILES}"
		echo -e "Cleaned files: ${CLEANED_FILES}"
		echo -e "Deleted files: ${DELETED_FILES}"
		echo -e "Skipped files: ${SKIPPED_FILES}"
		echo -e "Manual files: ${MANUAL_FILES}"
		echo -e ""
		if [[ "$CLEANUP" == "0" ]]; then
			echo -e "\e[32mUSAGE for automatic cleaning:\e[0m"
			echo -e "\e[1m# bash $0 clean ${CURRENT_PATH}\e[0m"
		fi
		echo -e ""
		echo -e "\e[31mBuy cPGuard for realtime protection with full features\e[0m"
		echo -e ""
}

doRestore() {
		# Start restore
		if [ "$INPUT_VALUE" == "" ]; then
			CURRENT_PATH=`pwd`
			echo -e ""
			echo -e "\e[1mStart restoring in ${CURRENT_PATH}\e[0m\n"
			OUTPUT=`find $CURRENT_PATH -iname "*.suspected"`
			echo -e "1"
		else
			if [ -d "$INPUT_VALUE" ]; then
				echo -e ""
				echo -e "\e[1mStart restoring in ${INPUT_VALUE}\e[0m\n"
				OUTPUT=`find $INPUT_VALUE -iname "*.suspected"`
			else
				echo -e ""
				echo -e "\e[1mInvalid PATH!\e[0m"
				echo -e ""
				exit 1
			fi
		fi
		while IFS= read -r lines;
		do
			ORGINAL=`echo $lines | rev | cut -d'.' -f2- | rev`
			if [ ! -f "${ORGINAL}" ]; then
				echo -e "\e[1m[RESTORE] \e[0m${lines} \e[1m->\e[0m ${ORGINAL}"
				`mv $lines $ORGINAL`
			else
				if [ ! -z "$FORCE" ]; then
					echo -e "\e[1m[RESTORE] \e[0m${lines} \e[1m->\e[0m ${ORGINAL}"
					`mv -f $lines $ORGINAL`
				else
					echo -e "\e[1m[SKIPPED] \e[0m${lines} \e[1m->\e[0m ${ORGINAL}"
				fi
			fi
		done < <(printf '%s\n' "$OUTPUT")
		echo -e ""

}

doDelete() {
		# Start delete
		if [ "$INPUT_VALUE" == "" ]; then
			CURRENT_PATH=`pwd`
			echo -e ""
			echo -e "\e[1mStart deleting *.suspected files in ${CURRENT_PATH} folder\e[0m\n"
			OUTPUT=`find $CURRENT_PATH -iname "*.suspected"`
			echo -e "1"
		else
			if [ -d "$INPUT_VALUE" ]; then
				echo -e ""
				echo -e "\e[1mStart deleting *.suspected files in ${INPUT_VALUE} folder\e[0m\n"
				OUTPUT=`find $INPUT_VALUE -iname "*.suspected"`
			else
				echo -e ""
				echo -e "\e[1mInvalid PATH!\e[0m"
				echo -e ""
				exit 1
			fi
		fi
		read -p "ARE YOU REALLY SURE (Cannot undo this!!!) (y/n)? " answer
		case ${answer:0:1} in
		y|Y )
        		echo -e "Start deleting in 10s ..."
			sleep 10
    		;;
	        * )
		        exit 1
    		;;
		esac
		DELETED_FILES="0"
		while IFS= read -r lines;
		do
			if [ -f "${lines}" ]; then
				echo -e "\e[1m[DELETED] \e[0m${lines}"
				`rm -f $lines`
				DELETED_FILES=$[DELETED_FILES + 1]
			fi
		done < <(printf '%s\n' "$OUTPUT")
		echo -e ""
		if [[ "$DELETED_FILES" == "0" ]]; then
			echo -e "Cannot find anything to delete!"
		else
			echo -e "Deleting done!"
		fi
}

doSubscriptionKey() {
		read -p "  Enter subscrition key: " subscription_key
		if [ ${#subscription_key} == 25 ]; then
			echo 'KEY="'$subscription_key'"' > "$HOME/.mescanner"
		else
			echo -e "  Invalid subscription key!\n"
			exit 1
		fi
}

doRemoveSignatures() {
		`rm -f /tmp/$SIG_1`
		`rm -f /tmp/$SIG_2`
		`rm -f /tmp/$SIG_3`
		`rm -f /tmp/$SIG_4`
		`rm -f /tmp/$SIG_5`
		`rm -f /tmp/$SIG_6`
}

####################################################

if [ -f "$HOME/.mescanner" ]; then
    source "$HOME/.mescanner"
else
	doRemoveSignatures
        showHeader
        echo -e ""
	echo -e "  Missing subscription key - buy it from https://malware.expert/shop\n"
        while true; do
            read -p "  Do you wish enter subscription key ? (yes/no) " yn
                case $yn in
                    [Yy]* ) doSubscriptionKey; break;;
                    [Nn]* ) exit;;
                        * ) echo "Please answer yes or no.";;
                esac
        done
fi

### options ###
for i in "$@"
do
case $i in
    -e=*|--exclude=*)
    EXCLUDE="${i#*=}"
    shift # past argument=value
    ;;
    *)
	if [ $# -eq 3 ]; then
        	FORCE="1"
	fi
	if [ $# -eq 2 ]; then
        	INPUT_VALUE=$2
	fi
	if [ $# -eq 1 ]; then
        	COMMAND=$1
	fi
    ;;
esac
done

case "$1" in
	update) doUpdate
		;;
	scan)   doScan
		;;
	clean)	CLEANUP=1
		doScan
		;;
	restore) doRestore
		;;
	delete) doDelete
		;;
        * )     showHelp
                exit 0
                ;;
esac
