Amcache ProgramId explicado: la identidad de aplicación de 44 caracteres

El campo ProgramId en Root\InventoryApplicationFile y Root\InventoryApplication es la identidad lógica de aplicación de Amcache. Es una cadena hex de 44 caracteres que identifica de forma única una aplicación — y, lo más importante, es determinista: la misma aplicación en un host diferente típicamente obtiene el mismo ProgramId. Eso lo convierte en uno de los pivotes cross-host más útiles de todo el toolkit DFIR de Windows.

Esta página es la referencia completa: qué es el valor, cómo lo construye Windows, cómo usarlo en una investigación de un solo host y cómo pivotarlo a través de un entorno en un hunt.

Para el contexto más amplio de Amcache, ver la referencia completa de Amcache; para la estructura del registro circundante, ver Estructura del registro Amcache.


Cómo se ve el valor#

Un ProgramId típico de un hive real:

0006fa0b2a9f8a4eb9d7c81e8b1f3c5d3e2a0000ffff

44 caracteres hexadecimales, sin prefijo, sin delimitadores. Cada entrada en InventoryApplicationFile lleva uno; cada entrada en InventoryApplication está indexada por uno. Los dos enlazan mediante el valor compartido: el ProgramId de un archivo te dice a qué aplicación instalada pertenece.

Estructura de los 44 caracteres#

El valor no es un único hash uniforme. Codifica varios componentes, de forma aproximada:

  • Una etiqueta de tipo / versión (los primeros caracteres).
  • Un hash de los atributos identificadores de la aplicación — principalmente nombre, publisher, versión e idioma.
  • Un discriminador final (a menudo 0000ffff o similar) usado por Windows para clasificación interna.

La construcción exacta ha cambiado a lo largo de los builds de Windows y no está completamente documentada por Microsoft. Para fines DFIR las reglas prácticas son:

  • Dos registros con el mismo ProgramId son sobre la misma aplicación.
  • El ProgramId es estable entre hosts para la misma instalación de aplicación — Office 365 en el host A y el mismo build de Office 365 en el host B comparten un ProgramId.
  • El ProgramId no es estable a través de upgrades de versión mayor — actualizar la aplicación típicamente cambia su ProgramId.

Usar ProgramId en un solo host#

El uso estrella es unir un archivo a su registro de aplicación.

Archivo → aplicación#

Encontraste una fila sospechosa en *_UnassociatedFileEntries.csv. Su ProgramId es 0006fa0b2a9f8a4eb9d7c81e8b1f3c5d3e2a0000ffff.

Busca el mismo ProgramId en *_AssociatedFileEntries.csv y en el catálogo padre de aplicaciones (la clave del registro Root\InventoryApplication\<ProgramId>, o el CSV equivalente si tu parser emite uno). Típicamente obtienes:

  • El nombre de visualización y el publisher de la aplicación.
  • La fecha de instalación.
  • El origen de instalación (a veces una URL de descarga).
  • La lista completa de archivos asociados con esa aplicación.

Esto es invaluable cuando un atacante deja una herramienta que se hace pasar por una aplicación legítima. El nombre del archivo y los metadatos podrían decir "AdobeReaderUpdater.exe / Adobe Inc.", pero su ProgramId o bien no coincide con ningún producto Adobe instalado (sospechoso) o coincide con una aplicación diferente de la que sugiere su nombre (muy sospechoso).

Aplicación → archivos#

El pivote inverso es igualmente útil. Encontraste un registro de aplicación sospechoso en InventoryApplication — quizá un build portable de algo con un publisher de "" y un origen de instalación inusual. Toma su ProgramId y lista cada archivo que lo comparta:

$pid = '0006fa0b2a9f8a4eb9d7c81e8b1f3c5d3e2a0000ffff'
 
Import-Csv .\HOST_amcache_UnassociatedFileEntries.csv |
  Where-Object { $_.ProgramId -eq $pid } |
  Select-Object FullPath, Hash, KeyLastWriteTimestamp
 
Import-Csv .\HOST_amcache_AssociatedFileEntries.csv |
  Where-Object { $_.ProgramId -eq $pid } |
  Select-Object FullPath, Hash, KeyLastWriteTimestamp

Obtienes la huella completa de archivos de esa aplicación tal como la vio el appraiser — cada EXE, DLL y PE que el appraiser vinculó de vuelta a esa aplicación. Para herramientas del atacante, esta suele ser la forma más rápida de enumerar el conjunto completo de binarios desplegados a partir de un único archivo observado.


Pivote cross-host con ProgramId#

Aquí es donde ProgramId brilla como primitiva de hunt. Como la identidad es estable entre hosts para la misma aplicación, un único ProgramId sospechoso en un host se convierte en una consulta que puedes ejecutar contra el Amcache de cualquier otro host:

$pid = '0006fa0b2a9f8a4eb9d7c81e8b1f3c5d3e2a0000ffff'
 
Get-ChildItem -Recurse -Filter *_UnassociatedFileEntries.csv |
  ForEach-Object {
    $rows = Import-Csv $_.FullName |
      Where-Object { $_.ProgramId -eq $pid }
    if ($rows) {
      $host = $_.PSChildName.Split('_')[0]
      foreach ($r in $rows) {
        [pscustomobject]@{
          Host = $host
          Path = $r.FullPath
          Hash = $r.Hash
          When = $r.KeyLastWriteTimestamp
        }
      }
    }
  } |
  Sort-Object When

Este es el pivote de movimiento lateral. Si un único host tiene un ProgramId sospechoso, ejecutar esta consulta a través de tus CSVs Amcache recogidos te dice cada otro host en el que alguna vez apareció la misma aplicación, con rutas y horas — típicamente una señal más ajustada que los pivotes solo de hash porque el ProgramId sobrevive a pequeñas variaciones en el binario (re-firma, recompilación con el mismo nombre/publisher/versión).

Para el playbook completo de movimiento lateral, ver Movimiento lateral y pivote ProgramId de Amcache.


Cuándo ProgramId es más útil#

Algunos patrones de investigación en los que ProgramId es el pivote correcto:

Misma familia, múltiples builds#

Un atacante puede compilar su herramienta múltiples veces con los mismos metadatos de nombre, publisher y versión — solo cambia el binario. Los hashes difieren. El ProgramId es el mismo. El hashing por sí solo pierde la familia; ProgramId la captura.

Binarios renombrados#

mimikatz.exesvchost64.exeupdate.exe. El nombre del archivo cambia; los metadatos PE incrustados son los mismos. Si el atacante no se molestó en limpiar el recurso de version-info, el ProgramId se mantiene igual.

Herramientas redesplegadas en un entorno#

El atacante deja la misma herramienta en 20 hosts mientras se mueve lateralmente. Los hashes pueden coincidir, las rutas normalmente no. El ProgramId es el identificador más consistente a través de los 20 hosts.


Cuándo ProgramId es el pivote equivocado#

Algunos casos en los que ProgramId rinde mal:

Herramientas con metadatos rotativos#

Si el atacante recompila con nombre/publisher/versión diferentes cada vez, cada build obtiene un ProgramId diferente. Para estos, el hash de contenido del archivo (el SHA-1 en Hash) es el mejor pivote cross-host, porque al menos un componente del build es idéntico.

Binarios living-off-the-land#

net.exe, psexec.exe, certutil.exe — herramientas legítimas abusadas. Cada host que haya ejecutado cualquiera de estos tiene el mismo ProgramId para ellos. Las coincidencias de ProgramId son esencialmente sin significado aquí. Pivota sobre la línea de comandos (4688 / Sysmon 1) o sobre la ruta desde la que se ejecutó el LOLBIN.

Binarios verdaderamente novedosos#

Un archivo que aparece por primera vez en cualquier sitio tiene un ProgramId que ningún otro host comparte. Sin un corpus con el que comparar, ProgramId no hace trabajo; hash más Publisher = '' and IsPeFile = True and FullPath bajo \Users\ es el filtro de triage que funciona.


Confusiones comunes#

Dos cosas que ProgramId no es:

No es un hash del binario#

Hash (o FileId) es el hash de contenido. ProgramId es un hash de identidad de aplicación, calculado a partir de metadatos, no de los bytes del binario. Dos binarios completamente diferentes con metadatos idénticos obtienen el mismo ProgramId; el mismo binario recompilado con metadatos diferentes obtiene un ProgramId diferente.

No es un identificador único por host#

Un error común es asumir que ProgramId identifica una instalación específica. No lo hace. Si un usuario instala Notepad++ en cinco hosts, esos cinco hosts comparten un ProgramId para Notepad++. Para distinguir instalaciones, necesitas fechas de instalación, orígenes o rutas además de ProgramId.


Dónde vive ProgramId en la salida de AmcacheParser#

AmcacheParser expone ProgramId en cada CSV de categoría que lo tenga:

CSV Uso de ProgramId
*_UnassociatedFileEntries.csv La aplicación a la que un archivo dice pertenecer (incluso si falta el registro de aplicación padre).
*_AssociatedFileEntries.csv La aplicación a la que el archivo está genuinamente asociado.
*_ProgramEntries.csv La propia identidad de la aplicación (schema legacy).
*_ShortcutEntries.csv La aplicación a la que apunta el acceso directo.

Para pivotes, el patrón estándar es: identificar una fila sospechosa en UnassociatedFileEntries, tomar su ProgramId y ejecutar los patrones de consulta arriba.


Ver también#

Para explorar valores ProgramId en tu propio hive sin instalar nada, suelta un archivo en la página de inicio del parser — nunca sale de tu navegador.

Artículos relacionados

Volver a todos los artículos