FileId de Amcache explicado: el formato de hash SHA-1 que almacena Windows
FileId en Root\InventoryApplicationFile es uno de los campos más útiles de toda la hive, y uno de los más malentendidos. Es un hash de contenido. No es del todo un SHA-1 estándar. No hashea del todo el archivo entero. Esta página es la referencia completa.
Para el contexto más amplio ve la Referencia completa de Amcache. Para la estructura del Registro que rodea esto dentro de la hive, ve Estructura del Registro de Amcache.
Cómo se ve el valor#
Un FileId típico de una hive real:
0000da39a3ee5e6b4b0d3255bfef95601890afd80709
41 caracteres en total:
- Los cuatro primeros siempre son
"0000". Tag de tipo fijo. - Los 40 restantes son el digest hex del SHA-1.
El prefijo "0000" es un artefacto histórico. Los builds tempranos del appraiser preveían múltiples algoritmos de hash con prefijos distintos. Solo llegó a usarse SHA-1. El prefijo lleva años siendo constante.
AmcacheParser separa esto en dos columnas CSV:
| Columna | Valor |
|---|---|
FileId |
String completo de 41 caracteres con prefijo. |
Hash |
40 caracteres hex, SHA-1 solo. |
Usa siempre Hash (o quita el prefijo tú) al hacer join contra feeds externos. VirusTotal, feeds de TI, bases de datos de allowlist todos quieren SHA-1 de 40 caracteres. Te van a fallar el match silenciosamente si incluyes el "0000".
Qué hashea de verdad#
La trampa que pilla a casi cualquier analista nuevo:
El SHA-1 hashea los primeros 31 MiB del archivo, no el archivo entero.
Para archivos más pequeños de 31 MiB (que es la mayoría de EXEs y DLLs), el hash de prefijo coincide con el SHA-1 del archivo completo. Indistinguibles.
Para archivos mayores, el valor de Amcache es un hash de prefijo. Sigue siendo lo bastante distintivo para identificar un build concreto de un binario concreto. Pero no es lo que sha1sum te daría sobre el archivo entero.
Por qué importa#
- Matches de VirusTotal. Por debajo de 31 MiB, el SHA-1 de Amcache coincide con el SHA-1 que VT indexa. Archivos más grandes (instaladores, algunos binarios de juegos, software empresarial grande) a menudo no coinciden, y una respuesta "no hay registro" de VT no significa nada útil.
- Bases de datos de hashes propias. Si mantienes una allowlist interna, guarda el mismo tipo de hash contra el que vas a comparar. O guarda SHA-1 de contenido completo (y acepta mismatches en binarios grandes) o mantén una columna paralela de hash de prefijo.
- Verificación. Si tienes el binario original y quieres verificar, hashea solo los primeros 31 MiB:
import hashlib
PREFIX_BYTES = 31 * 1024 * 1024
def amcache_sha1(path: str) -> str:
h = hashlib.sha1()
with open(path, 'rb') as f:
h.update(f.read(PREFIX_BYTES))
return h.hexdigest()Trampas del mundo real#
Un puñado de tropezones que aparecen en casos reales.
No incluyas el prefijo en los lookups#
# Mal
search_virustotal('0000da39a3ee5e6b4b0d3255bfef95601890afd80709')
# Bien
search_virustotal('da39a3ee5e6b4b0d3255bfef95601890afd80709')La API de VirusTotal espera el hash pelado. Incluir el prefijo devuelve un resultado vacío silenciosamente, que parece idéntico a "este archivo es desconocido". Eso desperdicia tiempo de triage y produce hallazgos equivocados.
Las colisiones de SHA-1 son teóricas pero no imposibles#
Existen ataques reales de colisión de SHA-1. En contexto no adversarial es irrelevante. Encontrar una colisión contra una entrada concreta de Amcache es absurdamente desproporcionado para lo que el atacante gana. Pero para matching de alta confianza en una investigación de alto riesgo, no trates un match de SHA-1 como identidad criptográfica. Combina con tamaño de archivo, link date y al menos un campo más.
No te fíes del FileId de una fila no-PE#
Amcache registra ocasionalmente valores FileId para archivos no-PE que el appraiser vio. El hash sigue siendo real, pero las herramientas posteriores que asumen contexto PE (búsquedas PE-aware de VirusTotal, reglas Yara contra bytes PE) devuelven resultados menos útiles.
Varias filas, mismo hash#
Si encuentras el mismo Hash en varias filas de *_UnassociatedFileEntries.csv en el mismo host, eso es significativo. El mismo contenido binario se inventarió en varias rutas. Razones habituales:
- El atacante copió la herramienta a varias ubicaciones para probar cuál se ejecuta.
- Un instalador legítimo dejó la misma DLL en varios directorios de producto.
- Un usuario copió un archivo a mano.
Agrupa por Hash, luego mira FullPath y KeyLastWriteTimestamp de cada instancia. Los timestamps cuentan la secuencia. Las rutas cuentan la intención.
Varios hashes, mismo FullPath#
El patrón opuesto. Misma ruta, distinto Hash en varias filas significa que el binario en esa ruta cambió entre inventarios. Señal fuerte de reemplazo:
- Legítimo: actualización de software sobrescribió el archivo.
- Sospechoso: el atacante intercambió un binario del sistema o una herramienta de usuario habitual por una copia troyanizada.
Ordena las filas por KeyLastWriteTimestamp para ver cuándo apareció cada hash nuevo. Correla con eventos de parche o eventos Sysmon File Create alrededor de esos momentos.
Pivotes que se ganan su sitio#
Caza de hash entre hosts#
# Pivotar un SHA-1 conocido-malo entre las CSVs de Amcache de cada host
$badHash = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'
Get-ChildItem -Recurse -Filter *_UnassociatedFileEntries.csv |
ForEach-Object {
Import-Csv $_.FullName |
Where-Object { $_.Hash -eq $badHash } |
Select @{n='Host';e={$_.PSChildName.Split('_')[0]}},
FullPath, KeyLastWriteTimestamp, Size
} |
Sort-Object HostAsí pasas de "encontramos este hash en un host" a "todos los hosts del parque que han tenido alguna vez este binario".
Enriquecimiento con VirusTotal#
import csv, requests, time
API = 'https://www.virustotal.com/api/v3/files/'
HEADERS = {'x-apikey': '<your-key>'}
seen = set()
with open('HOST_amcache_UnassociatedFileEntries.csv', newline='') as f:
for row in csv.DictReader(f):
h = row['Hash']
if not h or h in seen:
continue
seen.add(h)
r = requests.get(API + h, headers=HEADERS)
if r.status_code == 200:
stats = r.json()['data']['attributes']['last_analysis_stats']
if stats.get('malicious', 0) > 0:
print(h, stats, row['FullPath'])
time.sleep(15) # rate-limit de la API pública de VTIncluso a bajo volumen contra la API pública saca una lista compacta de hashes confirmados-malos en un host infectado típico.
Correlación con Sysmon Image Loaded#
El Event ID 7 de Sysmon registra el SHA-1 de cada DLL cargada por cada proceso. Unir el Hash de Amcache contra el campo Hashes de Sysmon 7 te dice exactamente qué procesos cargaron una DLL del atacante concreta, y cuándo.
Lecturas adicionales#
- Documentación de la API de VirusTotal para el endpoint de lookup de hash v3.
- Deep dive de Amcache.hve de Yogesh Khatri.
- El proyecto HashLookup de CIRCL.
Relacionados#
Artículos relacionados
- ¿Qué es Amcache FileId? (glosario)
FileId es '0000' más el SHA-1 de los primeros 31 MiB del archivo. Quita el prefijo antes de tirar a VirusTotal o no te devolverá nada en silencio.
- Dónde está Amcache.hve en disco (y cómo recogerlo)
Ruta: C:\Windows\AppCompat\Programs\Amcache.hve más los archivos .LOG. Siempre los tres. Y la forma correcta de recogerlo en un host vivo y desde shadow copies.
- Recuperar evidencia de binarios borrados desde Amcache
Amcache sobrevive a los binarios que registra. La ruta, el SHA-1, el publisher, el link date y el momento del inventario persisten meses después de que el atacante limpie el archivo. El flujo práctico.
- Amcache: la referencia forense completa de la hive Windows .hve
Amcache es la hive donde el Compatibility Appraiser anota cada PE en disco, con SHA-1, ruta completa, publisher, link date y un tiempo de escritura de clave. La referencia, del formato al uso investigativo.