Se passar algum tempo a ler journals USN, a bitmask Reason torna-se memória muscular como os EventIDs do EVTX. É o campo de 32 bits que o NTFS define em cada USN_RECORD_V2 para resumir o que acabou de mudar num ficheiro. Os bits são aditivos, não mutuamente exclusivos, e isso é a primeira coisa a interiorizar: um único registo pode carregar FileCreate | DataExtend | Close e essa combinação significa algo diferente de qualquer destes bits isolados.
O que se segue é a referência de campo que mantenho num post-it ao lado do monitor.
Os bits, pela ordem por que os vai encontrar
| Flag | Hex | O que significa na prática |
|---|---|---|
DataOverwrite | 0x00000001 | Uma região do stream de dados principal foi sobrescrita no mesmo sítio |
DataExtend | 0x00000002 | O stream de dados principal cresceu |
DataTruncation | 0x00000004 | O stream de dados principal encolheu |
NamedDataOverwrite / Extend / Truncation | 0x10 / 0x20 / 0x40 | Os mesmos três, mas num fluxo de dados alternativo |
FileCreate | 0x00000100 | Foi criado um novo ficheiro ou directório |
FileDelete | 0x00000200 | O ficheiro foi desligado do namespace |
EaChange | 0x00000400 | Atributos estendidos foram alterados |
SecurityChange | 0x00000800 | ACLs/owner foram alterados |
RenameOldName | 0x00001000 | A metade «antes» de uma renomeação |
RenameNewName | 0x00002000 | A metade «depois» de uma renomeação |
IndexableChange | 0x00004000 | A flag indexable foi alternada |
BasicInfoChange | 0x00008000 | Timestamps, atributos ou flags de compressão reescritos |
HardLinkChange | 0x00010000 | Um hard link foi adicionado ou removido |
CompressionChange | 0x00020000 | Compressão NTFS alternada |
EncryptionChange | 0x00040000 | Estado EFS alterado |
ObjectIdChange | 0x00080000 | Object ID definido ou limpo |
ReparsePointChange | 0x00100000 | Reparse point adicionado/alterado/removido |
StreamChange | 0x00200000 | Um fluxo de dados alternativo foi adicionado, renomeado ou eliminado |
Close | 0x80000000 | O handle que produziu a mudança foi libertado |
O bit Close merece um parágrafo à parte, porque interpretá-lo mal lança a sua contagem de eventos por uma ordem de magnitude. O NTFS coalesce operações sucessivas sob o mesmo handle e emite registos intermédios à medida que faz flush, depois um registo de resumo final com Close definido quando o handle desaparece. Os registos intermédios são reais, mas duplicam a contagem. Quando faz triagem a um journal parseado, filtre primeiro pelos registos com Close para desduplicar operações a uma linha por handle. Puxe os registos não-Close apenas quando precisa de granularidade sub-handle.
Padrões que ocorrem com frequência suficiente para memorizar
Algumas combinações de razões aparecem tão frequentemente em investigações que se deixa de lê-las carácter a carácter para se ler como palavras.
Ficheiro novo escrito por uma aplicação. FileCreate | DataExtend e depois mais registos DataExtend | Close à medida que o ficheiro cresce, terminados por BasicInfoChange | Close. O último registo é o ficheiro a ter o mtime carimbado no fecho.
Renomeação entre directorias. Dois registos a partilhar o mesmo FileReferenceNumber: RenameOldName | Close e depois RenameNewName | Close. A referência do pai difere entre os dois — esse delta é como reconstrói o movimento. Se só filtrar por um dos bits de rename perde metade da história.
Save-by-rename atómico. Office, VS Code, Notepad++, vim com backup, quase todos os editores modernos. Vê um FileCreate num ficheiro temporário (~$report.docx, report.docx.tmp, .swp, etc.), um ou mais registos DataExtend, um FileDelete | Close no original, depois RenameNewName | Close no temporário a apontar para o nome do original. A sequência completa acontece dentro de um único segundo de relógio.
Cifrar-depois-renomear de ransomware. Registos DataOverwrite sustentados a cobrir megabytes por ficheiro, seguidos de pares RenameOldName / RenameNewName com um sufixo uniforme ou extensão aleatória de comprimento fixo. A taxa de DataOverwrite e o cluster de mesma extensão nas renomeações são as duas metades do diagnóstico. O artigo de detecção de ransomware é o playbook.
Quarentena de antivírus. Um FileDelete | Close imediatamente a seguir a um FileCreate recente na mesma entrada MFT, muitas vezes com um SecurityChange no meio à medida que o agente AV move o ficheiro para um directório restrito. O journal preserva prova de que o AV tocou no ficheiro mesmo depois do binário em si ter desaparecido.
Upload por sincronização na cloud. RenameNewName | Close cujo novo pai é \Users\<u>\OneDrive\, \Dropbox\ ou \Google Drive\ e cujo pai antigo é o perfil do utilizador. O próprio agente produz registos DataExtend | Close em ficheiros dentro da pasta de sincronização para downloads — uploads tendem a parecer renomeações para dentro da pasta.
Timestomping. Um BasicInfoChange | Close isolado sem DataOverwrite, DataExtend, FileCreate ou rename em redor no mesmo FileReferenceNumber. Um touch legítimo é emparelhado com a escrita que despoletou a actualização do timestamp. O artigo sobre timestomping explora isto em pormenor.
Injecção em stream / abuso de ADS. StreamChange por si só geralmente indica um Zone.Identifier de um download de browser (emparelhado com o FileCreate do ficheiro original). Registos StreamChange repetidos no mesmo FileReferenceNumber longe de Downloads\, particularmente em binários de sistema assinados, merecem investigação.
O que os bits nunca lhe vão dizer
O campo Reason é sobre o ficheiro, não sobre o actor. Não há utilizador, nem PID, nem linha de comandos, nem nível de integridade. Anexar um actor a uma operação implica sempre ir a outro lado: Security.evtx 4663 (acesso a objecto, apenas se houvesse SACLs configurados, o que geralmente não havia), evento 11 do Sysmon (criação de ficheiro) e evento 1 (criação de processo com linha de comandos), ou evidência de execução de processo e entradas de Shimcache que põem um binário em disco no momento certo.
Os bits também não lhe dizem sobre conteúdo. O journal sabe que uma região foi sobrescrita, mas não que bytes lá estavam antes. Para isso precisa de before-images do $LogFile ou de uma Volume Shadow Copy.
Uma receita de leitura que funciona
Quando abro um journal parseado a frio:
- Filtre primeiro por
FileCreate. O que é genuinamente novo na janela? Procure caminhos em directorias graváveis pelo utilizador (\AppData\Local\Temp\,\Users\Public\,\ProgramData\fora de subdirectórios conhecidos de fabricante,\PerfLogs\). - Passe a
RenameNewName | Closee agrupe pela nova extensão ou pelo novo pai. Save-by-rename, movimentações por sincronização na cloud e sufixos de ransomware aparecem todos aqui. - Agrupe
DataExtendpor minuto. Escritas em massa — backups, cifragem, crescimento de arquivos de exfiltração — aparecem como picos sustentados contra uma baseline de resto silenciosa. - Leia primeiro os registos com
Close. Trate os intermédios como evidência de apoio, não eventos independentes. - Quando tiver um ficheiro candidato, agrupe todos os registos no mesmo
FileReferenceNumbere leia o ciclo de vida completo por ordem cronológica. Essa sequência é quase sempre a parte que vai para o relatório.
O parser desta página expõe directamente o filtro por razão, pelo que os passos 1 a 3 são alguns cliques. Para o passo 5, ordene por FileReferenceNumber e depois por USN.
Leituras adicionais
- Microsoft Learn — a referência USN_RECORD_V2 cobre cada definição de bit directamente da fonte.
- MFTECmd do Eric Zimmerman — o seu parser de
$Jmostra a bitmask Reason num CSV plano que aguenta pivots em folha de cálculo. - usnrs da Airbus CERT — a implementação open-source em Rust; ler
src/reason.rsé o caminho mais curto para interiorizar o layout dos bits.