Skip to content

Instantly share code, notes, and snippets.

@LloydLabs
Last active March 4, 2024 19:28
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LloydLabs/d4e0ffba3ba6ccce17fafc08d9118385 to your computer and use it in GitHub Desktop.
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
Copy link
Author

capstone==4.0.2
cffi==1.15.0
click==8.1.3
colorama==0.4.4
cryptography==37.0.2
dill==0.3.5.1
enum-compat==0.0.3
future==0.18.2
gevent==21.12.0
greenlet==1.1.2
keystone-engine==0.9.2
malduck==4.2.0
multiprocess==0.70.12.2
pefile==2019.4.18
pycparser==2.21
pycryptodomex==3.14.1
pyelftools==0.28
python-registry==1.3.1
PyYAML==6.0
qiling==1.4.2
typing_extensions==4.2.0
unicodecsv==0.14.1
unicorn==2.0.0rc7
yara-python==4.2.0
zope.event==4.5.0
zope.interface==5.4.0

@kuax
Copy link

kuax commented May 24, 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

@LloydLabs
Copy link
Author

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.

@kuax
Copy link

kuax commented May 25, 2022

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 🥲🤷🏻‍♂️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment