Jetzt reparieren
- Datei ablegen
- Reparatur läuft lokal
- Ergebnis herunterladen
Signal Desktop legt deinen gesamten Nachrichtenverlauf in einer einzigen SQLite-Datenbank namens db.sqlite ab, doch das ist nicht einfach nur SQLite: Signal verwendet SQLCipher, einen Fork, der die gesamte Datei samt Header transparent mit AES-256 verschlüsselt. Das bedeutet, dass eine Seiten-Rettungs-Engine (auch diese hier) nur hochentropisches Rauschen sieht und file is not a database (Datei ist keine Datenbank) meldet, bis die Datei entschlüsselt ist. Deshalb sind es zwei Schritte, um eine kaputte Signal-Datenbank wiederherzustellen: zuerst lokal entschlüsseln mit dem Schlüssel, der bereits auf deinem Gerät liegt, was eine Klartext-SQLite-Datei erzeugt; dann lässt du diese Datei oben fallen und das Werkzeug durchläuft ihre Seiten in deinem Browser und baut aus jeder Zeile, die es noch dekodieren kann, eine saubere Datenbank neu auf. Sowohl der Schlüssel als auch die Daten bleiben auf deinem Gerät.
Signals db.sqlite ist verschlüsselt — entschlüssle sie vor jeder Rettung
Die Datenbank von Signal Desktop liegt unter ~/Library/Application Support/Signal/sql/db.sqlite unter macOS, unter %AppData%\Signal\sql\db.sqlite unter Windows und unter ~/.config/Signal/sql/db.sqlite unter Linux. Da SQLCipher die gesamte Datei verschlüsselt, zeigt der Aufruf file db.sqlite zufällige Bytes statt der üblichen Signatur SQLite format 3\0. Jedes normale SQLite-Werkzeug — die sqlite3-Shell, DB Browser oder die Rettungs-Engine dieser Seite — sieht Chiffretext, keine Seiten, und kann und wird den Schlüssel nicht erraten.
Der 64 Hexadezimalzeichen lange SQLCipher-Schlüssel steht in config.json neben der Datenbank. In älteren Builds wird er im Klartext unter einem Feld key gespeichert; seit 2024 hüllt Signal ihn unter encryptedKey mithilfe der safeStorage-API von Electron ein — abgesichert durch das macOS-Schlüsselbund-Element namens "Signal Safe Storage", durch DPAPI unter Windows und durch gnome-libsecret / kwallet unter Linux. Da diese Hülle an dein Betriebssystem-Konto gebunden ist, entpackst du den Schlüssel auf derselben Maschine und mit demselben Benutzer, die ihn erstellt haben.
Sobald du den rohen Schlüssel hast, öffne die Datenbank in einem beliebigen SQLCipher-fähigen Werkzeug (dem sqlcipher-CLI oder einem DB-Browser-for-SQLite-Build, der mit SQLCipher kompiliert wurde) und führe PRAGMA key = "x'<64-hex-key>'"; gefolgt von PRAGMA cipher_compatibility = 4; aus — Signal liefert SQLCipher 4 mit. Exportiere dann eine Klartext-Kopie mit .recover oder mit VACUUM INTO 'plain.sqlite'. Diese Klartext-Datei ist die, die du hier fallen lässt. Arbeite immer mit einer Byte-für-Byte-Kopie, niemals mit deinem einzigen Original.
Was "malformed" innerhalb einer Signal-Datenbank bedeutet
Einmal entschlüsselt, ist eine Signal-Datenbank eine ganz gewöhnliche SQLite-Datei: ein flaches Array aus Seiten fester Größe (je 4096 Byte). Seite 1 enthält den Header und das Schema; jede Tabelle und jeder Index ist ein B-Baum aus inneren Seiten, die nach unten auf Blattseiten zeigen, welche die Zeilen enthalten. Signals Daten leben in Tabellen wie messages (jede Zeile über id identifiziert, mit conversationId, sent_at, received_at, type, einer Spalte body und einer Spalte json, die den vollständigen Nachrichtendatensatz trägt), conversations, reactions, sessions, items und einem Volltext-Index messages_fts (FTS5), der für die Suche verwendet wird.
Beschädigung ist fast immer lokal begrenzt. Ein Stromausfall mitten in einem Schreibvorgang, eine unterbrochene Synchronisierung, eine zerrissene Seite auf einem Speicher, der über das Flushen gelogen hat — ein einziger Zeiger in einem B-Baum stimmt nicht mehr mit den Daten überein, und SQLite wirft SQLITE_CORRUPT: Error: database disk image is malformed (11). Es verweigert die gesamte Datei in dem Moment, in dem es der Struktur nicht mehr trauen kann, obwohl deine anderen Tabellen unberührt in ihren Blattseiten liegen. Die Datei sieht weit schlimmer aus, als sie ist.
Der verwandte Fehler file is not a database (SQLITE_NOTADB, Code 26) hat hier zwei Bedeutungen: Entweder wurde die 16 Byte lange Header-Signatur beschädigt (die Seiten dahinter sind oft intakt) oder — bei Signal weitaus häufiger — du hast ein normales Werkzeug auf die noch verschlüsselte Datei gerichtet. Wenn du ihn vor dem Entschlüsseln siehst, spricht die Verschlüsselung, keine Beschädigung.
Was die Seiten-Rettung aus einer Signal-DB zurückholt — und was nicht
Die Seiten-Rettung repariert die kaputte Datei nicht an Ort und Stelle; sie tut dasselbe wie SQLites eigenes .recover. Sie durchläuft die entschlüsselte Datei Seite für Seite, und für jede Seite, die wie ein Tabellenblatt aussieht, liest sie jede Zelle direkt — rowid, serielle Typen, Werte — und fügt jede dekodierbare Zeile in eine brandneue, leere Datenbank ein. Beschädigte Seiten werden übersprungen; lesbare Seiten geben ihre Zeilen preis, gleichgültig ob der B-Baum, der sie indexierte, überlebt hat oder nicht. Zeilen, die die Zeigerkette nicht mehr erreicht, werden durch einen rohen Suchlauf gefunden, und Zeilen, deren Ursprungstabelle sich nicht bestimmen lässt, wandern in eine Tabelle lost_and_found, indexiert nach Seite und Zelle, statt verworfen zu werden. In der Praxis kommen deine messages-Zeilen mit intaktem body und json zurück, selbst wenn das, was zerbrach, der FTS-Index oder der Baum einer einzelnen Unterhaltung war.
Wenn der Absturz eine Geschwisterdatei -wal hinterlassen hat (der WAL-Modus stellt die neuesten bestätigten Transaktionen bereit, bevor sie per Checkpoint in db.sqlite geschrieben werden), stecken darin deine neuesten Nachrichten — doch auch sie ist mit SQLCipher verschlüsselt, also entschlüssle sie zusammen mit der Hauptdatei. Bei einer Klartext-Datenbank und ihrem Klartext-WAL legt die Engine die bestätigten Frames vor dem Scan darüber, damit die Wiederherstellung die letzte bestätigte Transaktion widerspiegelt.
Die ehrlichen Grenzen sind real. Anhänge liegen nicht in der Datenbank: Signal speichert Fotos, Videos und Dateien als separate, einzeln verschlüsselte Blobs unter attachments.noindex/, sodass eine wiederhergestellte messages-Zeile zwar den Zeiger und die Metadaten behält, das Retten der DB aber die Mediendatei selbst nicht entschlüsselt. Alles, was am Ende der Datei physisch überschrieben oder abgeschnitten wurde, ist verloren — eine SQLite-Datei hat keine Redundanz, aus der sie sich rekonstruieren ließe. Und die wiederhergestellte Ausgabe ist immer verdächtig: Da der Scan rohe Zellen liest, einschließlich der Freelist-Seiten, können gelöschte Nachrichten wieder auftauchen und ein Wert kann mit der falschen Speicherklasse zurückkommen, prüfe sie also gegen eine bekannte, vertrauenswürdige Quelle, bevor du dich darauf verlässt.
Warum das auf deine eigene Maschine gehört
Die Datenbank eines Messengers ist die eine Datei, die du am wenigsten auf einem Server haben möchtest, den du nicht kontrollierst: Sie ist jede Unterhaltung, jeder Kontakt und jede Gruppe, die du behalten hast. Upload-basierte "Reparatur"-Dienste kopieren diese Datei auf eine Infrastruktur, die du nicht prüfen kannst, unter Aufbewahrungsbedingungen, die du nicht verfasst hast — und bei einer Signal-Datenbank hebelt das im Stillen den ganzen Sinn aus, Signal überhaupt zu benutzen.
Hier läuft die Rettung vollständig in deinem Browser-Tab: Die Klartext-Datei wird von der Festplatte gelesen, lokal neu aufgebaut und heruntergeladen, und du kannst den Netzwerk-Tab (Network) öffnen und zusehen, wie 0 Byte das Gerät verlassen. Genauso wichtig: Auch der Entschlüsselungsschritt bleibt lokal — dein SQLCipher-Schlüssel stammt aus deinem eigenen Schlüsselbund (oder aus config.json) und wird von einem Werkzeug auf deiner Maschine verwendet, niemals übertragen. Eine Signal-Datenbank auf diese Weise wiederherzustellen bedeutet, weder die Daten noch den Schlüssel einem Dritten anzuvertrauen.
Was sich reparieren lässt und was nicht
Kann repariert werden
- Eine Signal-Datenbank, die du zu Klartext entschlüsselt hast (mit sqlcipher oder einem DB-Browser-Build mit SQLCipher) und die dann "database disk image is malformed" meldet
- messages-Zeilen — die Spalte body und den vollständigen json-Datensatz — extrahiert aus den überlebenden Blattseiten des B-Baums
- conversations, reactions und andere Tabellen, wenn eine einzige defekte Seite oder ein defekter Zeiger die ganze Datei zu Fall brachte
- Zeilen, die die Zeigerkette nicht erreicht, über einen rohen Seiten-Scan, wobei nicht zuordenbare Zeilen in einer Tabelle lost_and_found abgelegt werden
- Die neuesten Nachrichten aus einer entschlüsselten -wal-Begleitdatei, vor dem Scan darübergelegt
Kann nicht repariert werden
- Die noch verschlüsselte db.sqlite selbst — entschlüssle sie zuerst lokal; die Engine rettet Klartext-SQLite, keinen SQLCipher-Chiffretext
- Alles ohne den Schlüssel: ein encryptedKey, den du nicht entpacken kannst (verlorener Schlüsselbund, eine andere Maschine oder ein anderer Benutzer), wird nie zu lesbarem SQLite
- Nachrichtenanhänge — sie liegen als separat verschlüsselte Dateien unter attachments.noindex/, nicht innerhalb der Datenbank
- Nachrichten, die physisch überschrieben oder am Ende der Datei abgeschnitten wurden — kein Werkzeug kann sie erfinden
- Eine Garantie für Korrektheit — gelöschte Zeilen können wieder auftauchen und Typen können sich verschieben; prüfe wiederhergestellte Daten, bevor du dich darauf verlässt
Wenn eine Reparatur fehlschlägt, sagen wir dir warum (fehlende Daten gegenüber beschädigter Struktur), und für eine fehlgeschlagene Reparatur wird dir nie etwas berechnet.