Analisar a ShimCache: o formato binário do Windows 10 e 11

5 min de leitura

TL;DR. Primeiro u32 = tamanho do cabeçalho, tipicamente 0x30 a 0x36. Nesse offset cada entrada começa com a magic de quatro bytes 10ts, seguida por um cabeçalho de 12 bytes e um corpo de comprimento variável: caminho UTF-16LE (com prefixo de tamanho, sem terminador nulo), mtime em FILETIME e um blob de dados final. A flag de execução foi removida no Windows 10.

Cada lançamento do Windows desde o XP entregou uma disposição diferente da AppCompatCache. A variante Windows 10/11 é a que os investigadores veem na maior parte dos dias, e é a mais tolerante uma vez que se saiba onde estão as armadilhas. Este é o passo a passo que eu gostaria de ter tido quando tentei fazer um parser à mão pela primeira vez.

Disposição da entrada binária da ShimCache no Windows 10/11

O blob que vai analisar

Comece pelo valor binário em:

HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache\AppCompatCache

Extraia-o da hive SYSTEM (veja onde a ShimCache está armazenada). O que obtém é um único blob, tipicamente com dezenas a centenas de kilobytes. Pequeno cabeçalho, depois uma sequência de entradas, cada uma a começar com uma magic conhecida.

O cabeçalho

Os primeiros quatro bytes little-endian do blob são o offset da primeira entrada. No Windows 10/11 isto é normalmente 0x30, 0x34, 0x35 ou 0x36, consoante o build. Algumas hives trazem aqui 0x80, o que sinaliza uma disposição Windows 8.x que ainda usa a assinatura de entrada 10ts / 00ts.

Não fixe o tamanho do cabeçalho em código. Leia o primeiro u32 e valide que o byte nesse offset é o início de uma entrada reconhecida. Se nem 10ts nem 00ts aparecerem no offset declarado, varra para a frente à procura de 10ts e retome a partir daí. Hives reais entregam dados ligeiramente fora da especificação, e um parser que desiste à primeira incompatibilidade de byte é um parser que perde casos.

Uma entrada Windows 10/11, byte a byte

Cada entrada moderna começa com a assinatura de quatro bytes 10ts (0x31 0x30 0x74 0x73). Disposição completa:

OffsetTamanhoCampo
0x004Magic, "10ts"
0x044Sequência / desconhecido, frequentemente ignorado
0x084Tamanho dos dados da entrada, bytes a seguir a este campo
0x0C2Tamanho do caminho em bytes (UTF-16LE)
0x0ENBytes do caminho, UTF-16LE, sem terminador
0x0E + N8Hora da última modificação, FILETIME (100 ns desde 1601-01-01 UTC)
0x16 + N4Tamanho dos dados finais
0x1A + NMDados finais, normalmente vazios ou pequenos

O tamanho total da entrada é 0x0C + entry_data_size. A próxima entrada começa imediatamente a seguir.

Duas notas práticas:

  1. O caminho é UTF-16LE e não tem terminador nulo. Use o campo de tamanho do caminho. Não leia até atingir um zero; vai engolir o cabeçalho da entrada seguinte.
  2. O FILETIME é o mtime $STANDARD_INFORMATION do ficheiro. Não a hora em que o programa correu. Não a hora em que a entrada foi inserida. O mtime do ficheiro, copiado diretamente do NTFS no momento do exame. O artigo de prova de execução explica porque isso importa.

O que saiu no Windows 10

Versões anteriores do Windows acondicionavam uma flag Executed dentro de cada entrada. Analistas de Win7 e 8.x aprenderam a lê-la. O Windows 10 removeu-a. O Windows 11 não a repôs. Não há forma em cache de saber se um binário correu de facto. Precisa de Prefetch, AmCache, ou EVTX / Sysmon.

Para as disposições de campos antigas (XP até 8.1), veja formatos antigos da ShimCache.

Parsing na prática

Um parser que sobrevive a revisão:

  1. Valida a magic de quatro bytes no offset anunciado pelo cabeçalho.
  2. Itera entradas lendo o campo de tamanho dos dados da entrada e avançando.
  3. Descodifica o caminho como UTF-16LE usando o prefixo de tamanho.
  4. Converte o FILETIME para o formato de timestamp que as ferramentas a jusante preferem. Milissegundos Unix é conveniente. ISO 8601 UTC é o que os relatórios efetivamente querem.
  5. Expõe o rótulo de formato de OS detetado (Windows 10/11 vs 8.x vs 8.0) para filtragem a jusante.
  6. Reproduz SYSTEM.LOG1 e SYSTEM.LOG2 antes de ler o valor. Se o seu parser não fizer isto, está a ler uma hive desatualizada.

O Shimcache Parser open source implementa os passos 1 a 5 em Rust e entrega-os ao seu browser como WebAssembly. Lê um único ficheiro de hive e não reproduz os logs de transação, por isso passe primeiro uma hive dirty pelo rla.exe ou pelo Registry Explorer. Sem servidor, sem upload. Útil quando a hive SYSTEM veio de um ambiente regulado.

Leitura adicional

Artigos relacionados