
Carregador de dylib Mach-O em memória para Python padrão do macOS; descriptografa, mapeia e executa payloads sem usar dlopen ou gravar em disco, com incorporação opcional de arquivo de cobertura criptografado.
dyen carrega um dylib Mach-O em memória usando o Python padrão do macOS e chama um ponto de entrada exportado sem dlopen nesse dylib. O objetivo é que a imagem nunca seja um arquivo isolado no alvo e nunca seja registrada no dyld.
por cenobyte [email protected] 2026
https://github.com/cenobyte-vincit/dyen
O dyen é um carregador Mach-O em memória apenas com stdlib (loader.py) para o /usr/bin/python3 padrão do macOS. Ele lê um dylib simples, um blob DYEN v1 autônomo (o envelope criptografado) ou um arquivo de cobertura com esse blob anexado ao EOF, mapeia a fatia do host com mmap anônimo, aplica os fixups clássicos de rebase/bind e chama dyld_main por meio de um ponteiro de função ctypes.
encrypt_dyld.py é a ferramenta de preparação: ele envolve um dylib como um blob DYEN na saída padrão ou copia um arquivo de cobertura para com o blob anexado. O payload incluído apenas imprime . é exclusivo para macOS; o criptografador roda em Linux ou macOS.
dyld_<basename>dyld/dyld.chello worldloader.pyO carregador incluído lê um caminho local ou stdin. Um dropper ou estágio 1 buscaria o arquivo de cobertura (ou os bytes brutos) pela rede e os passaria para read_dyld_bytes(), de modo que o dylib criptografado nunca seja gravado como um arquivo próprio no alvo.
.dyen ou arquivo de cobertura.hello world). Diagnósticos vão para o stderr./usr/bin/python3 padrão (apenas stdlib)loader.py é exclusivo para macOS. Compilar o dylib de PoC exige o clang da Apple em um Mac. encrypt_dyld.py roda em Linux ou macOS com um dylib pré-compilado.
clang e make (para compilar dyld/dyld.c)encrypt_dyld.pyruff, pylint, cppcheck (brew install ruff pylint cppcheck)make
Isso compila build/libdyld.dylib (universal x86_64 + arm64), grava build/libdyld.dyen e incorpora o mesmo dylib criptografado em pdf/dyld_c4611_sample_explain.pdf. O PDF gerado não é versionado; make o grava e make clean o exclui. O arquivo de cobertura de origem é pdf/c4611_sample_explain.pdf.
O dylib simples é um artefato intermediário. Em tempo de execução, o carregador descriptografa um blob DYEN antes do mapeamento, seja de um arquivo .dyen autônomo ou de um arquivo de cobertura com o dyld criptografado anexado ao final.
# Blob DYEN autônomo para a saída padrão (o Makefile redireciona para build/libdyld.dyen)
python3 encrypt_dyld.py build/libdyld.dylib CHANGEMEASAP > build/libdyld.dyen
# Esconde o dyld criptografado dentro de um arquivo de cobertura -> pdf/dyld_c4611_sample_explain.pdf
python3 encrypt_dyld.py build/libdyld.dylib CHANGEMEASAP pdf/c4611_sample_explain.pdf
# Usa o número de série deste Mac como chave de criptografia em vez de uma senha (teste local)
python3 encrypt_dyld.py build/libdyld.dylib "$(python3 loader.py --serial)" > build/libdyld.dyen
# Usa o número de série do Mac alvo como chave de criptografia (número de série fictício)
python3 encrypt_dyld.py build/libdyld.dylib C07XH4V2MD6T > build/libdyld.dyen
encrypt_dyld.py roda em Linux ou macOS. loader.py roda apenas em macOS.
A CLI do carregador aceita um caminho de arquivo ou lê de stdin. Ela não tem uma opção de senha. A descriptografia tenta primeiro o serial de hardware deste Mac e depois DEFAULT_DYLD_KEY (CHANGEMEASAP). Se o serial não puder ser lido, apenas a chave fixa no código é tentada. A CLI sempre carrega a entrada e chama dyld_main. Copie loader.py para o alvo.
/usr/bin/python3 loader.py build/libdyld.dylib
/usr/bin/python3 loader.py pdf/dyld_c4611_sample_explain.pdf
/usr/bin/python3 loader.py build/libdyld.dyen
cat build/libdyld.dylib | /usr/bin/python3 loader.py
cat pdf/dyld_c4611_sample_explain.pdf | /usr/bin/python3 loader.py
cat build/libdyld.dyen | /usr/bin/python3 loader.py
Um arquivo de cobertura sem o blob anexado falha:
/usr/bin/python3 loader.py pdf/c4611_sample_explain.pdf
loader.py --serial imprime o serial de hardware deste Mac (apenas macOS):
python3 loader.py --serial
Defina LOADER_DEBUG com qualquer valor para obter rastreamentos detalhados no stderr:
LOADER_DEBUG=1 /usr/bin/python3 loader.py build/libdyld.dyen
loader.py e encrypt_dyld.py:
| Código | Significado |
|---|---|
| 0 | Sucesso |
| 1 | Erro de uso, falha na descriptografia, serial ausente ou erro de E/S |
LOADER_DEBUG (qualquer valor) imprime rastreamentos detalhados no stderr: origem da entrada, verificações de cabeçalho/magic, detecção de blob DYEN, descriptografia, map/bind, resolução de símbolos. make passa LOADER_DEBUG=1 para encrypt_dyld.py por padrão; make LOADER_DEBUG= silencia os rastreamentos da etapa de criptografia.
DEFAULT_DYLD_KEY em loader.py é CHANGEMEASAP. A criptografia exige uma senha explícita ou o argumento de serial do Mac para encrypt_dyld.py.
A contagem de iterações (PBKDF2_ITERATIONS em loader.py) segue a recomendação atual da Folha de Referência de Armazenamento de Senhas da OWASP para PBKDF2-HMAC-SHA256 (600.000 iterações). O PKCS #5 / RFC 8018 define o PBKDF2, mas deixa a contagem de iterações a cargo de quem implementa; o NIST SP 800-63B permite PBKDF2 sem exigir um número específico. A OWASP publica alvos práticos que aumentam com o tempo à medida que o hardware fica mais rápido; portanto, esse valor deve ser revisado periodicamente. É uma orientação de fortalecimento de senha aplicada aqui à chave de teste, não uma constante permanente do protocolo.
Host de compilação:
make test
make lint # cppcheck (C) + ruff + pylint (Python)
make check # cppcheck only
make lint-python # ruff + pylint
make test executa os linters, compila o dylib de PoC, criptografa-o (.dyen autônomo e incorporação em PDF) e executa os testes de integração sob /usr/bin/python3. Cada teste de carregamento verifica se o stdout contém hello world. Esses testes rodam em um Mac no mesmo local. Eles não são uma prova de execução limpa.
O CrowdStrike Falcon para macOS detecta essa técnica. Outros EDRs não foram testados.
O carregador implementa apenas rebase/bind clássicos de LC_DYLD_INFO. Ele não trata chained fixups, Objective-C ou Swift. dlopen é usado apenas para dependências do sistema assinadas pela Apple (por exemplo, /usr/lib/libSystem.B.dylib). O payload nunca é registrado no dyld.
A ocultação por anexação ao EOF é visível em um hex dump.
Armamento: altere DYENCRYPT antes do uso operacional. A magic de PoC em DYLD_BLOB_MAGIC (loader.py) é uma assinatura estática. Regras YARA, scanners de arquivo e heurísticas simples de rfind podem usá-la como chave. Substitua-a por um valor exclusivo de sua escolha (mantenha o criptografador e o carregador sincronizados; o tamanho não é fixo, mas mais longo é melhor para arquivos de cobertura incorporados).
O dylib incluído apenas imprime hello world.