Um NOP não quebrou a minha regra YARA. Uma única mudança de codificação quebrou.
Segurança Engenharia 9 de agosto de 2026

Um NOP não quebrou a minha regra YARA. Uma única mudança de codificação quebrou.

Uma investigação ao nível do byte para engenheiros de deteção: porque inserir um NOP deixa intacta uma regra hexadecimal YARA, enquanto uma única mudança de codificação x86-64 equivalente a contorna — reproduzido com o Aether 1.0.0 como implementação de referência.

C
Christian Schnapka
Macstab Engineering
6 min de leitura
Também no Medium Ler no Medium

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.

Diagrama que contrasta o plano de identidade (SHA-256, tamanho, dump hexadecimal, correspondência de strings YARA) com o plano semântico (oráculo multi-entrada, simulador, execução nativa)
Plano de identidade versus plano semântico: uma assinatura de bytes e um oráculo comportamental observam coisas diferentes.

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.

yara
{ 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:

Diagrama da codificação original B8 que se ramifica na variante NOP da seed 42 (a regra acerta) e na variante REX da seed 5 (a regra falha), ambas com RAX puro igual a 7
O mesmo resultado puro, outra classe de codificação: a variante com NOP mantém a agulha intacta, a forma REX remove-a.
  • 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.

Fluxograma do pipeline MorphEngine do Aether: lift, analyze, ramificação por modo de produto, diversify, expand, shuffle, split, permute, assemble e verify
O pipeline MorphEngine: cada variante emitida passa por nova descodificação e verificação antes de contar.

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.

Topologia do laboratório: host do autor que compila o Aether com o Zydis v4.1.1, um contentor Linux amd64 para a prova nativa e um contentor de análise opcional com ClamAV e YARA
Topologia do laboratório: prova nativa num contentor amd64, medições dos detetores num contentor de análise separado.

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.

#detection-engineering #yara #x86-64 #reverse-engineering #malware-analysis
C

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.