diff --git a/.gitignore b/.gitignore index 2000470..7d7934a 100644 --- a/.gitignore +++ b/.gitignore @@ -188,3 +188,6 @@ History #Firefox* result/ results/ + +hack-browser-data +!/hack-browser-data \ No newline at end of file diff --git a/cmd/hack-browser-data/main.go b/cmd/hack-browser-data/main.go index dc9c345..bfc53fc 100644 --- a/cmd/hack-browser-data/main.go +++ b/cmd/hack-browser-data/main.go @@ -29,7 +29,7 @@ func Execute() { Name: "hack-browser-data", Usage: "Export passwords/cookies/history/bookmarks from browser", UsageText: "[hack-browser-data -b chrome -f json -dir results -cc]\nExport all browingdata(password/cookie/history/bookmark) from browser\nGithub Link: https://github.com/moonD4rk/HackBrowserData", - Version: "0.4.1", + Version: "0.4.2", Flags: []cli.Flag{ &cli.BoolFlag{Name: "verbose", Aliases: []string{"vv"}, Destination: &verbose, Value: false, Usage: "verbose"}, &cli.BoolFlag{Name: "compress", Aliases: []string{"zip"}, Destination: &compress, Value: false, Usage: "compress result to zip"}, @@ -65,6 +65,7 @@ func Execute() { if err = fileutil.CompressDir(outputDir); err != nil { log.Error(err) } + log.Noticef("compress success") } return nil }, diff --git a/internal/browingdata/browsingdata.go b/internal/browingdata/browsingdata.go index 444eda9..01024a4 100644 --- a/internal/browingdata/browsingdata.go +++ b/internal/browingdata/browsingdata.go @@ -7,6 +7,7 @@ import ( "hack-browser-data/internal/browingdata/cookie" "hack-browser-data/internal/browingdata/creditcard" "hack-browser-data/internal/browingdata/download" + "hack-browser-data/internal/browingdata/extension" "hack-browser-data/internal/browingdata/history" "hack-browser-data/internal/browingdata/localstorage" "hack-browser-data/internal/browingdata/password" @@ -78,6 +79,8 @@ func (d *Data) addSource(Sources []item.Item) { d.sources[source] = &creditcard.ChromiumCreditCard{} case item.ChromiumLocalStorage: d.sources[source] = &localstorage.ChromiumLocalStorage{} + case item.ChromiumExtension: + d.sources[source] = &extension.ChromiumExtension{} case item.YandexPassword: d.sources[source] = &password.YandexPassword{} case item.YandexCreditCard: @@ -94,6 +97,8 @@ func (d *Data) addSource(Sources []item.Item) { d.sources[source] = &download.FirefoxDownload{} case item.FirefoxLocalStorage: d.sources[source] = &localstorage.FirefoxLocalStorage{} + case item.FirefoxExtension: + d.sources[source] = &extension.FirefoxExtension{} } } } diff --git a/internal/browingdata/cookie/cookie.go b/internal/browingdata/cookie/cookie.go index 437b2ff..45a4fbd 100644 --- a/internal/browingdata/cookie/cookie.go +++ b/internal/browingdata/cookie/cookie.go @@ -69,7 +69,6 @@ func (c *ChromiumCookie) Parse(masterKey []byte) error { CreateDate: typeutil.TimeEpoch(createDate), ExpireDate: typeutil.TimeEpoch(expireDate), } - // TODO: replace DPAPI if len(encryptValue) > 0 { var err error if masterKey == nil { diff --git a/internal/browingdata/extension/extension.go b/internal/browingdata/extension/extension.go new file mode 100644 index 0000000..ecc8261 --- /dev/null +++ b/internal/browingdata/extension/extension.go @@ -0,0 +1,75 @@ +package extension + +import ( + "os" + + "github.com/tidwall/gjson" + + "hack-browser-data/internal/item" + "hack-browser-data/internal/log" + "hack-browser-data/internal/utils/fileutil" +) + +type ChromiumExtension []*extension + +type extension struct { + Name string + Description string + Version string + HomepageURL string +} + +const ( + manifest = "manifest.json" +) + +func (c *ChromiumExtension) Parse(masterKey []byte) error { + files, err := fileutil.FilesInFolder(item.TempChromiumExtension, manifest) + if err != nil { + return err + } + defer os.RemoveAll(item.TempChromiumExtension) + for _, f := range files { + file, err := fileutil.ReadFile(f) + if err != nil { + log.Error("Failed to read file: %s", err) + continue + } + b := gjson.Parse(file) + *c = append(*c, &extension{ + Name: b.Get("name").String(), + Description: b.Get("description").String(), + Version: b.Get("version").String(), + HomepageURL: b.Get("homepage_url").String(), + }) + } + return nil +} + +func (c *ChromiumExtension) Name() string { + return "extension" +} + +type FirefoxExtension []*extension + +func (f *FirefoxExtension) Parse(masterKey []byte) error { + s, err := fileutil.ReadFile(item.TempFirefoxExtension) + if err != nil { + return err + } + defer os.Remove(item.TempFirefoxExtension) + j := gjson.Parse(s) + for _, v := range j.Get("addons").Array() { + *f = append(*f, &extension{ + Name: v.Get("defaultLocale.name").String(), + Description: v.Get("defaultLocale.description").String(), + Version: v.Get("version").String(), + HomepageURL: v.Get("defaultLocale.homepageURL").String(), + }) + } + return nil +} + +func (f *FirefoxExtension) Name() string { + return "extension" +} diff --git a/internal/browingdata/history/history.go b/internal/browingdata/history/history.go index c1f5aa7..f5fc6bf 100644 --- a/internal/browingdata/history/history.go +++ b/internal/browingdata/history/history.go @@ -44,7 +44,6 @@ func (c *ChromiumHistory) Parse(masterKey []byte) error { visitCount int lastVisitTime int64 ) - // TODO: handle rows error if err := rows.Scan(&url, &title, &visitCount, &lastVisitTime); err != nil { log.Warn(err) } diff --git a/internal/browser/browser.go b/internal/browser/browser.go index 7871d5f..6cc5cc0 100644 --- a/internal/browser/browser.go +++ b/internal/browser/browser.go @@ -40,7 +40,6 @@ func PickBrowser(name, profile string) ([]Browser, error) { func pickChromium(name, profile string) []Browser { var browsers []Browser name = strings.ToLower(name) - // TODO: add support for 「all」 flag and set profilePath if name == "all" { for _, v := range chromiumList { if !fileutil.FolderExists(filepath.Clean(v.profilePath)) { diff --git a/internal/browser/chromium/chromium.go b/internal/browser/chromium/chromium.go index b4404a1..d2c0c1c 100644 --- a/internal/browser/chromium/chromium.go +++ b/internal/browser/chromium/chromium.go @@ -1,7 +1,6 @@ package chromium import ( - "io/ioutil" "os" "path/filepath" "strings" @@ -63,21 +62,21 @@ func (c *chromium) BrowsingData() (*browingdata.Data, error) { func (c *chromium) copyItemToLocal() error { for i, path := range c.itemPaths { - if fileutil.FolderExists(path) { - if err := fileutil.CopyDir(path, i.String(), "lock"); err != nil { - return err + filename := i.String() + var err error + switch { + case fileutil.FolderExists(path): + if i == item.ChromiumLocalStorage { + err = fileutil.CopyDir(path, filename, "lock") } - } else { - var filename = i.String() - // TODO: Handle read file error - d, err := ioutil.ReadFile(path) - if err != nil { - return err - } - err = ioutil.WriteFile(filename, d, 0777) - if err != nil { - return err + if i == item.ChromiumExtension { + err = fileutil.CopyDirContains(path, filename, "manifest.json") } + default: + err = fileutil.CopyFile(path, filename) + } + if err != nil { + return err } } return nil diff --git a/internal/browser/firefox/firefox.go b/internal/browser/firefox/firefox.go index 17878b0..e40db10 100644 --- a/internal/browser/firefox/firefox.go +++ b/internal/browser/firefox/firefox.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "io/fs" - "io/ioutil" "path/filepath" "hack-browser-data/internal/browingdata" @@ -58,15 +57,8 @@ func (f *firefox) getMultiItemPath(profilePath string, items []item.Item) (map[s func (f *firefox) copyItemToLocal() error { for i, path := range f.itemPaths { - // var dstFilename = item.TempName() - var filename = i.String() - // TODO: Handle read file error - d, err := ioutil.ReadFile(path) - if err != nil { - return err - } - err = ioutil.WriteFile(filename, d, 0777) - if err != nil { + filename := i.String() + if err := fileutil.CopyFile(path, filename); err != nil { return err } } diff --git a/internal/item/filename.go b/internal/item/filename.go index 23909bb..8810f53 100644 --- a/internal/item/filename.go +++ b/internal/item/filename.go @@ -10,6 +10,7 @@ const ( fileChromiumCookie = "Cookies" fileChromiumBookmark = "Bookmarks" fileChromiumLocalStorage = "Local Storage/leveldb" + fileChromiumExtension = "Extensions" fileYandexPassword = "Ya Passman Data" fileYandexCredit = "Ya Credit Cards" @@ -19,6 +20,7 @@ const ( fileFirefoxPassword = "logins.json" fileFirefoxData = "places.sqlite" fileFirefoxLocalStorage = "webappsstore.sqlite" + fileFirefoxExtension = "extensions.json" ) const ( @@ -48,5 +50,5 @@ const ( TempFirefoxDownload = "firefoxDownload" TempFirefoxLocalStorage = "firefoxLocalStorage" TempFirefoxCreditCard = "" - TempFirefoxExtension = "" + TempFirefoxExtension = "firefoxExtension" ) diff --git a/internal/item/item.go b/internal/item/item.go index df8eac3..671cb0e 100644 --- a/internal/item/item.go +++ b/internal/item/item.go @@ -44,7 +44,7 @@ func (i Item) FileName() string { case ChromiumCreditCard: return fileChromiumCredit case ChromiumExtension: - return UnknownItem + return fileChromiumExtension case ChromiumHistory: return fileChromiumHistory case YandexPassword: @@ -63,11 +63,11 @@ func (i Item) FileName() string { return fileFirefoxData case FirefoxLocalStorage: return fileFirefoxLocalStorage - case FirefoxCreditCard: - return UnsupportedItem case FirefoxHistory: return fileFirefoxData case FirefoxExtension: + return fileFirefoxExtension + case FirefoxCreditCard: return UnsupportedItem default: return UnknownItem @@ -91,7 +91,7 @@ func (i Item) String() string { case ChromiumCreditCard: return TempChromiumCreditCard case ChromiumExtension: - return UnsupportedItem + return TempChromiumExtension case ChromiumHistory: return TempChromiumHistory case YandexPassword: @@ -115,7 +115,7 @@ func (i Item) String() string { case FirefoxCreditCard: return UnsupportedItem case FirefoxExtension: - return UnsupportedItem + return TempFirefoxExtension default: return UnknownItem } diff --git a/internal/utils/fileutil/filetutil.go b/internal/utils/fileutil/filetutil.go index b8c8b92..315a122 100644 --- a/internal/utils/fileutil/filetutil.go +++ b/internal/utils/fileutil/filetutil.go @@ -3,6 +3,7 @@ package fileutil import ( "archive/zip" "bytes" + "errors" "fmt" "io/ioutil" "os" @@ -10,8 +11,6 @@ import ( "path/filepath" "strings" - "hack-browser-data/internal/log" - cp "github.com/otiai10/copy" ) @@ -39,12 +38,29 @@ func FolderExists(foldername string) bool { return info.IsDir() } +// FilesInFolder returns the files contains in the provided folder +func FilesInFolder(dir, filename string) ([]string, error) { + if !FolderExists(dir) { + return nil, errors.New(dir + " folder does not exist") + } + var files []string + err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error { + if !f.IsDir() && strings.Contains(path, filename) { + files = append(files, path) + } + return err + }) + return files, err +} + // ReadFile reads the file from the provided path func ReadFile(filename string) (string, error) { s, err := ioutil.ReadFile(filename) return string(s), err } +// CopyDir copies the directory from the source to the destination +// skip the file if you don't want to copy func CopyDir(src, dst, skip string) error { s := cp.Options{Skip: func(src string) (bool, error) { return strings.Contains(strings.ToLower(src), skip), nil @@ -52,27 +68,73 @@ func CopyDir(src, dst, skip string) error { return cp.Copy(src, dst, s) } +// CopyDirContains copies the directory from the source to the destination +// contain is the file if you want to copy +func CopyDirContains(src, dst, contain string) error { + var filelist []string + err := filepath.Walk(src, func(path string, f os.FileInfo, err error) error { + if !f.IsDir() && strings.Contains(strings.ToLower(f.Name()), contain) { + filelist = append(filelist, path) + } + return err + }) + if err != nil { + return err + } + if err := os.MkdirAll(dst, 0755); err != nil { + return err + } + for index, file := range filelist { + // p = dir/index_file + p := fmt.Sprintf("%s/%d_%s", dst, index, BaseDir(file)) + err = CopyFile(file, p) + if err != nil { + return err + } + } + return nil +} + +// CopyFile copies the file from the source to the destination +func CopyFile(src, dst string) error { + // TODO: Handle read file error + d, err := ioutil.ReadFile(src) + if err != nil { + return err + } + err = ioutil.WriteFile(dst, d, 0777) + if err != nil { + return err + } + return nil +} + +// Filename returns the filename from the provided path func Filename(browser, item, ext string) string { replace := strings.NewReplacer(" ", "_", ".", "_", "-", "_") return strings.ToLower(fmt.Sprintf("%s_%s.%s", replace.Replace(browser), item, ext)) } +// ParentDir returns the parent directory of the provided path func ParentDir(p string) string { return filepath.Dir(filepath.Clean(p)) } +// BaseDir returns the base directory of the provided path func BaseDir(p string) string { return filepath.Base(p) } +// ParentBaseDir returns the parent base directory of the provided path func ParentBaseDir(p string) string { return BaseDir(ParentDir(p)) } +// CompressDir compresses the directory into a zip file func CompressDir(dir string) error { files, err := ioutil.ReadDir(dir) if err != nil { - log.Error(err) + return err } var b = new(bytes.Buffer) zw := zip.NewWriter(b) @@ -91,7 +153,7 @@ func CompressDir(dir string) error { } err = os.Remove(fileName) if err != nil { - log.Error(err) + return err } } if err := zw.Close(); err != nil { @@ -106,6 +168,5 @@ func CompressDir(dir string) error { if err != nil { return err } - log.Noticef("compress success, zip filename is %s", filename) return nil }