can scan for function calls now

This commit is contained in:
2026-04-04 15:42:57 +11:00
parent ac7a1658c3
commit a54bc2b107
8 changed files with 291 additions and 58 deletions

View File

@@ -2,5 +2,5 @@ class Detection:
def __init__(self): def __init__(self):
pass pass
def run(self, contents: bytes, file) -> bool: def run(self, contents: bytes, file, path: str) -> bool:
pass pass

View File

@@ -1,23 +1,26 @@
from .detection import Detection from .detection import Detection
from console import console
import json
import hashlib import hashlib
import os
hashes = {
"test virus": {
"md5": "e74af7e29983d1c1d1b6fa11c9e6ea9a",
"sha1": "e66c4bc68cc58313baf227843df85911f23ae202",
"sha256": "fcd2db21ad4eb03b77405235381b87ddd2d29150cfc273db520b3f852702c8e4"
}
}
class Hash(Detection): class Hash(Detection):
def run(self, content: bytes, _) -> bool: def run(self, content: bytes, _, __) -> bool:
console.print("[d]Getting hashes...")
with open(os.path.realpath(os.path.join(__file__, "../../hashes.json")), "r") as f:
hashes = json.load(f)
console.print("[d]Generating hashes...")
md5, sha1, sha256 = hashlib.md5(content).hexdigest(), hashlib.sha1(content).hexdigest(), hashlib.sha256(content).hexdigest() md5, sha1, sha256 = hashlib.md5(content).hexdigest(), hashlib.sha1(content).hexdigest(), hashlib.sha256(content).hexdigest()
console.print(f"[d] - MD5: {md5}\n - SHA1: {sha1}\n - SHA256: {sha256}[/]")
for virus_name, virus_hashes in hashes.items(): for virus_name, virus_hashes in hashes.items():
if virus_hashes["md5"] == md5 or virus_hashes["sha1"] == sha1 or virus_hashes["sha256"] == sha256: if virus_hashes["md5"] == md5 or virus_hashes["sha1"] == sha1 or virus_hashes["sha256"] == sha256:
console.print(f"[bold orange1]⚠️ HASH MATCH: {virus_name} DETECTED")
return True return True
return False return False

View File

@@ -1,45 +1,115 @@
from .detection import Detection from .detection import Detection
from elftools.elf.elffile import ELFFile from elftools.elf.elffile import ELFFile
from elftools.common.exceptions import ELFError
from elftools.elf.elffile import Section
from console import console
import r2pipe
import re
scary = {
"Reverse Shell": {"socket": 1, "connect": 1, "execve": 1, "dup2": 1},
"Process Injection": {"mmap": 1, "mprotect": 1, "ptrace": 1, "malloc": 10},
"Dynamic Resolution": {"dlopen": 1, "dlsym": 1, "socket": 1, "connect": 1},
}
class Heuristics(Detection): class Heuristics(Detection):
THRESHOLD = 3 THRESHOLD = 4
def run(self, contents: bytes, file): def scan_for_strings(self, section: Section):
elf = ELFFile(file) data = section.data()
strings = []
scary = { current = b""
"Process spawning": { for b in data:
"exec": 2, if 32 <= b <= 126: # printable ASCII
"execv": 2, current += bytes([b])
"execve": 2, else:
"execvp": 2, if len(current) >= 4:
"system": 2, strings.append(current.decode(errors="ignore"))
"popen": 2, current = b""
"fork": 2,
"vfork": 2,
"clone": 2,
},
"Tracing": { return strings
"ptrace": 1
},
"Networking": { def scan_for_scary_strings(self, string_list: list[str]):
"connect": 1, scores = {}
"socket": 1,
}
}
score = 0
dynsym = elf.get_section_by_name(".dynsym")
if dynsym:
for sym in dynsym.iter_symbols():
for reason, scary_names in scary.items(): for reason, scary_names in scary.items():
if sym.name in scary_names.keys(): scores[reason] = 0
score += scary_names[sym.name]
print(score) for sym in string_list:
if sym in scary_names.keys():
scores[reason] += 1
return score >= self.THRESHOLD if self.is_ip(sym):
scores["Reverse Shell"] += 1
return scores
def detect_function_calls(self, scores: dict, path: str):
r2 = r2pipe.open(path)
r2.cmd("aaa")
functions = r2.cmdj("aflj")
for func_name in [f["name"] for f in functions]:
for reason, scary_names in scary.items():
for key in scary_names.keys():
if func_name in key:
scores[reason] += 1
return scores
def sum_scores(self, a_dict: dict, b_dict: dict):
return {k: a_dict.get(k, 0) + b_dict.get(k, 0) for k in a_dict.keys() | b_dict.keys()}
def is_ip(self, ip_port: str):
try:
if ':' in ip_port:
ip, port = ip_port.split(':')
if not (0 <= int(port) <= 65535):
return False
else:
ip = ip_port
parts = ip.split('.')
return all(0 <= int(p) <= 255 for p in parts)
except ValueError:
return False
def subtract_score(self, scores: dict[str, int], score_name: str, amount: int):
if score_name == "all":
for key, value in scores.items():
scores[key] -= amount
else:
scores[score_name] -= amount
def run(self, contents: bytes, file, path: str):
try:
elf = ELFFile(file)
except ELFError:
return False
scores = {}
rodata = elf.get_section_by_name(".rodata")
if rodata:
scores = self.sum_scores(scores, self.scan_for_scary_strings(self.scan_for_strings(rodata)))
plt = elf.get_section_by_name(".plt")
if plt:
scores = self.sum_scores(scores, self.scan_for_scary_strings(self.scan_for_strings(plt)))
if b"glibc" in contents:
self.subtract_score(scores, "all", 1)
scores = self.detect_function_calls(scores, path)
for virus_type, score in scores.items():
if score > self.THRESHOLD:
return True
return False

View File

@@ -5,7 +5,7 @@ import os
class Yara(Detection): class Yara(Detection):
def run(self, contents: bytes, _) -> bool: def run(self, contents: bytes, _, __) -> bool:
rules = yara.compile(os.path.realpath(os.path.join(__file__, "../../yara_rules.yara"))) rules = yara.compile(os.path.realpath(os.path.join(__file__, "../../yara_rules.yara")))
matches = rules.match(data=contents) matches = rules.match(data=contents)

11
src/hashes.json Normal file
View File

@@ -0,0 +1,11 @@
{
"Linux.Gafgyt": {
"md5": "7c0c01dbf6b557b4b154d84254554ff3",
"sha1": "6cd2581525bfc1f484bd67c1b38a84eb52ad8751",
"sha256": "ff4816dd923e0c7d2806c9928ed29396133cc1f81ed40a47c8e748c366811448"
},
"Linux.Bew": {
"md5": "27d857e12b9be5d43f935b8cc86eaabf",
"sha256": "80c4d1a1ef433ac44c4fe72e6ca42395261fbca36eff243b07438263a1b1cf06"
}
}

View File

@@ -2,4 +2,4 @@ from scanner import Scanner
scanner = Scanner() scanner = Scanner()
scanner.scan_file("a.out") scanner.scan_file("virus")

View File

@@ -4,6 +4,8 @@ from console import console
from rich.table import Table from rich.table import Table
from time import time from time import time
import os
class Scanner: class Scanner:
def __init__(self): def __init__(self):
@@ -22,13 +24,11 @@ class Scanner:
table = Table("Scan", "Match") table = Table("Scan", "Match")
for name, detection in self.detections.items(): for name, detection in self.detections.items():
start_time = time() console.print(f"Running {name}..")
console.print(f"[d]Running {name}..", end='\r')
match = detection.run(contents, f) match = detection.run(contents, f, os.path.realpath(file_path))
table.add_row(name, "[bold orange1]⚠️ Match" if match else "[bold green]✅ Clean") table.add_row(name, "[bold orange1]⚠️ Match" if match else "[bold green]✅ Clean")
print()
console.print(f"Running {name}.. [d]({round(time()-start_time, 3)}s)", highlight=False)
f.close() f.close()

View File

@@ -1,12 +1,161 @@
rule test_virus {
rule wannacry_ransomware
{
meta: meta:
author = "Azure" author = "Azure"
description = "A test virus that isn't actually dangerous but in theory is really naughty." description = "This is a rule that tests against strings in WannaCry"
strings: strings:
$a = "virus" $b = "C:\\%s\\qeriuwjhrf"
$c = "WNcry@2017"
$d = "msg/m_bulgarian.wnry"
$e = "WanaCryptor"
condition:
3 of them
}
rule mimikatz_strings
{
meta:
author = "Azure"
description = "Detects Mimikatz usage"
strings:
$m1 = "sekurlsa::logonpasswords"
$m2 = "mimikatz"
$m3 = "lsadump::sam"
$m4 = "kerberos::tickets"
$m5 = "privilege::debug"
condition:
2 of them
}
rule generic_ransomware_extensions
{
meta:
author = "Azure"
description = "Detects ransomware file extensions"
strings:
$r1 = ".locked"
$r2 = ".encrypted"
$r3 = ".crypt"
$r4 = "restore_files"
$r5 = "readme.txt"
condition:
3 of them
}
rule lokibot_stealer
{
meta:
author = "Azure"
description = "Detects LokiBot stealer behavior"
strings:
$l1 = "pass.txt"
$l2 = "wallet.dat"
$l3 = "cookies.sqlite"
$l4 = "logins.json"
$l5 = "FileZilla"
condition:
3 of them
}
rule vmdetect
{
meta:
author = "nex"
description = "Possibly employs anti-virtualization techniques"
strings:
// Binary tricks
$vmware = {56 4D 58 68}
$virtualpc = {0F 3F 07 0B}
$ssexy = {66 0F 70 ?? ?? 66 0F DB ?? ?? ?? ?? ?? 66 0F DB ?? ?? ?? ?? ?? 66 0F EF}
$vmcheckdll = {45 C7 00 01}
$redpill = {0F 01 0D 00 00 00 00 C3}
// Random strings
$vmware1 = "VMXh"
$vmware2 = "Ven_VMware_" nocase
$vmware3 = "Prod_VMware_Virtual_" nocase
$vmware4 = "hgfs.sys" nocase
$vmware5 = "mhgfs.sys" nocase
$vmware6 = "prleth.sys" nocase
$vmware7 = "prlfs.sys" nocase
$vmware8 = "prlmouse.sys" nocase
$vmware9 = "prlvideo.sys" nocase
$vmware10 = "prl_pv32.sys" nocase
$vmware11 = "vpc-s3.sys" nocase
$vmware12 = "vmsrvc.sys" nocase
$vmware13 = "vmx86.sys" nocase
$vmware14 = "vmnet.sys" nocase
$vmware15 = "vmicheartbeat" nocase
$vmware16 = "vmicvss" nocase
$vmware17 = "vmicshutdown" nocase
$vmware18 = "vmicexchange" nocase
$vmware19 = "vmdebug" nocase
$vmware20 = "vmmouse" nocase
$vmware21 = "vmtools" nocase
$vmware22 = "VMMEMCTL" nocase
$vmware23 = "vmx86" nocase
$vmware24 = "vmware" nocase
$virtualpc1 = "vpcbus" nocase
$virtualpc2 = "vpc-s3" nocase
$virtualpc3 = "vpcuhub" nocase
$virtualpc4 = "msvmmouf" nocase
$xen1 = "xenevtchn" nocase
$xen2 = "xennet" nocase
$xen3 = "xennet6" nocase
$xen4 = "xensvc" nocase
$xen5 = "xenvdb" nocase
$xen6 = "XenVMM" nocase
$virtualbox1 = "VBoxHook.dll" nocase
$virtualbox2 = "VBoxService" nocase
$virtualbox3 = "VBoxTray" nocase
$virtualbox4 = "VBoxMouse" nocase
$virtualbox5 = "VBoxGuest" nocase
$virtualbox6 = "VBoxSF" nocase
$virtualbox7 = "VBoxGuestAdditions" nocase
$virtualbox8 = "VBOX HARDDISK" nocase
// MAC addresses
$vmware_mac_1a = "00-05-69"
$vmware_mac_1b = "00:05:69"
$vmware_mac_1c = "000569"
$vmware_mac_2a = "00-50-56"
$vmware_mac_2b = "00:50:56"
$vmware_mac_2c = "005056"
$vmware_mac_3a = "00-0C-29" nocase
$vmware_mac_3b = "00:0C:29" nocase
$vmware_mac_3c = "000C29" nocase
$vmware_mac_4a = "00-1C-14" nocase
$vmware_mac_4b = "00:1C:14" nocase
$vmware_mac_4c = "001C14" nocase
$virtualbox_mac_1a = "08-00-27"
$virtualbox_mac_1b = "08:00:27"
$virtualbox_mac_1c = "080027"
condition: condition:
any of them any of them
}
rule linux_bew
{
meta:
description = "Linux.Bew Backdoor"
author = "Joan Soriano / @w0lfvan"
strings:
$a = "src/secp256k1.c"
$b = "hfir.u230.org"
$c = "tempfile-x11session"
condition:
all of them
} }