-
-
Save LloydLabs/d4e0ffba3ba6ccce17fafc08d9118385 to your computer and use it in GitHub Desktop.
import sys | |
from typing import Dict, List | |
import malduck | |
import pefile | |
import yara | |
from capstone import CS_ARCH_X86, CS_MODE_64, Cs | |
from capstone.x86 import X86_INS_RET | |
from qiling import * | |
from qiling.const import QL_VERBOSE | |
from unicorn.x86_const import UC_X86_REG_RCX, UC_X86_REG_RDX | |
def _emotet_end_emu(ql: Qiling, address: int, size: int) -> None: | |
buf = ql.mem.read(address, size) | |
md = Cs(CS_ARCH_X86, CS_MODE_64) | |
md.detail = True | |
for _ in filter(lambda x: x.id == X86_INS_RET, md.disasm(buf, address)): | |
ql.stop() | |
def _emotet_get_c2_pair(addr: int) -> Dict: | |
ip_buf = ql.mem.map_anywhere(0x4) | |
port_buf = ql.mem.map_anywhere(0x4) | |
ql.reg.write(UC_X86_REG_RCX, ip_buf) | |
ql.reg.write(UC_X86_REG_RDX, port_buf) | |
ql.hook_code(_emotet_end_emu) | |
ql.run(begin=addr) | |
c2_port = malduck.u16(ql.mem.read(port_buf, 4), offset=2) | |
c2_ip = malduck.ipv4(bytes(ql.mem.read(ip_buf, 4))) | |
if c2_ip == "0.0.0.0": | |
return | |
return {"ip": c2_ip, "port": c2_port} | |
def _emotet_get_c2s(ql: Qiling, path: str) -> List: | |
with open(path, 'rb') as f: | |
data = f.read() | |
addresses = [] | |
pe = pefile.PE(data=data) | |
rule = yara.compile(source="rule load {strings: $load = {48 8D 05 ?? ?? ?? ?? 48 89 81 ?? ?? 00} condition: $load}") | |
matches = rule.match(data=data) | |
for off, _, _ in matches[0].strings: | |
addr = ((pe.get_rva_from_offset(off) + malduck.int32(data, offset=off + 3)) + 7) + pe.OPTIONAL_HEADER.ImageBase | |
if pair := _emotet_get_c2_pair(addr): | |
addresses.append(pair) | |
return addresses | |
if __name__ == "__main__": | |
if len(sys.argv) != 2: | |
print("emo_x64_dump.py <name>") | |
sys.exit(0) | |
ql = Qiling([sys.argv[1]], "rootfs/x8664_windows", verbose=QL_VERBOSE.DISABLED) | |
print(_emotet_get_c2s(ql, sys.argv[1])) |
LloydLabs
commented
May 22, 2022
Hi there, thanks for the interesting work!
I tried this on a couple of new samples I classify as Emotet via other means, but keep getting a UC_ERR_MAP
... since I'm a sw developer and not a malware reverse engineer, I'm not sure what's not working 🤷♂️
But just in case it is useful for your research or to improve this script, here are just a couple of the samples I'm referring to:
76c1be451fa4affc1c20e50522c2343d8c91158ec481278c2478ff57cf2c347d
87c362c7e55d295c94e278b4652f83b969dd6ca2fdabe357db70aebcc04f70c9
f80e678bb0e79ba50c9134a8dbed95d63ae52b45300a23c64a2f95db1a0d7da2
964e9baad6c1239ae5032baedf858b8c9705dc4976618e8e68d05c76f41e13fd
d84b0313af90541eadcab64b3dac0f7016ff75922d4bad733492d614ac8192f8
Hey @kuax! At initial triage of these samples, they are packed versions of Emotet. To run the extractor, you'll need to unpack them.
For the first sample, you can just put a breakpoint on VirtualAlloc
within DllMain
and break on when it's called further down in the function.
I see, thanks for the feedback! In my analysis I could clearly see those were Emotet, but was surprised none of the usual sandboxes seemed to have picked it up (at least on VT)...
Another very interesting example is e17d5db1856c406f8fa55b0ec282959cf8eda1c9b697f85d47b329d4d80f4cea
with only 9 detections on VT, but again it seems to me that this is clearly Emotet. I wish I had the RE skills to understand why it doesn't get picked up, what does it do so differently etc 🥲🤷🏻♂️