phoreal/phoreal.py (51 lines of code) (raw):
import logging
from malduck.extractor import Extractor
from malduck.procmem import ProcessMemory
from malduck.yara import YaraRuleMatch
from ..utils import Config, get_rule_metadata
logger = logging.getLogger(__name__)
class Phoreal(Extractor):
family = "phoreal"
yara_rules = ("phoreal",)
overrides = []
@Extractor.rule
def phoreal(self, p: ProcessMemory, match: YaraRuleMatch) -> Config | bool:
_info: Config = get_rule_metadata(match)
return _info
@Extractor.extractor("rcdata")
def PHOREAL_RC4_key(self, p: ProcessMemory, addr: int):
from malduck import rc4
key_size = 17 # apparently hardcoded in phoreal
key_addr = addr + 6 # offset from start of string
ct_addr = key_addr + 17
max_size = 200
key = bytearray()
ct = bytearray()
logger.info("[+] Found RC4 passphrase @ %X" % key_addr)
for offset in range(0, key_size):
o = p.uint8v(key_addr + offset)
if o:
key.append(o)
logger.info("[+] RC4 passphrase: " + key.hex())
nc = 0 # null counter
for offset in range(0, max_size):
o = p.uint8v(ct_addr + offset)
if o:
ct.append(o)
if o == 0:
nc += 1
else:
nc = 0
if nc == 2:
break
logger.info("[+] RC4 cyphertext: " + ct.hex())
rc4_decode = rc4(key, ct)
logger.info("[+] RC4 decrypted plaintext: %s" % str(rc4_decode))
domains = str(rc4_decode).split(";")
domains[0] = domains[0].split("'")[1]
del domains[-1]
conf = {
"rc4_key": key.hex(),
self.family: {"plaintext": str(rc4_decode), "domains": domains},
}
return conf