Skip to content

Commit 30a0476

Browse files
Merge pull request #100 from alexandreborges/dev
Malwoverview 8.0.5
2 parents 6691dda + d63f8c8 commit 30a0476

5 files changed

Lines changed: 140 additions & 10 deletions

File tree

README.md

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Malwoverview
22

3-
[<img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/alexandreborges/malwoverview?color=red&style=for-the-badge">](https://github.com/alexandreborges/malwoverview/releases/tag/v8.0.4) [<img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/alexandreborges/malwoverview?color=Yellow&style=for-the-badge">](https://github.com/alexandreborges/malwoverview/releases) [<img alt="GitHub Release Date" src="https://img.shields.io/github/release-date/alexandreborges/malwoverview?label=Release%20Date&style=for-the-badge">](https://github.com/alexandreborges/malwoverview/releases) [<img alt="GitHub" src="https://img.shields.io/github/license/alexandreborges/malwoverview?style=for-the-badge">](https://github.com/alexandreborges/malwoverview/blob/master/LICENSE)
3+
[<img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/alexandreborges/malwoverview?color=red&style=for-the-badge">](https://github.com/alexandreborges/malwoverview/releases/tag/v8.0.5) [<img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/alexandreborges/malwoverview?color=Yellow&style=for-the-badge">](https://github.com/alexandreborges/malwoverview/releases) [<img alt="GitHub Release Date" src="https://img.shields.io/github/release-date/alexandreborges/malwoverview?label=Release%20Date&style=for-the-badge">](https://github.com/alexandreborges/malwoverview/releases) [<img alt="GitHub" src="https://img.shields.io/github/license/alexandreborges/malwoverview?style=for-the-badge">](https://github.com/alexandreborges/malwoverview/blob/master/LICENSE)
44
[<img alt="GitHub stars" src="https://img.shields.io/github/stars/alexandreborges/malwoverview?logoColor=Red&style=for-the-badge">](https://github.com/alexandreborges/malwoverview/stargazers)
55
[<img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/ale_sp_brazil?style=for-the-badge&logo=X&color=blueviolet">](https://twitter.com/ale_sp_brazil)
66
[![Downloads](https://static.pepy.tech/personalized-badge/malwoverview?period=month&units=international_system&left_color=grey&right_color=orange&left_text=Last%2030%20days)](https://pepy.tech/project/malwoverview)
@@ -46,7 +46,7 @@
4646
See GNU Public License on <http://www.gnu.org/licenses/>.
4747

4848

49-
## Current Version: 8.0.4 (Codename: Revolutions)
49+
## Current Version: 8.0.5 (Codename: Revolutions)
5050

5151
Important note: Malwoverview does NOT submit samples to any endpoint by default,
5252
so it respects possible Non-Disclosure Agreements (NDAs). There're specific options
@@ -1422,6 +1422,36 @@ Use --help with any subcommand for details:
14221422

14231423
## HISTORY
14241424

1425+
Version 8.0.5:
1426+
1427+
This version:
1428+
1429+
* Improves the directory check against VirusTotal (-d option). The
1430+
"AV Detection" column is renamed to "AV", the Sample and other
1431+
columns are narrowed and realigned, and two new local-analysis
1432+
columns are added for each sample: "Overlay", indicating whether
1433+
the PE has an overlay (YES/NO, or N/A for non-PE files), and
1434+
"Ent", showing the file entropy as a value from 0.00 to 8.00. The
1435+
entropy is the highest per-section entropy of the PE (the best
1436+
signal for packed or encrypted sections), falling back to the
1437+
whole-file Shannon entropy for non-PE files.
1438+
1439+
* Shows the overlay size in the VirusTotal file report (-v 1/2/3).
1440+
When a PE has an overlay, an "Overlay Size" field is now printed
1441+
right below the "Overlay" field, formatted in KB/MB rather than
1442+
raw bytes.
1443+
1444+
* Shows overlay information in the VirusTotal hash report (-v 8).
1445+
The report by hash now reports whether the sample has an overlay
1446+
(and its size, in KB/MB) directly from VirusTotal's pe_info data,
1447+
without downloading the sample.
1448+
1449+
* Shows the file entropy in the VirusTotal file report (-v 1/2/3).
1450+
An "Entropy" field is now printed below the overlay information,
1451+
using the same value as the -d directory check: the highest
1452+
per-section entropy of the PE (0.00 to 8.00), falling back to the
1453+
whole-file Shannon entropy for non-PE files.
1454+
14251455
Version 8.0.4:
14261456

14271457
This version:

malwoverview/malwoverview.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
# Corey Forman (https://github.com/digitalsleuth)
2222
# Christian Clauss (https://github.com/cclauss)
2323

24-
# Malwoverview.py: version 8.0.4 (codename: Revolutions)
24+
# Malwoverview.py: version 8.0.5 (codename: Revolutions)
2525

2626
import os
2727
import sys
@@ -68,7 +68,7 @@
6868
__author__ = "Alexandre Borges"
6969
__copyright__ = "Copyright 2018-2026 Alexandre Borges"
7070
__license__ = "GNU General Public License v3.0"
71-
__version__ = "8.0.4"
71+
__version__ = "8.0.5"
7272
__email__ = "reverseexploit at proton.me"
7373

7474
def finish_hook(signum, frame):

malwoverview/modules/virustotal.py

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from datetime import datetime
33
from malwoverview.utils.colors import mycolors, printr
44
from malwoverview.utils.hash import sha256hash
5-
from malwoverview.utils.peinfo import ftype, isoverlay, overextract, list_imports_exports
5+
from malwoverview.utils.peinfo import ftype, isoverlay, overlaysize, humansize, fileentropy, overextract, list_imports_exports
66
import geocoder
77
import validators
88
import requests
@@ -52,27 +52,47 @@ def filechecking_v3(self, ffpname2, showreport, impexp, ovrly):
5252
mysha256hash = sha256hash(targetfile)
5353

5454
magictype = ftype(targetfile)
55+
ovrlsize = ''
5556
if re.match(r'^PE[0-9]{2}|^MS-DOS', magictype):
5657
ret_overlay = isoverlay(targetfile)
58+
if (ret_overlay == "YES"):
59+
ovrlsize = humansize(overlaysize(targetfile))
60+
fent = "%.2f" % fileentropy(targetfile)
5761

5862
if (showreport == 0):
5963
self.vthashwork(mysha256hash, showreport)
6064

6165
if re.match(r'^PE[0-9]{2}|^MS-DOS', magictype):
6266
if (cv.bkg == 1):
6367
print(mycolors.foreground.lightred + "Overlay: ".ljust(21) + mycolors.reset + ret_overlay, end='\n')
68+
if (ret_overlay == "YES"):
69+
print(mycolors.foreground.lightred + "Overlay Size: ".ljust(21) + mycolors.reset + ovrlsize, end='\n')
6470
if re.match(r'^PE[0-9]{2}|^MS-DOS', magictype):
6571
if (cv.bkg == 0):
6672
print(mycolors.foreground.red + "Overlay: ".ljust(21) + mycolors.reset + ret_overlay, end='\n')
73+
if (ret_overlay == "YES"):
74+
print(mycolors.foreground.red + "Overlay Size: ".ljust(21) + mycolors.reset + ovrlsize, end='\n')
75+
if (cv.bkg == 1):
76+
print(mycolors.foreground.lightred + "Entropy: ".ljust(21) + mycolors.reset + fent, end='\n')
77+
if (cv.bkg == 0):
78+
print(mycolors.foreground.red + "Entropy: ".ljust(21) + mycolors.reset + fent, end='\n')
6779
else:
6880
self.vtreportwork(mysha256hash, 1)
6981

7082
if re.match(r'^PE[0-9]{2}|^MS-DOS', magictype):
7183
if (cv.bkg == 1):
7284
print(mycolors.foreground.lightred + "Overlay: ".ljust(21) + mycolors.reset + ret_overlay, end='\n')
85+
if (ret_overlay == "YES"):
86+
print(mycolors.foreground.lightred + "Overlay Size: ".ljust(21) + mycolors.reset + ovrlsize, end='\n')
7387
if re.match(r'^PE[0-9]{2}|^MS-DOS', magictype):
7488
if (cv.bkg == 0):
7589
print(mycolors.foreground.red + "Overlay: ".ljust(21) + mycolors.reset + ret_overlay, end='\n')
90+
if (ret_overlay == "YES"):
91+
print(mycolors.foreground.red + "Overlay Size: ".ljust(21) + mycolors.reset + ovrlsize, end='\n')
92+
if (cv.bkg == 1):
93+
print(mycolors.foreground.lightred + "Entropy: ".ljust(21) + mycolors.reset + fent, end='\n')
94+
if (cv.bkg == 0):
95+
print(mycolors.foreground.red + "Entropy: ".ljust(21) + mycolors.reset + fent, end='\n')
7696
if (impexp == 1):
7797
list_imports_exports(targetfile)
7898
if (ovrly == 1):
@@ -1284,6 +1304,12 @@ def vthashwork(self, myhash, showreport):
12841304
if ('imphash' in attrs['pe_info']):
12851305
imphash = attrs['pe_info']['imphash']
12861306
print(mycolors.foreground.yellow + "\n".ljust(22) + "Imphash: ".ljust(15) + mycolors.reset + str(imphash), end='')
1307+
if ('overlay' in attrs['pe_info']):
1308+
print(mycolors.foreground.yellow + "\n".ljust(22) + "Overlay: ".ljust(15) + mycolors.reset + "YES", end='')
1309+
if ('size' in attrs['pe_info']['overlay']):
1310+
print(mycolors.foreground.yellow + "\n".ljust(22) + "Overlay Size: ".ljust(15) + mycolors.reset + humansize(attrs['pe_info']['overlay']['size']), end='')
1311+
else:
1312+
print(mycolors.foreground.yellow + "\n".ljust(22) + "Overlay: ".ljust(15) + mycolors.reset + "NO", end='')
12871313
if ('import_list' in attrs['pe_info']):
12881314
print(mycolors.foreground.yellow + "\n".ljust(22) + "Libraries: ".ljust(15), end='')
12891315
for lib in attrs['pe_info']['import_list']:
@@ -1473,6 +1499,12 @@ def vthashwork(self, myhash, showreport):
14731499
if ('imphash' in attrs['pe_info']):
14741500
imphash = attrs['pe_info']['imphash']
14751501
print(mycolors.foreground.blue + "\n".ljust(22) + "Imphash: ".ljust(15) + mycolors.reset + str(imphash), end='')
1502+
if ('overlay' in attrs['pe_info']):
1503+
print(mycolors.foreground.blue + "\n".ljust(22) + "Overlay: ".ljust(15) + mycolors.reset + "YES", end='')
1504+
if ('size' in attrs['pe_info']['overlay']):
1505+
print(mycolors.foreground.blue + "\n".ljust(22) + "Overlay Size: ".ljust(15) + mycolors.reset + humansize(attrs['pe_info']['overlay']['size']), end='')
1506+
else:
1507+
print(mycolors.foreground.blue + "\n".ljust(22) + "Overlay: ".ljust(15) + mycolors.reset + "NO", end='')
14761508
if ('import_list' in attrs['pe_info']):
14771509
print(mycolors.foreground.blue + "\n".ljust(22) + "Libraries: ".ljust(15), end='')
14781510
for lib in attrs['pe_info']['import_list']:
@@ -2005,18 +2037,30 @@ def vtdirchecking(self, repo2, apitype):
20052037

20062038
file_hash_dict = dict(list(zip(F, H)))
20072039

2008-
print("\nSample".center(10) + "Filename".center(72) + "Description".center(26) + "Threat Label".center(28) + "AV Detection".center(26))
2009-
print('-' * 154, end="\n\n")
2040+
print("\n" + "Sample".ljust(9) + "Filename".ljust(72) + "Description".ljust(30) + "Threat Label".ljust(34) + "AV".ljust(4) + "Overlay".center(9) + "Ent")
2041+
print('-' * 162, end="\n\n")
20102042

20112043
hashnumber = 0
20122044

20132045
for key, value in file_hash_dict.items():
20142046
hashnumber = hashnumber + 1
20152047
(type_description, threat_label, malicious) = self.vtbatchwork(value)
2048+
try:
2049+
magictype = ftype(key)
2050+
except Exception:
2051+
magictype = ''
2052+
if re.match(r'^PE[0-9]{2}|^MS-DOS', magictype):
2053+
try:
2054+
overlay = isoverlay(key)
2055+
except Exception:
2056+
overlay = "N/A"
2057+
else:
2058+
overlay = "N/A"
2059+
entropy = "%.2f" % fileentropy(key)
20162060
if (cv.bkg == 1):
2017-
print(mycolors.foreground.lightcyan + "file_" + str(hashnumber) + "\t " + mycolors.reset + (key.strip()).ljust(71) + mycolors.foreground.yellow + (type_description).ljust(30) + mycolors.foreground.lightcyan + (threat_label).ljust(34) + mycolors.foreground.lightred + str(malicious))
2061+
print(mycolors.foreground.lightcyan + ("file_" + str(hashnumber)).ljust(9) + mycolors.reset + (key.strip()).ljust(72) + mycolors.foreground.yellow + (type_description).ljust(30) + mycolors.foreground.lightcyan + (threat_label).ljust(34) + mycolors.foreground.lightred + str(malicious).ljust(4) + mycolors.foreground.yellow + overlay.center(9) + mycolors.foreground.lightcyan + entropy)
20182062
if (cv.bkg == 0):
2019-
print(mycolors.foreground.blue + "file_" + str(hashnumber) + "\t " + mycolors.reset + (key.strip()).ljust(71) + mycolors.foreground.cyan + (type_description).ljust(30) + mycolors.foreground.blue + (threat_label).ljust(34) + mycolors.foreground.red + str(malicious))
2063+
print(mycolors.foreground.blue + ("file_" + str(hashnumber)).ljust(9) + mycolors.reset + (key.strip()).ljust(72) + mycolors.foreground.cyan + (type_description).ljust(30) + mycolors.foreground.blue + (threat_label).ljust(34) + mycolors.foreground.red + str(malicious).ljust(4) + mycolors.foreground.cyan + overlay.center(9) + mycolors.foreground.blue + entropy)
20202064
if (apitype_var == 1):
20212065
if ((hashnumber % 4) == 0):
20222066
time.sleep(61)

malwoverview/utils/peinfo.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from malwoverview.utils.colors import mycolors, printr
33
import malwoverview.modules.configvars as cv
44
import magic
5+
import math
56
import os
67

78

@@ -20,6 +21,61 @@ def isoverlay(file_item):
2021
return ovr
2122

2223

24+
def rawentropy(file_item):
25+
try:
26+
with open(file_item, "rb") as f:
27+
data = f.read()
28+
except Exception:
29+
return 0.0
30+
if not data:
31+
return 0.0
32+
occurences = [0] * 256
33+
for byte in data:
34+
occurences[byte] += 1
35+
entropy = 0.0
36+
length = len(data)
37+
for count in occurences:
38+
if count:
39+
p = float(count) / length
40+
entropy -= p * math.log(p, 2)
41+
return entropy
42+
43+
44+
def fileentropy(file_item):
45+
try:
46+
pe = pefile.PE(file_item)
47+
sect_entropies = [section.get_entropy() for section in pe.sections]
48+
if sect_entropies:
49+
return max(sect_entropies)
50+
except Exception:
51+
pass
52+
return rawentropy(file_item)
53+
54+
55+
def overlaysize(file_item):
56+
try:
57+
pe = pefile.PE(file_item)
58+
offset = pe.get_overlay_data_start_offset()
59+
if offset is None:
60+
return 0
61+
return os.path.getsize(file_item) - offset
62+
except Exception:
63+
return 0
64+
65+
66+
def humansize(num_bytes):
67+
try:
68+
num_bytes = float(num_bytes)
69+
except (TypeError, ValueError):
70+
return str(num_bytes)
71+
for unit in ('bytes', 'KB', 'MB', 'GB', 'TB'):
72+
if num_bytes < 1024.0 or unit == 'TB':
73+
if unit == 'bytes':
74+
return "%d bytes" % int(num_bytes)
75+
return "%.2f %s" % (num_bytes, unit)
76+
num_bytes /= 1024.0
77+
78+
2379
def overextract(fname):
2480
with open(fname, "rb") as o:
2581
r = o.read()

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "malwoverview"
7-
version = "8.0.4"
7+
version = "8.0.5"
88
description = "Malwoverview is a first response tool for threat hunting."
99
readme = "README.md"
1010
authors = [{name = "Alexandre Borges", email = "reverseexploit@proton.me"}]

0 commit comments

Comments
 (0)