mirror of
https://github.com/moonD4rk/HackBrowserData.git
synced 2026-07-04 21:37:47 +02:00
bf96ba8c80
* feat(restore): cross-platform restore via dump engine rebuild (#606) Restore previously required the dump's origin OS, overlaying keys onto locally-discovered browsers. It now rebuilds Chromium engines from the dump's vaults (v2 adds engine kind), so copied data or an archive zip decrypts on any OS. * fix(restore): polish help text, drop dead check, dedup dump kinds pflag treats backticked words in flag usage as the value placeholder, so --data-zip rendered as "--data-zip archive" in help output.
121 lines
3.9 KiB
Go
121 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/moond4rk/hackbrowserdata/browser"
|
|
"github.com/moond4rk/hackbrowserdata/log"
|
|
"github.com/moond4rk/hackbrowserdata/masterkey"
|
|
"github.com/moond4rk/hackbrowserdata/utils/fileutil"
|
|
)
|
|
|
|
func restoreCmd() *cobra.Command {
|
|
var (
|
|
keysPath string
|
|
dataDir string
|
|
dataZip string
|
|
browserName string
|
|
category string
|
|
outputFormat string
|
|
outputDir string
|
|
compress bool
|
|
)
|
|
|
|
cmd := &cobra.Command{
|
|
Use: "restore",
|
|
Short: "Decrypt copied profile data using exported master keys",
|
|
Example: ` hack-browser-data restore --keys keys.json --data-zip data.zip
|
|
hack-browser-data restore --keys keys.json --data-dir ./data -b chrome -c cookie
|
|
hack-browser-data restore --keys keys.json --data-dir ./chrome-userdata -b chrome
|
|
ssh origin "hack-browser-data dumpkeys" | hack-browser-data restore --keys - --data-zip data.zip`,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
resolvedDir, cleanup, err := resolveDataDir(dataDir, dataZip)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cleanup()
|
|
|
|
browsers, err := loadRestoreBrowsers(keysPath, resolvedDir, browserName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(browsers) == 0 {
|
|
log.Warnf("no browsers to restore from the supplied keys and data")
|
|
return nil
|
|
}
|
|
categories, err := parseCategories(category)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return extractAndWrite(browsers, categories, outputDir, outputFormat, compress)
|
|
},
|
|
}
|
|
|
|
cmd.Flags().StringVar(&keysPath, "keys", "", "keys file from dumpkeys (use - for stdin)")
|
|
cmd.Flags().StringVar(&dataDir, "data-dir", "", "copied profile data dir (archive layout, or one browser's User Data with -b)")
|
|
cmd.Flags().StringVar(&dataZip, "data-zip", "", "zip produced by the archive command (alternative to --data-dir)")
|
|
cmd.Flags().StringVarP(&browserName, "browser", "b", "", "restore only this browser (optional; must match a vault in --keys)")
|
|
cmd.Flags().StringVarP(&category, "category", "c", "all", "data categories (comma-separated): all|"+categoryNames())
|
|
cmd.Flags().StringVarP(&outputFormat, "format", "f", "json", "output format: csv|json|cookie-editor")
|
|
cmd.Flags().StringVarP(&outputDir, "dir", "d", "results", "output directory")
|
|
cmd.Flags().BoolVar(&compress, "zip", false, "compress output to zip")
|
|
|
|
_ = cmd.MarkFlagRequired("keys")
|
|
cmd.MarkFlagsMutuallyExclusive("data-dir", "data-zip")
|
|
|
|
return cmd
|
|
}
|
|
|
|
func loadRestoreBrowsers(keysPath, dataDir, browserName string) ([]browser.Browser, error) {
|
|
if keysPath == "" {
|
|
return nil, fmt.Errorf("requires --keys <file> (or - for stdin)")
|
|
}
|
|
|
|
var r io.Reader = os.Stdin
|
|
if keysPath != "-" {
|
|
f, err := os.Open(keysPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open keys file %q: %w", keysPath, err)
|
|
}
|
|
defer f.Close()
|
|
r = f
|
|
}
|
|
dump, err := masterkey.ReadJSON(r)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read keys file %q: %w", keysPath, err)
|
|
}
|
|
|
|
return browser.BuildFromDump(dump, dataDir, browserName)
|
|
}
|
|
|
|
// resolveDataDir returns the directory restore reads from: --data-dir as-is, or --data-zip extracted
|
|
// into a temp dir (removed by the returned cleanup). Exactly one of the two must be set.
|
|
func resolveDataDir(dataDir, dataZip string) (string, func(), error) {
|
|
noop := func() {}
|
|
if (dataDir == "") == (dataZip == "") {
|
|
return "", noop, fmt.Errorf("exactly one of --data-dir or --data-zip is required")
|
|
}
|
|
if dataDir != "" {
|
|
return dataDir, noop, nil
|
|
}
|
|
tmp, err := os.MkdirTemp("", "hbd-restore-*")
|
|
if err != nil {
|
|
return "", noop, fmt.Errorf("create temp dir: %w", err)
|
|
}
|
|
if err := fileutil.Unzip(dataZip, tmp); err != nil {
|
|
removeTempDir(tmp)
|
|
return "", noop, fmt.Errorf("extract %s: %w", dataZip, err)
|
|
}
|
|
return tmp, func() { removeTempDir(tmp) }, nil
|
|
}
|
|
|
|
func removeTempDir(dir string) {
|
|
if err := os.RemoveAll(dir); err != nil {
|
|
log.Warnf("restore: remove temp dir %s: %v", dir, err)
|
|
}
|
|
}
|