Código-fonte no GitHub
Uma assinatura fixa um único ponto no espaço de codificações
Uma regra hexadecimal YARA é um predicado sintático sobre um buffer de bytes. O que ela tenta apanhar é um objeto semântico, por exemplo uma função que devolve 7. Entre esses dois planos está a propriedade que define o x86-64: uma codificação de comprimento variável em que a correspondência entre sequências de bytes e efeitos arquiteturais é massivamente de muitos para um. Colocar RAX a 7 e retornar admite, numa estimativa conservadora, mais de uma dúzia de codificações legais.
Uma assinatura fixa um ponto nesse espaço. Todo o trabalho de um mutador é deslocar-se para outro. Por isso, a suposição intuitiva — inserir um NOP, mudar o hash e o tamanho, e a regra de identidade fica contornada — mede no plano errado. A pergunta nunca é se alguns bytes se moveram. É se o ponto exato que a regra fixa ainda ocorre algures no buffer, e se a regra fixa um ponto ou uma família.
Trata-se de uma análise laboratorial em x86-64 para a avaliação autorizada de deteções, com o Aether 1.0.0 e o Zydis v4.1.1 como implementação de referência reprodutível. Não reivindica semântica arbitrária de .text, reescrita binária de uso geral nem o contorno de qualquer produto de segurança.
Seis bytes, um NOP, e a regra continua a acertar
A função de teste tem seis bytes: B8 07 00 00 00 C3, ou seja, mov eax, 7; ret. A regra didática corresponde exatamente a esses bytes, de forma contígua. É uma regra apenas para laboratório, não uma assinatura de produção; o seu valor está em tornar visível o plano de identidade.
A primeira captura SAFE com a seed 42 produziu 90 B8 07 00 00 00 C3: um NOP de um byte à frente do original inalterado. O hash mudou, o tamanho mudou, o valor de retorno mantém-se e a regra continua a corresponder, porque a agulha de seis bytes está intacta um byte mais à frente.
A razão é mecânica. O YARA reduz o padrão a átomos curtos, tipicamente substrings de até quatro bytes como 07 00 00 00, coloca-os num autómato Aho-Corasick e só executa o verificador completo quando há um acerto de átomo. O autómato é independente da posição, por isso um deslocamento de um byte não muda nada. O corolário: a força real de uma regra de identidade é a qualidade do seu átomo mais raro, não o seu comprimento. Usar curingas no imediato, como B8 ?? ?? ?? ?? C3, obriga o YARA a ancorar-se nos átomos de um byte B8 e C3, que saturam qualquer .text real e transformam a regra numa verificação lenta e de baixa precisão.
Portanto, a conclusão de que a inserção de enchimento contornou a assinatura não é aceitável sem testar a própria regra. Esta experiência mediu padding, não a destruição da regra.
{ B8 07 00 00 00 C3 } // contiguous identity Uma mudança de codificação, e a regra falha
A seed 5 segue outro caminho pela mesma política SAFE e emite 48 C7 C0 07 00 00 00 C3. A string original deixa de ocorrer porque a instrução mudou de classe de codificação: um prefixo REX cujo bit W seleciona um operando de 64 bits, o grupo de opcodes MOV C7 /0, um ModR/M de registo direto que seleciona RAX, o mesmo imediato 7 e ret. O resultado é, na prática, mov rax, 7; ret.
A afirmação tem um limite preciso. B8 estende com zeros o seu imediato de 32 bits para RAX, enquanto REX.W + C7 /0 o estende com sinal. Para o imediato positivo 7, ambas as formas produzem o mesmo valor de 64 bits. No entanto, o campo pure_rax do Aether é um oráculo do valor de retorno de 32 bits. Com um imediato como 0x80000000, as duas formas difeririam nos 32 bits superiores e o oráculo não o conseguiria ver. A afirmação semântica está deliberadamente restrita a esta forma com imediato positivo e ao domínio de saída de 32 bits declarado.
Também a seed, por si só, não determina a saída; uma captura publicada deve indicar um processo novo, o primeiro morph com seed e a build exata. Com essa disciplina, a comparação é limpa:
- Original B8 … C3: regra de identidade HIT, pure_rax 7
- Seed 42 (90 + original): regra de identidade HIT, pure_rax 7 → 7 OK
- Seed 5 (48 C7 C0 … C3): regra de identidade MISS, pure_rax 7 → 7 OK
O que torna o resultado credível
Isto não é um argumento de que a deteção é impossível. Uma regra que reconheça ambas as famílias de codificação pode continuar a detetar as duas. É um argumento de que um único fragmento de identidade é incompleto.
O Aether descodifica cada instrução com o Zydis e classifica-a como Control, Memory, Identity, PureRax ou outra classe; só as instruções classificadas entram num catálogo de substituições semanticamente delimitado, e ret nunca é diversificado como puro. Os bytes que o Zydis não consegue descodificar são mantidos como opacos, pelo que uma má descodificação reduz a superfície de transformação em vez de inventar uma reescrita. Cada saída é novamente descodificada e verificada por uma comparação pura do retorno de 32 bits, um simulador independente do host com pares fixos de múltiplas entradas e, em x86-64 ou no laboratório Docker amd64, execução nativa. Nos modos Lab ou Industry, uma discrepância pura resulta numa falha fechada.
O benchmark do laboratório do autor em Apple Silicon usou 5.096 entradas de corpus (1.000 sintéticas e 4.096 provenientes de ELF). Regista 10.192 morphs bem-sucedidos, 5.020 verificações puras e 4.862 verificações nativas sem qualquer rutura, e 94,89% de saídas únicas segundo SHA-256. Na matriz focada de 128 seeds, a regra de identidade de laboratório acertou 30 vezes (23,44%) e falhou 98 vezes (76,56%), sem ruturas puras. Estes são dados de captura para essa build e esse host, não números permanentes do produto.
As camadas que têm de concordar
A lição não é usar mais assinaturas, nem que o morphing ganha. É testar as regras no plano que realmente observam. O exemplo de seis bytes atravessa cinco camadas, e tratar qualquer uma delas como o sistema inteiro é a forma como um dump de bytes convincente se torna uma afirmação falsa. Uma nova descodificação estrutural não pode provar a semântica completa da máquina, um oráculo puro aprovado não pode provar que um EDR não gerou um alerta, e um MISS do YARA não pode provar que uma codificação está ausente de todas as famílias de deteção.
- Descodificação: que bytes e que grafo de fluxo de controlo descodificámos realmente?
- Análise: esta transformação é permitida pelo modelo atual?
- Lowering: os saltos ainda apontam para os blocos lógicos pretendidos?
- Oráculo: o contrato de saída declarado foi cumprido neste domínio?
- Deteção: que observável mudou, e que sensor ainda o vê?
Transformar a experiência em CI de deteção
O padrão útil: gerar gémeos semânticos legais dentro de um domínio declarado de forma restrita, executar a regra e o oráculo, guardar as evidências e depois reparar o sensor que falhou. Execute o job em pelo menos três planos: o oráculo semântico rejeita mutações que violam o contrato declarado, a identidade é registada como bytes, tamanho e hash exatos, e o detetor indicado é executado com a sua regra exata. Uma observação do ClamAV ou de um EDR é uma quarta medição separada, não um substituto do resultado YARA.
Guarde o código-fonte exato da regra YARA de laboratório, o relatório JSON do benchmark, as listagens descodificadas de antes e depois, e a versão e a data do detetor. Indique o oráculo em vez de escrever validado: o Apple Silicon consegue executar a simulação, mas não executar x86-64 de forma nativa, por isso use o laboratório Docker amd64 quando for necessária execução em hardware.
Quando uma regra falha, a reparação deve seguir o plano. Uma regra de bytes contíguos pode precisar de alternativas por família de codificação, de contexto ou de um segundo sinal; uma regra que cubra tanto B8 ?? … C3 como 48 C7 C0 ?? … C3 pode reconhecer ambas as formas. Um detetor de tamanho ou entropia pode já ver a expansão média de cerca de 2,19× e precisar apenas de calibração. Um sensor comportamental deve ser testado contra superfícies de execução, alocação, temporização e memória, em vez de ser inferido a partir de um hash estático.
Key Takeaways
Um NOP não quebrou a regra; uma codificação equivalente quebrou. A experiência demonstra uma fraqueza numa regra de identidade contígua, não uma falha universal da deteção, e só é válida dentro do seu domínio declarado: uma forma com imediato positivo, um oráculo de retorno de 32 bits, uma build específica e uma regra específica.
A resposta certa não é prometer invulnerabilidade, mas tornar os pressupostos de uma regra executáveis e cobertos por testes de regressão. O Aether destina-se a uso autorizado em laboratório ao abrigo da Aether Research License. O artigo completo no Medium contém cada medição e cada ressalva, e os documentos SCOPE, SCORECARD, LIBRARY e SECURITY do repositório devem ser lidos antes de tratar qualquer resultado como uma afirmação válida para implementação em produção.
Christian Schnapka
Macstab Engineering
Christian Schnapka desenvolve as ferramentas de engenharia abertas por trás da Macstab. Publica as versões completas destas investigações no Medium, com cada medição, cada ressalva e cada resultado negativo incluídos.