Amcache FileId erklärt: das SHA-1-Hash-Format, das Windows speichert

FileId in Root\InventoryApplicationFile ist eines der nützlichsten Felder der ganzen Hive und eines der meistmissverstandenen. Es ist ein Inhaltshash. Es ist nicht ganz ein Standard-SHA-1. Es hasht nicht ganz die ganze Datei. Diese Seite ist die vollständige Referenz.

Für den weiteren Kontext siehe die Vollständige Amcache-Referenz. Für die umgebende Registry-Struktur innerhalb der Hive siehe Amcache-Registry-Struktur.

Wie der Wert aussieht#

Ein typisches FileId aus einer realen Hive:

0000da39a3ee5e6b4b0d3255bfef95601890afd80709

41 Zeichen insgesamt:

  • Die ersten vier sind immer "0000". Fester Typ-Tag.
  • Die übrigen 40 sind der SHA-1-Hex-Digest.

Das "0000"-Präfix ist ein historisches Artefakt. Frühe Appraiser-Builds rechneten mit mehreren Hash-Algorithmen mit unterschiedlichen Präfixen. Ausgeliefert wurde nur SHA-1. Das Präfix ist seit Jahren konstant.

AmcacheParser teilt das in zwei CSV-Spalten auf:

Spalte Wert
FileId Voller 41-Zeichen-String mit Präfix.
Hash 40 Hex-Zeichen, nur SHA-1.

Verwenden Sie immer Hash (oder streifen Sie das Präfix selbst ab), wenn Sie gegen externe Feeds joinen. VirusTotal, TI-Feeds, Allowlist-Datenbanken wollen alle 40-Zeichen-SHA-1. Sie matchen stillschweigend nichts, wenn Sie das "0000" mitsenden.

Was es tatsächlich hasht#

Die Falle, die fast jeden neuen Analysten erwischt:

Der SHA-1 hasht die ersten 31 MiB der Datei, nicht die ganze Datei.

Für Dateien kleiner als 31 MiB (was die meisten EXEs und DLLs sind), entspricht der Präfix-Hash dem Vollinhalts-SHA-1. Nicht unterscheidbar.

Für größere Dateien ist Amcaches Wert ein Präfix-Hash. Immer noch unterscheidend genug, um einen bestimmten Build einer bestimmten Binärdatei zu identifizieren. Aber es ist nicht das, was sha1sum Ihnen über die ganze Datei geben würde.

Warum das zählt#

  • VirusTotal-Treffer. Unter 31 MiB matcht der Amcache-SHA-1 den SHA-1, den VT indexiert. Größere Dateien (Installer, manche Spiele-Binärdateien, große Enterprise-Software) matchen oft nicht, und eine "kein Eintrag"-Antwort von VT bedeutet nichts Nützliches.
  • Eigene Hash-Datenbanken. Wenn Sie eine interne Allowlist pflegen, speichern Sie dieselbe Art Hash, gegen die Sie vergleichen wollen. Entweder Vollinhalts-SHA-1s speichern (und Mismatches bei großen Binärdateien akzeptieren) oder eine parallele Präfix-Hash-Spalte pflegen.
  • Verifikation. Wenn Sie die Originalbinärdatei haben und verifizieren möchten, hashen Sie nur die ersten 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()

Reale Fallen#

Eine Handvoll Stolperfallen, die in echten Fällen auftauchen.

Das Präfix nicht in Lookups einschließen#

# Falsch
search_virustotal('0000da39a3ee5e6b4b0d3255bfef95601890afd80709')
 
# Richtig
search_virustotal('da39a3ee5e6b4b0d3255bfef95601890afd80709')

VirusTotals API erwartet den nackten Hash. Mit Präfix bekommen Sie still ein leeres Ergebnis, das identisch wie "Datei unbekannt" aussieht. Das verschwendet Triage-Zeit und produziert falsche Befunde.

SHA-1-Kollisionen sind theoretisch, aber nicht unmöglich#

Echte SHA-1-Kollisionsangriffe existieren. In einem nicht-adversarialen Kontext ist das irrelevant. Eine Kollision gegen einen bestimmten Amcache-Eintrag zu finden, ist wild unverhältnismäßig zu dem, was ein Angreifer gewinnt. Aber für ein hochzuverlässiges Matching in einer hochstand-Ermittlung sollten Sie einen SHA-1-Treffer nicht als kryptografische Identität behandeln. Mit Dateigröße, Linkdatum und mindestens einem weiteren Feld kombinieren.

Dem FileId einer Nicht-PE-Zeile nicht trauen#

Amcache erfasst gelegentlich FileId-Werte für Nicht-PE-Dateien, die der Appraiser gesehen hat. Der Hash ist immer noch echt, aber nachgelagerte Tools, die PE-Kontext annehmen (VirusTotals PE-bewusste Suchen, Yara-Regeln gegen PE-Bytes), liefern weniger nützliche Ergebnisse.

Mehrere Zeilen, gleicher Hash#

Wenn Sie denselben Hash über mehrere *_UnassociatedFileEntries.csv-Zeilen auf demselben Host finden, ist das bedeutsam. Derselbe Binärinhalt wurde an mehreren Pfaden inventarisiert. Übliche Gründe:

  • Angreifer hat das Tool an mehrere Stellen kopiert, um zu testen, welcher Ort ausgeführt wird.
  • Legitimer Installer hat dieselbe DLL in mehrere Produktverzeichnisse abgelegt.
  • Benutzer hat eine Datei manuell herumkopiert.

Nach Hash clustern, dann FullPath und KeyLastWriteTimestamp für jede Instanz ansehen. Zeitstempel verraten die Reihenfolge. Pfade verraten die Absicht.

Mehrere Hashes, gleicher FullPath#

Das umgekehrte Muster. Gleicher Pfad, unterschiedlicher Hash über mehrere Zeilen bedeutet, dass die Binärdatei an diesem Pfad sich geändert hat zwischen Inventarisierungen. Starkes Signal für Binärersatz:

  • Legitim: Software-Update hat die Datei überschrieben.
  • Verdächtig: Angreifer hat eine System-Binärdatei oder ein regelmäßig genutztes User-Tool gegen eine trojanisierte Kopie getauscht.

Die Zeilen nach KeyLastWriteTimestamp sortieren, um zu sehen, wann jeder neue Hash auftauchte. Mit Patch-Events oder Sysmon-File-Create-Events um diese Zeiten korrelieren.

Pivots, die ihre Miete verdienen#

Cross-Host-Hash-Jagd#

# Einen bekannten Bad-SHA-1 über die Amcache-CSVs aller Hosts pivotieren
$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 Host

So gehen Sie von "wir haben diesen Hash auf einem Host gefunden" zu "jeder Host im Bestand, der je diese Binärdatei hatte".

VirusTotal-Anreicherung#

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 der öffentlichen VT-API

Selbst niedrigvolumig gegen die öffentliche API liefert das eine knappe Liste bestätigt-böser Hashes auf einem typisch infizierten Host.

Sysmon-Image-Loaded-Korrelation#

Sysmon Event ID 7 zeichnet den SHA-1 jeder DLL auf, die von jedem Prozess geladen wurde. Wenn Sie Amcache Hash gegen Sysmon 7s Hashes-Feld joinen, sehen Sie genau, welche Prozesse eine gegebene Angreifer-DLL geladen haben und wann.

Weiterführende Quellen#

Verwandt#

Verwandte Beiträge

Zurück zu allen Beiträgen