Skip to content

Commit

Permalink
Added live mode inspection, additional code adjustments, bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ramirak committed Jun 4, 2023
1 parent fb6ad50 commit 1166ace
Show file tree
Hide file tree
Showing 14 changed files with 264 additions and 140 deletions.
6 changes: 4 additions & 2 deletions Analysis/harddisk.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@

def take_disk_snapshot():
conf = files.retrieve_from_file(enums.files.CONFIG.value)
if conf == enums.results.FILE_NOT_FOUND.value:
return conf
path = conf["snapshot_path"]
sys_map = {}
for filename in glob.iglob(path + "**", recursive=True):
if os.path.isfile(filename):
print(filename)
sys_map.update({filename: [sha256sum(filename), os.path.getsize(filename)]})
return sys_map


def sha256sum(filename):
if os.path.isdir(filename) or not os.path.exists(filename):
return None
return enums.results.FILE_NOT_FOUND.value

h = hashlib.sha256()
b = bytearray(128*1024)
mv = memoryview(b)
Expand Down
167 changes: 102 additions & 65 deletions Analysis/integrity.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,57 @@
import glob, os, time
import glob, os, time, subprocess
from Analysis.memory import take_processes_snapshot
import Data.files as files
from Data.report import write_report, get_proc_header, format_proc
import Data.enums as enums
import Api.vt as vt
from Api.vt import get_report
from Analysis.registry import take_registry_snapshot
from Utils.string_utils import *
from .harddisk import *
from Analysis.networking import *
from Utils.validations import parse_res

def live_mode_inspect(exec_path, duration):
sucess = enums.results.SUCCESS.value

if not os.path.exists(exec_path):
return enums.results.FILE_NOT_FOUND.value

print("Taking snapshot, please wait..")
res = take_snapshot()
if res != enums.results.SUCCESS.value:
return res

res1 = ""
# Not supported for windows
if os.name != 'nt':
print("Starting packet capture..")
res1 = catpure_packets(duration)

print("Starting file execution..")
subprocess.Popen(exec_path.split(), stdout=subprocess.PIPE)

# wait for requested duration before checking for changes.
print("Sleeping for " + str(duration) + "..")
time.sleep(int(duration))

print("Checking integrity..")
res2 = check_integrity()

# Timeout, check found connections if any
if res1 == sucess and res2 == sucess:
report = inspect_addresses(enums.files.TDUMP.value)
write_report(enums.files.TRACES.value, "Connections lookup", report , True, "a+")

return sucess

def take_snapshot():
try:
disk_map = take_disk_snapshot()
reg_map = take_registry_snapshot()
proc_map = take_processes_snapshot()
if disk_map == enums.results.FILE_NOT_FOUND.value:
return disk_map
files.dump_to_file(disk_map, enums.files.HASHES.value)
files.dump_to_file(reg_map, enums.files.REGISTRY.value)
files.dump_to_file(proc_map, enums.files.PROCESSES.value)
files.dump_to_file(take_registry_snapshot(), enums.files.REGISTRY.value)
files.dump_to_file(take_processes_snapshot(), enums.files.PROCESSES.value)
return enums.results.SUCCESS.value
except:
return enums.results.GENERAL_FAILURE.value
Expand All @@ -23,115 +60,115 @@ def take_snapshot():
def check_integrity():
try:
conf = files.retrieve_from_file(enums.files.CONFIG.value)
if conf == enums.results.FILE_NOT_FOUND.value:
return conf

path = conf["snapshot_path"]
scan = bool(conf["virus_total_scan"])

sys_map, reg_map, proc_map = {} , {}, {}

if os.path.exists(enums.files.HASHES.value):
sys_map = files.retrieve_from_file(enums.files.HASHES.value)
if os.path.exists(enums.files.REGISTRY.value):
reg_map = files.retrieve_from_file(enums.files.REGISTRY.value)
if os.path.exists(enums.files.PROCESSES.value):
proc_map = files.retrieve_from_file(enums.files.PROCESSES.value)
fnf = enums.results.FILE_NOT_FOUND.value
sys_map = files.retrieve_from_file(enums.files.HASHES.value)
reg_map = files.retrieve_from_file(enums.files.REGISTRY.value)
proc_map = files.retrieve_from_file(enums.files.PROCESSES.value)

## User should first create a system snapshot
if not len(sys_map) or not len(reg_map) or not len(proc_map):
if sys_map == fnf or reg_map == fnf or proc_map == fnf:
return enums.results.SNAPSHOT_NOT_FOUND.value

write_report(enums.files.TRACES.value, "MaltraceX Log File", "" , False , "w")

f = open(enums.files.TRACES.value, "w")
f.write(print_header("MaltraceX Log File"))

## First check system files
r1 = inspect_files(path, sys_map, scan, f)
r = inspect_files(path, sys_map, scan)
write_report(enums.files.TRACES.value, "Files lookup", r, True ,"a+")

## Check changes to chosen registry locations
r2 = inspect_registry(reg_map, f)
r = inspect_registry(reg_map)
write_report(enums.files.TRACES.value, "Registry lookup (Win only)", r, True , "a+")

## Check memory for new running processes
r3 = inspect_procs(proc_map, f)
f.close()
r = inspect_procs(proc_map)
write_report(enums.files.TRACES.value, "Memory lookup", r, True ,"a+")

files.show_file_content(enums.files.TRACES.value)

err = enums.results.GENERAL_FAILURE.value
if r1 == err or r2 == err or r3 == err:
return enums.results.FINISHED_WITH_ERRORS.value
return enums.results.SUCCESS.value
except Exception as e:
print(e)
return enums.results.GENERAL_FAILURE.value


def inspect_files(path, sys_map, scan, f):
f.write(print_header("Explorer lookup:"))
def inspect_files(path, sys_map, scan):
report = ""
for filename in glob.iglob(path + '**', recursive=True):
print(filename)
if os.path.isfile(filename):
if(filename not in sys_map):
f.write("\nFound new trace: " + filename + " was created on: " + str(time.ctime(os.path.getmtime(filename)) + "\n"))
report += "\nFound new trace: " + filename + " was created on: " + str(time.ctime(os.path.getmtime(filename)) + "\n")
new_hash = sha256sum(filename)
if new_hash == enums.results.GENERAL_FAILURE.value:
if new_hash == enums.results.GENERAL_FAILURE.value or new_hash == enums.results.FILE_NOT_FOUND.value:
continue
f.write("File Hash : " + new_hash)
report += "File Hash : " + new_hash
## Virus total scan depends on user configuration
if scan:
f.write(vt.get_report(new_hash))
f.write(print_divider())
if scan:
vt_report = get_report(filename, new_hash)
if parse_res(vt_report) != -1:
report += vt_report
else:
hash_before = sys_map[filename][0]
hash_after = sha256sum(filename)
if hash_after == enums.results.GENERAL_FAILURE.value:
if hash_after == enums.results.GENERAL_FAILURE.value or hash_after == enums.results.FILE_NOT_FOUND.value:
continue
size_before = sys_map[filename][1]
size_after = os.path.getsize(filename)

if(hash_before != hash_after):
f.write("\nFile - " + filename + " was changed on: " + str(time.ctime(os.path.getmtime(filename)) + "\n"))
f.write("Original hash : " + hash_before + ", Size: " + str(size_before) + "B\n")
f.write("New hash : " + hash_after + ", Size: " + str(size_after) + "B\n")
report += "\nFile - " + filename + " was changed on: " + str(time.ctime(os.path.getmtime(filename)) + "\n")
report += "Original hash : " + str(hash_before) + ", Size: " + str(size_before) + "B\n"
report += "New hash : " + str(hash_after) + ", Size: " + str(size_after) + "B\n"
## Virus total scan depends on user configuration
if scan:
f.write(vt.get_report(hash_after))
f.write(print_divider())
f.write(print_header("End explorer lookup:"))
return enums.results.SUCCESS.value
vt_report = get_report(filename, hash_after)
if parse_res(vt_report) != -1:
report += vt_report

for filename in sys_map:
if not os.path.isfile(filename):
report += "Deleted - " + filename + "\n"

return report

def inspect_registry(reg_map, f):

def inspect_registry(reg_map):
report = ""
## Windows only
if os.name == 'nt':
## Now check common registry locations
f.write(print_header("Registry lookup:"))
new_reg_map = take_registry_snapshot()
if len(new_reg_map) == 0:
return enums.results.GENERAL_FAILURE.value

for folder in new_reg_map:
for key in new_reg_map[folder][1]:
new_val = new_reg_map[folder][1][key]
if folder not in reg_map:
continue
elif key not in reg_map[folder][1]:
f.write("Found new registry key:\nIn: " + new_reg_map[folder][0] + "\\" + folder + "\nKey: " + key + ", Value: " + new_val)
f.write(print_divider())
report += "Found new registry key:\nIn: " + new_reg_map[folder][0] + "\\" + folder + "\nKey: " + key + ", Value: " + new_val
elif new_val != reg_map[folder][1][key]:
f.write("Found new value for: " + key + "\nIn: " + new_reg_map[folder][0] + "\\" + folder + "\nOld Value: " + reg_map[folder][1][key] + "\nNew Value: " + new_val)
f.write(print_divider())
f.write(print_header("End registry lookup:"))
return enums.results.SUCCESS.value
report += "Found new value for: " + key + "\nIn: " + new_reg_map[folder][0] + "\\" + folder + "\nOld Value: " + reg_map[folder][1][key] + "\nNew Value: " + new_val
return report


def inspect_procs(proc_map, f):
def inspect_procs(proc_map):
report = ""
new_proc_map = take_processes_snapshot()
if len(new_proc_map) == 0:
return enums.results.GENERAL_FAILURE.value
f.write(print_header("Memory Lookup:"))
f.write("\n* New processes:\n\n")
f.write(get_proc_header())
report += "\n* New processes:\n\n"
report += get_proc_header()
for proc in new_proc_map:
if proc not in proc_map:
f.write(format_proc(proc, new_proc_map[proc]))
f.write("\n* Killed processes:\n\n")
f.write(get_proc_header())
report += format_proc(proc, new_proc_map[proc])
report += "\n* Killed processes:\n\n"
report += get_proc_header()
for proc in proc_map:
if proc not in new_proc_map:
f.write(format_proc(proc, proc_map[proc]))
f.write(print_header("End memory Lookup:"))
return enums.results.SUCCESS.value
report += format_proc(proc, proc_map[proc])
return report



2 changes: 1 addition & 1 deletion Analysis/memory.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import psutil
from datetime import datetime

from Utils.string_utils import format_proc
from Data.report import format_proc

def take_processes_snapshot():
pids = psutil.pids()
Expand Down
39 changes: 39 additions & 0 deletions Analysis/networking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import subprocess, whois
from Data.files import retrieve_lines_from_file
import Data.enums as enums
from Utils.string_utils import find_between

def catpure_packets(duration):
output = enums.files.TDUMP.value
try:
# Capture any traffic not destined to local network and not related to basic http/s traffic
cmd = "sudo timeout " + duration + " tcpdump -nni any not port 443 and not port 80 and not dst net 192.168.0.0/16 and not dst net 10.0.0.0/16 and not dst net 172.16.0.0/16 &> " + output + "&"

subprocess.call(cmd, shell=True)
return enums.results.SUCCESS.value
except Exception as e:
print(e)
return enums.results.GENERAL_FAILURE.value


def inspect_addresses(pcap_filename):
packets = retrieve_lines_from_file(pcap_filename)
strings = []
new_connections = set()
report = ""

for packet in packets:
strings.append(find_between(packet, "> ", ":"))

for string in strings:
new_connections.add(string.rsplit('.', 1)[0])

for address in new_connections:
if len(address) <= 1:
continue
ip_info = str(whois.whois(address))
report += "New connection: " + address + " : \n"
report += ip_info + "\n"

return report

2 changes: 1 addition & 1 deletion Analysis/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ def take_registry_snapshot():

else:
def take_registry_snapshot():
return {}
return {}
26 changes: 7 additions & 19 deletions Api/vt.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,36 @@
import Data.files as files
import Data.enums as enums
from Analysis.harddisk import sha256sum
from Utils.string_utils import print_header


def get_report(hash):
def get_report(filename, hash):
conf = files.retrieve_from_file(enums.files.CONFIG.value)
if conf == enums.results.FILE_NOT_FOUND.value:
return conf

api_k = conf["virus_total_key"]

if api_k == "":
return enums.results.API_KEY_NOT_FOUND.value

result = "\n Checking " + filename + " - " + hash + ":\n"

url = "https://www.virustotal.com/api/v3/files/" + hash
headers = {"accept": "application/json", "x-apikey": api_k}
response = requests.get(url, headers=headers)
result = ""
if response.status_code != 200:
return enums.results.NO_MATCH_FOUND.value

res = json.loads(response.text)
report_attr = res["data"]["attributes"]
result += print_header("Virus Total report:")
result += "Total Malicious: " + str(report_attr["last_analysis_stats"]["malicious"]) + "\n"
result += "Total Undetected: " + str(report_attr["last_analysis_stats"]["undetected"]) + "\n"
result += "File Reputation: " + str(report_attr["reputation"]) + "\n"
if "popular_threat_classification" in report_attr:
result += "Suggested label: " + report_attr["popular_threat_classification"]["suggested_threat_label"] + "\n"
result += print_header("Engines results:")
engines_list = report_attr["last_analysis_results"]
for i in engines_list:
result += engines_list[i]["engine_name"] + " - " + engines_list[i]["category"] + "\n"
return result


def write_vt_report(chosen_file):
file_hash = sha256sum(chosen_file)
if file_hash == None:
return enums.results.FILE_NOT_FOUND.value
report = get_report(file_hash)
if report == enums.results.API_KEY_NOT_FOUND.value or report == enums.results.NO_MATCH_FOUND.value:
return report
log_file = enums.files.REPORT.value
with open(log_file, "a+") as logfile:
now = datetime.now()
dt_string = now.strftime("%d/%m/%Y %H:%M:%S")
logfile.write("\n-------------------- " + chosen_file + " - " + file_hash + ": " + dt_string + " --------------------\n")
logfile.write(report + "\n")
return enums.results.SUCCESS.value
3 changes: 2 additions & 1 deletion Data/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class files(Enum):
TRACES = p + "traces.mt"
HASHES = p + "hashes.mt"
PESCAN = p + "pescan.mt"
TDUMP = p + "tdump.mt"
REPORT = p + "reports.mt"
REGISTRY = p + "registry.mt"
PROCESSES = p + "processes.mt"
Expand All @@ -25,4 +26,4 @@ class results(Enum):
NON_PE_FILE = 3
FINISHED_WITH_ERRORS = 4
FILE_NOT_FOUND = 5
SUCCESS = 6
SUCCESS = 6
Loading

0 comments on commit 1166ace

Please sign in to comment.