diff --git a/README.md b/README.md index 41a8028..172c05b 100644 --- a/README.md +++ b/README.md @@ -47,13 +47,13 @@ Based on Apple's security policy, some browsers **require a current user passwor | Opera | ✅ | ✅ | ✅ | ✅ | | OperaGX | ✅ | ✅ | ✅ | ✅ | | Vivaldi | ✅ | ✅ | ✅ | ✅ | -| Yandex | ✅ | ✅ | ✅ | ✅ | | CocCoc | ✅ | ✅ | ✅ | ✅ | | Firefox | ✅ | ✅ | ✅ | ✅ | | Firefox Beta | ✅ | ✅ | ✅ | ✅ | | Firefox Dev | ✅ | ✅ | ✅ | ✅ | | Firefox ESR | ✅ | ✅ | ✅ | ✅ | | Firefox Nightly | ✅ | ✅ | ✅ | ✅ | +| Yandex | ✅ | ✅ | ✅ | ✅ | | Safari | ❌ | ❌ | ❌ | ❌ | ### Linux @@ -84,14 +84,14 @@ Installation of `HackBrowserData` is dead-simple, just download [the release for ### Building from source -support `go 1.14+` +only support `go 1.18+` with go generics ```bash -git clone https://github.com/moonD4rk/HackBrowserData +$ git clone https://github.com/moonD4rk/HackBrowserData -cd HackBrowserData +$ cd HackBrowserData/cmd/hack-browser-data -go build +$ CGO_ENABLED=1 go build ``` ### Cross compile @@ -125,70 +125,57 @@ NAME: USAGE: [hack-browser-data -b chrome -f json -dir results -cc] - Get all data(password/cookie/history/bookmark) from chrome + Export all browingdata(password/cookie/history/bookmark) from browser + Github Link: https://github.com/moonD4rk/HackBrowserData VERSION: - 0.3.7 + 0.4.0 + GLOBAL OPTIONS: - --verbose, --vv verbose (default: false) - --compress, --cc compress result to zip (default: false) - --browser value, -b value available browsers: all|opera|firefox|chrome|edge (default: "all") - --results-dir value, --dir value export dir (default: "results") - --format value, -f value format, csv|json|console (default: "csv") - --profile-dir-path value, -p value custom profile dir path, get with chrome://version - --key-file-path value, -k value custom key file path - --help, -h show help (default: false) - --version, -v print the version (default: false) - -PS C:\test> .\hack-browser-data.exe -b all -f json --dir results -cc -[x]: Get 44 cookies, filename is results/microsoft_edge_cookie.json -[x]: Get 54 history, filename is results/microsoft_edge_history.json -[x]: Get 1 passwords, filename is results/microsoft_edge_password.json -[x]: Get 4 bookmarks, filename is results/microsoft_edge_bookmark.json -[x]: Get 6 bookmarks, filename is results/360speed_bookmark.json -[x]: Get 19 cookies, filename is results/360speed_cookie.json -[x]: Get 18 history, filename is results/360speed_history.json -[x]: Get 1 passwords, filename is results/360speed_password.json -[x]: Get 12 history, filename is results/qq_history.json -[x]: Get 1 passwords, filename is results/qq_password.json -[x]: Get 12 bookmarks, filename is results/qq_bookmark.json -[x]: Get 14 cookies, filename is results/qq_cookie.json -[x]: Get 28 bookmarks, filename is results/firefox_bookmark.json -[x]: Get 10 cookies, filename is results/firefox_cookie.json -[x]: Get 33 history, filename is results/firefox_history.json -[x]: Get 1 passwords, filename is results/firefox_password.json -[x]: Get 1 passwords, filename is results/chrome_password.json -[x]: Get 4 bookmarks, filename is results/chrome_bookmark.json -[x]: Get 6 cookies, filename is results/chrome_cookie.json -[x]: Get 6 history, filename is results/chrome_history.json -[x]: Compress success, zip filename is results/archive.zip -``` -### Run with custom browser profile path + --verbose, --vv verbose (default: false) + --compress, --zip compress result to zip (default: false) + --browser value, -b value available browsers: all|chrome|opera-gx|vivaldi|coccoc|brave|edge|chromium|chrome-beta|opera|yandex|firefox (default: "all") + --results-dir value, --dir value export dir (default: "results") + --format value, -f value file name csv|json (default: "csv") + --profile-path value, -p value custom profile dir path, get with chrome://version + --help, -h show help (default: false) + --version, -v print the version (default: false) + + +PS C:\test> .\hack-browser-data.exe -b all -f json --dir results -zip +[NOTICE] [browser.go:46,pickChromium] find browser Chrome success +[NOTICE] [browser.go:46,pickChromium] find browser Microsoft Edge success +[NOTICE] [browsingdata.go:59,Output] output to file results/microsoft_edge_download.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/microsoft_edge_password.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/microsoft_edge_creditcard.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/microsoft_edge_bookmark.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/microsoft_edge_cookie.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/microsoft_edge_history.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_history.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_download.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_password.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_creditcard.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_bookmark.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_cookie.json success ``` -PS C:\Users\User\Desktop> .\hack-browser-data.exe -b edge -p 'C:\Users\User\AppData\Local\Microsoft\Edge\User Data\Default' -k 'C:\Users\User\AppData\Local\Microsoft\Edge\User Data\Local State' +### Run with custom browser profile folder -[x]: Get 29 history, filename is results/microsoft_edge_history.csv -[x]: Get 0 passwords, filename is results/microsoft_edge_password.csv -[x]: Get 1 credit cards, filename is results/microsoft_edge_credit.csv -[x]: Get 4 bookmarks, filename is results/microsoft_edge_bookmark.csv -[x]: Get 54 cookies, filename is results/microsoft_edge_cookie.csv - - -PS C:\Users\User\Desktop> .\hack-browser-data.exe -b edge -p 'C:\Users\User\AppData\Local\Microsoft\Edge\User Data\Default' - -[x]: Get 1 credit cards, filename is results/microsoft_edge_credit.csv -[x]: Get 4 bookmarks, filename is results/microsoft_edge_bookmark.csv -[x]: Get 54 cookies, filename is results/microsoft_edge_cookie.csv -[x]: Get 29 history, filename is results/microsoft_edge_history.csv -[x]: Get 0 passwords, filename is results/microsoft_edge_password.csv +``` +PS C:\Users\User\Desktop> .\hack-browser-data.exe -b chrome -p 'C:\Users\User\AppData\Local\Microsoft\Edge\User Data\Default' + +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_creditcard.csv success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_bookmark.csv success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_cookie.csv success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_history.csv success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_download.csv success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_password.csv success ``` ### Some other projects based on HackBrowserData [Sharp-HackBrowserData](https://github.com/S3cur3Th1sSh1t/Sharp-HackBrowserData) [Reflective-HackBrowserData](https://github.com/idiotc4t/Reflective-HackBrowserData) -... ## Contributors diff --git a/README_ZH.md b/README_ZH.md index c12362b..1863d44 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -80,16 +80,14 @@ ### 从源码编译 -支持版本 `go 1.14+` +仅支持 `go 1.18+` 以后版本,一些函数使用到了泛型 ``` bash -git clone https://github.com/moonD4rk/HackBrowserData +$ git clone https://github.com/moonD4rk/HackBrowserData -cd HackBrowserData +$ cd HackBrowserData/cmd/hack-browser-data -go get -v -t -d ./... - -go build +$ CGO_ENABLED=1 go build ``` ### 跨平台编译 @@ -122,43 +120,39 @@ NAME: USAGE: [hack-browser-data -b chrome -f json -dir results -cc] - Get all data(password/cookie/history/bookmark) from chrome + Export all browingdata(password/cookie/history/bookmark) from browser + Github Link: https://github.com/moonD4rk/HackBrowserData VERSION: - 0.3.7 + 0.4.0 + GLOBAL OPTIONS: - --verbose, --vv verbose (default: false) - --compress, --cc compress result to zip (default: false) - --browser value, -b value available browsers: all|opera|firefox|chrome|edge (default: "all") - --results-dir value, --dir value export dir (default: "results") - --format value, -f value format, csv|json|console (default: "csv") - --profile-dir-path value, -p value custom profile dir path, get with chrome://version - --key-file-path value, -k value custom key file path - --help, -h show help (default: false) - --version, -v print the version (default: false) - -PS C:\test> .\hack-browser-data.exe -b all -f json --dir results --cc -[x]: Get 44 cookies, filename is results/microsoft_edge_cookie.json -[x]: Get 54 history, filename is results/microsoft_edge_history.json -[x]: Get 1 passwords, filename is results/microsoft_edge_password.json -[x]: Get 4 bookmarks, filename is results/microsoft_edge_bookmark.json -[x]: Get 6 bookmarks, filename is results/360speed_bookmark.json -[x]: Get 19 cookies, filename is results/360speed_cookie.json -[x]: Get 18 history, filename is results/360speed_history.json -[x]: Get 1 passwords, filename is results/360speed_password.json -[x]: Get 12 history, filename is results/qq_history.json -[x]: Get 1 passwords, filename is results/qq_password.json -[x]: Get 12 bookmarks, filename is results/qq_bookmark.json -[x]: Get 14 cookies, filename is results/qq_cookie.json -[x]: Get 28 bookmarks, filename is results/firefox_bookmark.json -[x]: Get 10 cookies, filename is results/firefox_cookie.json -[x]: Get 33 history, filename is results/firefox_history.json -[x]: Get 1 passwords, filename is results/firefox_password.json -[x]: Get 1 passwords, filename is results/chrome_password.json -[x]: Get 4 bookmarks, filename is results/chrome_bookmark.json -[x]: Get 6 cookies, filename is results/chrome_cookie.json -[x]: Get 6 history, filename is results/chrome_history.json -[x]: Compress success, zip filename is results/archive.zip + --verbose, --vv verbose (default: false) + --compress, --zip compress result to zip (default: false) + --browser value, -b value available browsers: all|chrome|opera-gx|vivaldi|coccoc|brave|edge|chromium|chrome-beta|opera|yandex|firefox (default: "all") + --results-dir value, --dir value export dir (default: "results") + --format value, -f value file name csv|json (default: "csv") + --profile-path value, -p value custom profile dir path, get with chrome://version + --help, -h show help (default: false) + --version, -v print the version (default: false) + + +PS C:\test> .\hack-browser-data.exe -b all -f json --dir results -zip +[NOTICE] [browser.go:46,pickChromium] find browser Chrome success +[NOTICE] [browser.go:46,pickChromium] find browser Microsoft Edge success +[NOTICE] [browsingdata.go:59,Output] output to file results/microsoft_edge_download.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/microsoft_edge_password.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/microsoft_edge_creditcard.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/microsoft_edge_bookmark.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/microsoft_edge_cookie.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/microsoft_edge_history.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_history.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_download.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_password.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_creditcard.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_bookmark.json success +[NOTICE] [browsingdata.go:59,Output] output to file results/chrome_cookie.json success + ``` ### 基于此工具的一些其他项目 diff --git a/cmd/hack-browser-data/main.go b/cmd/hack-browser-data/main.go index b480958..5ab77f3 100644 --- a/cmd/hack-browser-data/main.go +++ b/cmd/hack-browser-data/main.go @@ -1,13 +1,11 @@ package main import ( - "fmt" "os" "strings" "hack-browser-data/internal/browser" "hack-browser-data/internal/log" - "hack-browser-data/internal/outputter" "hack-browser-data/internal/utils/fileutil" "github.com/urfave/cli/v2" @@ -30,14 +28,14 @@ func Execute() { app := &cli.App{ Name: "hack-browser-data", Usage: "Export passwords/cookies/history/bookmarks from browser", - UsageText: "[hack-browser-data -b chrome -f json -dir results -cc]\nGet all browingdata(password/cookie/history/bookmark) 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.0", Flags: []cli.Flag{ &cli.BoolFlag{Name: "verbose", Aliases: []string{"vv"}, Destination: &verbose, Value: false, Usage: "verbose"}, - &cli.BoolFlag{Name: "compress", Aliases: []string{"cc"}, Destination: &compress, Value: false, Usage: "compress result to zip"}, + &cli.BoolFlag{Name: "compress", Aliases: []string{"zip"}, Destination: &compress, Value: false, Usage: "compress result to zip"}, &cli.StringFlag{Name: "browser", Aliases: []string{"b"}, Destination: &browserName, Value: "all", Usage: "available browsers: all|" + strings.Join(browser.ListBrowser(), "|")}, &cli.StringFlag{Name: "results-dir", Aliases: []string{"dir"}, Destination: &outputDir, Value: "results", Usage: "export dir"}, - &cli.StringFlag{Name: "format", Aliases: []string{"f"}, Destination: &outputFormat, Value: "csv", Usage: "format, csv|json|console"}, + &cli.StringFlag{Name: "format", Aliases: []string{"f"}, Destination: &outputFormat, Value: "csv", Usage: "file name csv|json"}, &cli.StringFlag{Name: "profile-path", Aliases: []string{"p"}, Destination: &profilePath, Value: "", Usage: "custom profile dir path, get with chrome://version"}, }, HideHelpCommand: true, @@ -45,7 +43,7 @@ func Execute() { if verbose { log.Init("debug") } else { - log.Init("error") + log.Init("notice") } var ( browsers []browser.Browser @@ -56,29 +54,16 @@ func Execute() { if err != nil { log.Error(err) } - output := outputter.New(outputFormat) for _, b := range browsers { - data, err := b.GetBrowsingData() + data, err := b.BrowsingData() if err != nil { log.Error(err) } - var f *os.File - for _, source := range data.Sources { - filename := fmt.Sprintf("%s_%s.%s", b.Name(), source.Name(), outputFormat) - f, err = output.CreateFile(outputDir, filename) - if err != nil { - log.Error(err) - } - err = output.Write(source, f) - if err != nil { - log.Error(err) - } - } + data.Output(outputDir, b.Name(), outputFormat) } if compress { - err = fileutil.CompressDir(outputDir) - if err != nil { + if err = fileutil.CompressDir(outputDir); err != nil { log.Error(err) } } diff --git a/internal/browingdata/bookmark.go b/internal/browingdata/bookmark/bookmark.go similarity index 92% rename from internal/browingdata/bookmark.go rename to internal/browingdata/bookmark/bookmark.go index ec2a17c..2a79058 100644 --- a/internal/browingdata/bookmark.go +++ b/internal/browingdata/bookmark/bookmark.go @@ -1,4 +1,4 @@ -package browingdata +package bookmark import ( "database/sql" @@ -82,6 +82,11 @@ func (c *ChromiumBookmark) Name() string { type FirefoxBookmark []bookmark +const ( + queryFirefoxBookMark = `SELECT id, url, type, dateAdded, title FROM (SELECT * FROM moz_bookmarks INNER JOIN moz_places ON moz_bookmarks.fk=moz_places.id)` + closeJournalMode = `PRAGMA journal_mode=off` +) + func (f *FirefoxBookmark) Parse(masterKey []byte) error { var ( err error diff --git a/internal/browingdata/browsingdata.go b/internal/browingdata/browsingdata.go index aeed250..adf8128 100644 --- a/internal/browingdata/browsingdata.go +++ b/internal/browingdata/browsingdata.go @@ -1,10 +1,17 @@ package browingdata import ( - "time" + "path" + "hack-browser-data/internal/browingdata/bookmark" + "hack-browser-data/internal/browingdata/cookie" + "hack-browser-data/internal/browingdata/creditcard" + "hack-browser-data/internal/browingdata/download" + "hack-browser-data/internal/browingdata/history" + "hack-browser-data/internal/browingdata/password" "hack-browser-data/internal/item" "hack-browser-data/internal/log" + "hack-browser-data/internal/utils/fileutil" ) type Data struct { @@ -35,96 +42,53 @@ func (d *Data) Recovery(masterKey []byte) error { return nil } +func (d *Data) Output(dir, browserName, flag string) { + output := NewOutPutter(flag) + + for _, source := range d.Sources { + + filename := fileutil.Filename(browserName, source.Name(), output.Ext()) + + f, err := output.CreateFile(dir, filename) + if err != nil { + log.Error(err) + } + if err := output.Write(source, f); err != nil { + log.Error(err) + } + log.Noticef("output to file %s success", path.Join(dir, filename)) + } +} + func (d *Data) addSource(Sources []item.Item) { for _, source := range Sources { switch source { case item.ChromiumPassword: - d.Sources[source] = &ChromiumPassword{} + d.Sources[source] = &password.ChromiumPassword{} case item.ChromiumCookie: - d.Sources[source] = &ChromiumCookie{} + d.Sources[source] = &cookie.ChromiumCookie{} case item.ChromiumBookmark: - d.Sources[source] = &ChromiumBookmark{} + d.Sources[source] = &bookmark.ChromiumBookmark{} case item.ChromiumHistory: - d.Sources[source] = &ChromiumHistory{} + d.Sources[source] = &history.ChromiumHistory{} case item.ChromiumDownload: - d.Sources[source] = &ChromiumDownload{} + d.Sources[source] = &download.ChromiumDownload{} case item.ChromiumCreditCard: - d.Sources[source] = &ChromiumCreditCard{} + d.Sources[source] = &creditcard.ChromiumCreditCard{} case item.YandexPassword: - d.Sources[source] = &YandexPassword{} + d.Sources[source] = &password.YandexPassword{} case item.YandexCreditCard: - d.Sources[source] = &YandexCreditCard{} + d.Sources[source] = &creditcard.YandexCreditCard{} case item.FirefoxPassword: - d.Sources[source] = &FirefoxPassword{} + d.Sources[source] = &password.FirefoxPassword{} case item.FirefoxCookie: - d.Sources[source] = &FirefoxCookie{} + d.Sources[source] = &cookie.FirefoxCookie{} case item.FirefoxBookmark: - d.Sources[source] = &FirefoxBookmark{} + d.Sources[source] = &bookmark.FirefoxBookmark{} case item.FirefoxHistory: - d.Sources[source] = &FirefoxHistory{} + d.Sources[source] = &history.FirefoxHistory{} case item.FirefoxDownload: - d.Sources[source] = &FirefoxDownload{} + d.Sources[source] = &download.FirefoxDownload{} } } } - -const ( - queryChromiumCredit = `SELECT guid, name_on_card, expiration_month, expiration_year, card_number_encrypted FROM credit_cards` - queryChromiumLogin = `SELECT origin_url, username_value, password_value, date_created FROM logins` - queryYandexLogin = `SELECT action_url, username_value, password_value, date_created FROM logins` - queryChromiumHistory = `SELECT url, title, visit_count, last_visit_time FROM urls` - queryChromiumDownload = `SELECT target_path, tab_url, total_bytes, start_time, end_time, mime_type FROM downloads` - queryChromiumCookie = `SELECT name, encrypted_value, host_key, path, creation_utc, expires_utc, is_secure, is_httponly, has_expires, is_persistent FROM cookies` - queryFirefoxHistory = `SELECT id, url, last_visit_date, title, visit_count FROM moz_places where title not null` - queryFirefoxDownload = `SELECT place_id, GROUP_CONCAT(content), url, dateAdded FROM (SELECT * FROM moz_annos INNER JOIN moz_places ON moz_annos.place_id=moz_places.id) t GROUP BY place_id` - queryFirefoxBookMark = `SELECT id, url, type, dateAdded, title FROM (SELECT * FROM moz_bookmarks INNER JOIN moz_places ON moz_bookmarks.fk=moz_places.id)` - queryFirefoxCookie = `SELECT name, value, host, path, creationTime, expiry, isSecure, isHttpOnly FROM moz_cookies` - queryMetaData = `SELECT item1, item2 FROM metaData WHERE id = 'password'` - queryNssPrivate = `SELECT a11, a102 from nssPrivate` - closeJournalMode = `PRAGMA journal_mode=off` -) - -type ( - loginData struct { - UserName string - encryptPass []byte - encryptUser []byte - Password string - LoginUrl string - CreateDate time.Time - } - cookie struct { - Host string - Path string - KeyName string - encryptValue []byte - Value string - IsSecure bool - IsHTTPOnly bool - HasExpire bool - IsPersistent bool - CreateDate time.Time - ExpireDate time.Time - } - history struct { - Title string - Url string - VisitCount int - LastVisitTime time.Time - } - download struct { - TargetPath string - Url string - TotalBytes int64 - StartTime time.Time - EndTime time.Time - MimeType string - } - card struct { - GUID string - Name string - ExpirationYear string - ExpirationMonth string - CardNumber string - } -) diff --git a/internal/browingdata/cookie.go b/internal/browingdata/cookie/cookie.go similarity index 83% rename from internal/browingdata/cookie.go rename to internal/browingdata/cookie/cookie.go index 8ea4252..08aca0a 100644 --- a/internal/browingdata/cookie.go +++ b/internal/browingdata/cookie/cookie.go @@ -1,9 +1,10 @@ -package browingdata +package cookie import ( "database/sql" "os" "sort" + "time" _ "github.com/mattn/go-sqlite3" @@ -15,6 +16,24 @@ import ( type ChromiumCookie []cookie +type cookie struct { + Host string + Path string + KeyName string + encryptValue []byte + Value string + IsSecure bool + IsHTTPOnly bool + HasExpire bool + IsPersistent bool + CreateDate time.Time + ExpireDate time.Time +} + +const ( + queryChromiumCookie = `SELECT name, encrypted_value, host_key, path, creation_utc, expires_utc, is_secure, is_httponly, has_expires, is_persistent FROM cookies` +) + func (c *ChromiumCookie) Parse(masterKey []byte) error { cookieDB, err := sql.Open("sqlite3", item.TempChromiumCookie) if err != nil { @@ -77,6 +96,10 @@ func (c *ChromiumCookie) Name() string { type FirefoxCookie []cookie +const ( + queryFirefoxCookie = `SELECT name, value, host, path, creationTime, expiry, isSecure, isHttpOnly FROM moz_cookies` +) + func (f *FirefoxCookie) Parse(masterKey []byte) error { cookieDB, err := sql.Open("sqlite3", item.TempFirefoxCookie) if err != nil { diff --git a/internal/browingdata/creditcard.go b/internal/browingdata/creditcard/creditcard.go similarity index 67% rename from internal/browingdata/creditcard.go rename to internal/browingdata/creditcard/creditcard.go index 8ac135e..3580f11 100644 --- a/internal/browingdata/creditcard.go +++ b/internal/browingdata/creditcard/creditcard.go @@ -1,4 +1,4 @@ -package browingdata +package creditcard import ( "database/sql" @@ -13,6 +13,20 @@ import ( type ChromiumCreditCard []card +type card struct { + GUID string + Name string + ExpirationYear string + ExpirationMonth string + CardNumber string + Address string + NickName string +} + +const ( + queryChromiumCredit = `SELECT guid, name_on_card, expiration_month, expiration_year, card_number_encrypted, billing_address_id, nickname FROM credit_cards` +) + func (c *ChromiumCreditCard) Parse(masterKey []byte) error { creditDB, err := sql.Open("sqlite3", item.TempChromiumCreditCard) if err != nil { @@ -27,17 +41,19 @@ func (c *ChromiumCreditCard) Parse(masterKey []byte) error { defer rows.Close() for rows.Next() { var ( - name, month, year, guid string - value, encryptValue []byte + name, month, year, guid, address, nickname string + value, encryptValue []byte ) - if err := rows.Scan(&guid, &name, &month, &year, &encryptValue); err != nil { + if err := rows.Scan(&guid, &name, &month, &year, &encryptValue, &address, &nickname); err != nil { log.Warn(err) } - creditCardInfo := card{ + ccInfo := card{ GUID: guid, Name: name, ExpirationMonth: month, ExpirationYear: year, + Address: address, + NickName: nickname, } if masterKey == nil { value, err = decrypter.DPApi(encryptValue) @@ -50,8 +66,8 @@ func (c *ChromiumCreditCard) Parse(masterKey []byte) error { return err } } - creditCardInfo.CardNumber = string(value) - *c = append(*c, creditCardInfo) + ccInfo.CardNumber = string(value) + *c = append(*c, ccInfo) } return nil } @@ -68,6 +84,7 @@ func (c *YandexCreditCard) Parse(masterKey []byte) error { } defer os.Remove(item.TempYandexCreditCard) defer creditDB.Close() + defer creditDB.Close() rows, err := creditDB.Query(queryChromiumCredit) if err != nil { return err @@ -75,17 +92,19 @@ func (c *YandexCreditCard) Parse(masterKey []byte) error { defer rows.Close() for rows.Next() { var ( - name, month, year, guid string - value, encryptValue []byte + name, month, year, guid, address, nickname string + value, encryptValue []byte ) - if err := rows.Scan(&guid, &name, &month, &year, &encryptValue); err != nil { + if err := rows.Scan(&guid, &name, &month, &year, &encryptValue, &address, &nickname); err != nil { log.Warn(err) } - creditCardInfo := card{ + ccInfo := card{ GUID: guid, Name: name, ExpirationMonth: month, ExpirationYear: year, + Address: address, + NickName: nickname, } if masterKey == nil { value, err = decrypter.DPApi(encryptValue) @@ -98,8 +117,8 @@ func (c *YandexCreditCard) Parse(masterKey []byte) error { return err } } - creditCardInfo.CardNumber = string(value) - *c = append(*c, creditCardInfo) + ccInfo.CardNumber = string(value) + *c = append(*c, ccInfo) } return nil } diff --git a/internal/browingdata/download.go b/internal/browingdata/download/download.go similarity index 82% rename from internal/browingdata/download.go rename to internal/browingdata/download/download.go index 8761bd8..ae7a939 100644 --- a/internal/browingdata/download.go +++ b/internal/browingdata/download/download.go @@ -1,10 +1,11 @@ -package browingdata +package download import ( "database/sql" "os" "sort" "strings" + "time" "hack-browser-data/internal/item" "hack-browser-data/internal/log" @@ -16,6 +17,19 @@ import ( type ChromiumDownload []download +type download struct { + TargetPath string + Url string + TotalBytes int64 + StartTime time.Time + EndTime time.Time + MimeType string +} + +const ( + queryChromiumDownload = `SELECT target_path, tab_url, total_bytes, start_time, end_time, mime_type FROM downloads` +) + func (c *ChromiumDownload) Parse(masterKey []byte) error { historyDB, err := sql.Open("sqlite3", item.TempChromiumDownload) if err != nil { @@ -58,6 +72,11 @@ func (c *ChromiumDownload) Name() string { type FirefoxDownload []download +const ( + queryFirefoxDownload = `SELECT place_id, GROUP_CONCAT(content), url, dateAdded FROM (SELECT * FROM moz_annos INNER JOIN moz_places ON moz_annos.place_id=moz_places.id) t GROUP BY place_id` + closeJournalMode = `PRAGMA journal_mode=off` +) + func (f *FirefoxDownload) Parse(masterKey []byte) error { var ( err error diff --git a/internal/browingdata/history.go b/internal/browingdata/history/history.go similarity index 84% rename from internal/browingdata/history.go rename to internal/browingdata/history/history.go index c7fb322..c1f5aa7 100644 --- a/internal/browingdata/history.go +++ b/internal/browingdata/history/history.go @@ -1,9 +1,10 @@ -package browingdata +package history import ( "database/sql" "os" "sort" + "time" _ "github.com/mattn/go-sqlite3" @@ -14,6 +15,17 @@ import ( type ChromiumHistory []history +type history struct { + Title string + Url string + VisitCount int + LastVisitTime time.Time +} + +const ( + queryChromiumHistory = `SELECT url, title, visit_count, last_visit_time FROM urls` +) + func (c *ChromiumHistory) Parse(masterKey []byte) error { historyDB, err := sql.Open("sqlite3", item.TempChromiumHistory) if err != nil { @@ -56,6 +68,11 @@ func (c *ChromiumHistory) Name() string { type FirefoxHistory []history +const ( + queryFirefoxHistory = `SELECT id, url, last_visit_date, title, visit_count FROM moz_places where title not null` + closeJournalMode = `PRAGMA journal_mode=off` +) + func (f *FirefoxHistory) Parse(masterKey []byte) error { var ( err error diff --git a/internal/outputter/outputter.go b/internal/browingdata/outputter.go similarity index 76% rename from internal/outputter/outputter.go rename to internal/browingdata/outputter.go index 7548d7d..d87ffef 100644 --- a/internal/outputter/outputter.go +++ b/internal/browingdata/outputter.go @@ -1,4 +1,4 @@ -package outputter +package browingdata import ( "encoding/csv" @@ -9,17 +9,15 @@ import ( "github.com/gocarina/gocsv" jsoniter "github.com/json-iterator/go" - - "hack-browser-data/internal/browingdata" ) -type outPutter struct { +type OutPutter struct { json bool csv bool } -func New(flag string) *outPutter { - o := &outPutter{} +func NewOutPutter(flag string) *OutPutter { + o := &OutPutter{} if flag == "json" { o.json = true } else { @@ -28,7 +26,7 @@ func New(flag string) *outPutter { return o } -func (o *outPutter) Write(data browingdata.Source, writer io.Writer) error { +func (o *OutPutter) Write(data Source, writer io.Writer) error { switch o.json { case true: encoder := jsoniter.NewEncoder(writer) @@ -45,7 +43,7 @@ func (o *outPutter) Write(data browingdata.Source, writer io.Writer) error { } } -func (o *outPutter) CreateFile(dir, filename string) (*os.File, error) { +func (o *OutPutter) CreateFile(dir, filename string) (*os.File, error) { if filename == "" { return nil, errors.New("empty filename") } @@ -68,3 +66,11 @@ func (o *outPutter) CreateFile(dir, filename string) (*os.File, error) { } return file, nil } + +func (o *OutPutter) Ext() string { + if o.json { + return "json" + } else { + return "csv" + } +} diff --git a/internal/outputter/outputter_test.go b/internal/browingdata/outputter_test.go similarity index 88% rename from internal/outputter/outputter_test.go rename to internal/browingdata/outputter_test.go index e0785c4..83f8361 100644 --- a/internal/outputter/outputter_test.go +++ b/internal/browingdata/outputter_test.go @@ -1,4 +1,4 @@ -package outputter +package browingdata import ( "os" @@ -6,7 +6,7 @@ import ( ) func TestNewOutPutter(t *testing.T) { - out := New("json") + out := NewOutPutter("json") if out == nil { t.Error("New() returned nil") } diff --git a/internal/browingdata/password.go b/internal/browingdata/password/password.go similarity index 90% rename from internal/browingdata/password.go rename to internal/browingdata/password/password.go index d4b2110..dc264b6 100644 --- a/internal/browingdata/password.go +++ b/internal/browingdata/password/password.go @@ -1,4 +1,4 @@ -package browingdata +package password import ( "bytes" @@ -20,6 +20,19 @@ import ( type ChromiumPassword []loginData +type loginData struct { + UserName string + encryptPass []byte + encryptUser []byte + Password string + LoginUrl string + CreateDate time.Time +} + +const ( + queryChromiumLogin = `SELECT origin_url, username_value, password_value, date_created FROM logins` +) + func (c *ChromiumPassword) Parse(masterKey []byte) error { loginDB, err := sql.Open("sqlite3", item.TempChromiumPassword) if err != nil { @@ -79,6 +92,10 @@ func (c *ChromiumPassword) Name() string { type YandexPassword []loginData +const ( + queryYandexLogin = `SELECT action_url, username_value, password_value, date_created FROM logins` +) + func (c *YandexPassword) Parse(masterKey []byte) error { loginDB, err := sql.Open("sqlite3", item.TempYandexPassword) if err != nil { @@ -139,6 +156,11 @@ func (c *YandexPassword) Name() string { type FirefoxPassword []loginData +const ( + queryMetaData = `SELECT item1, item2 FROM metaData WHERE id = 'password'` + queryNssPrivate = `SELECT a11, a102 from nssPrivate` +) + func (f *FirefoxPassword) Parse(masterKey []byte) error { globalSalt, metaBytes, nssA11, nssA102, err := getFirefoxDecryptKey(item.TempFirefoxKey4) if err != nil { @@ -189,8 +211,8 @@ func (f *FirefoxPassword) Parse(masterKey []byte) error { } *f = append(*f, loginData{ LoginUrl: v.LoginUrl, - UserName: string(decrypter.PKCS5UnPadding(user)), - Password: string(decrypter.PKCS5UnPadding(pwd)), + UserName: string(user), + Password: string(pwd), CreateDate: v.CreateDate, }) } diff --git a/internal/browser/browser.go b/internal/browser/browser.go index 11ee860..badecd3 100644 --- a/internal/browser/browser.go +++ b/internal/browser/browser.go @@ -9,14 +9,14 @@ import ( "hack-browser-data/internal/browser/firefox" "hack-browser-data/internal/log" "hack-browser-data/internal/utils/fileutil" + "hack-browser-data/internal/utils/typeutil" ) type Browser interface { + // Name is browser's name Name() string - - GetMasterKey() ([]byte, error) - // GetBrowsingData returns the browsing data for the browser. - GetBrowsingData() (*browingdata.Data, error) + // BrowsingData returns all browsing data in the browser. + BrowsingData() (*browingdata.Data, error) } func PickBrowser(name, profile string) ([]Browser, error) { @@ -47,11 +47,11 @@ func pickChromium(name, profile string) []Browser { browsers = append(browsers, b) } else { // TODO: show which browser find failed - if strings.Contains(err.Error(), "profile path is not exist") { - log.Infof("find browser %s failed, profile path is not exist", v.name) + if strings.Contains(err.Error(), "profile folder is not exist") { + log.Errorf("find browser %s failed, profile folder is not exist, maybe not installed", v.name) continue } else { - log.Error("new chromium error:", err) + log.Errorf("new chromium error:", err) } } } @@ -62,10 +62,10 @@ func pickChromium(name, profile string) []Browser { } b, err := chromium.New(c.name, c.storage, profile, c.items) if err != nil { - if strings.Contains(err.Error(), "profile path is not exist") { - log.Infof("find browser %s failed, profile path is not exist", c.name) + if strings.Contains(err.Error(), "profile folder is not exist") { + log.Fatalf("find browser %s failed, profile folder is not exist, maybe not installed", c.name) } else { - log.Error("new chromium error:", err) + log.Fatalf("new chromium error:", err) } } browsers = append(browsers, b) @@ -85,12 +85,12 @@ func pickFirefox(name, profile string) []Browser { } if multiFirefox, err := firefox.New(v.name, v.storage, profile, v.items); err == nil { for _, b := range multiFirefox { - log.Noticef("find browser: firefox %s success", b.Name()) + log.Noticef("find browser firefox %s success", b.Name()) browsers = append(browsers, b) } } else { - if strings.Contains(err.Error(), "profile path is not exist") { - log.Noticef("find browser: firefox %s failed, profile path is not exist", v.name) + if strings.Contains(err.Error(), "profile folder is not exist") { + log.Errorf("find browser firefox %s failed, profile folder is not exist", v.name) } else { log.Error(err) } @@ -104,12 +104,8 @@ func pickFirefox(name, profile string) []Browser { func ListBrowser() []string { var l []string - for c := range chromiumList { - l = append(l, c) - } - for f := range firefoxList { - l = append(l, f) - } + l = append(l, typeutil.Keys(chromiumList)...) + l = append(l, typeutil.Keys(firefoxList)...) return l } diff --git a/internal/browser/chromium/chromium.go b/internal/browser/chromium/chromium.go index 135a6d1..3ffe53c 100644 --- a/internal/browser/chromium/chromium.go +++ b/internal/browser/chromium/chromium.go @@ -30,7 +30,7 @@ func New(name, storage, profilePath string, items []item.Item) (*chromium, error } // TODO: Handle file path is not exist if !fileutil.FolderExists(profilePath) { - return nil, fmt.Errorf("%s profile path is not exist: %s", name, profilePath) + return nil, fmt.Errorf("%s profile folder is not exist: %s", name, profilePath) } itemsPaths, err := c.getItemPath(profilePath, items) if err != nil { @@ -46,7 +46,7 @@ func (c *chromium) Name() string { return c.name } -func (c *chromium) GetBrowsingData() (*browingdata.Data, error) { +func (c *chromium) BrowsingData() (*browingdata.Data, error) { b := browingdata.New(c.items) if err := c.copyItemToLocal(); err != nil { @@ -72,7 +72,7 @@ func (c *chromium) copyItemToLocal() error { // TODO: Handle read file error d, err := ioutil.ReadFile(path) if err != nil { - fmt.Println(err.Error()) + return err } err = ioutil.WriteFile(filename, d, 0777) if err != nil { diff --git a/internal/browser/chromium/chromium_linux.go b/internal/browser/chromium/chromium_linux.go index 6af3a81..76419a9 100644 --- a/internal/browser/chromium/chromium_linux.go +++ b/internal/browser/chromium/chromium_linux.go @@ -2,6 +2,7 @@ package chromium import ( "crypto/sha1" + "errors" "os" "github.com/godbus/dbus/v5" @@ -49,8 +50,7 @@ func (c *chromium) GetMasterKey() ([]byte, error) { if label == c.storage { se, err := i.GetSecret(session.Path()) if err != nil { - log.Error(err) - return nil, err + return nil, errors.New("get storage from dbus error:" + err.Error()) } chromiumSecret = se.Value } diff --git a/internal/browser/firefox/firefox.go b/internal/browser/firefox/firefox.go index eea438a..4c15107 100644 --- a/internal/browser/firefox/firefox.go +++ b/internal/browser/firefox/firefox.go @@ -26,7 +26,7 @@ type firefox struct { // New returns a new firefox instance. func New(name, storage, profilePath string, items []item.Item) ([]*firefox, error) { if !fileutil.FolderExists(profilePath) { - return nil, fmt.Errorf("%s profile path is not exist: %s", name, profilePath) + return nil, fmt.Errorf("%s profile folder is not exist: %s", name, profilePath) } f := &firefox{ name: name, @@ -36,7 +36,7 @@ func New(name, storage, profilePath string, items []item.Item) ([]*firefox, erro } multiItemPaths, err := f.getMultiItemPath(f.profilePath, f.items) if err != nil { - if strings.Contains(err.Error(), "profile path is not exist") { + if strings.Contains(err.Error(), "profile folder is not exist") { log.Error(err) return nil, nil } @@ -45,7 +45,7 @@ func New(name, storage, profilePath string, items []item.Item) ([]*firefox, erro var firefoxList []*firefox for name, itemPaths := range multiItemPaths { firefoxList = append(firefoxList, &firefox{ - name: name, + name: fmt.Sprintf("firefox-%s", name), items: typeutil.Keys(itemPaths), itemPaths: itemPaths, }) @@ -66,7 +66,7 @@ func (f *firefox) copyItemToLocal() error { // TODO: Handle read file error d, err := ioutil.ReadFile(path) if err != nil { - fmt.Println(err.Error()) + return err } err = ioutil.WriteFile(filename, d, 0777) if err != nil { @@ -100,7 +100,7 @@ func (f *firefox) Name() string { return f.name } -func (f *firefox) GetBrowsingData() (*browingdata.Data, error) { +func (f *firefox) BrowsingData() (*browingdata.Data, error) { b := browingdata.New(f.items) if err := f.copyItemToLocal(); err != nil { diff --git a/internal/decrypter/decrypter.go b/internal/decrypter/decrypter.go index d93d58f..88b4081 100644 --- a/internal/decrypter/decrypter.go +++ b/internal/decrypter/decrypter.go @@ -179,14 +179,17 @@ func aes128CBCDecrypt(key, iv, encryptPass []byte) ([]byte, error) { dst := make([]byte, encryptLen) mode := cipher.NewCBCDecrypter(block, iv) mode.CryptBlocks(dst, encryptPass) - dst = PKCS5UnPadding(dst) + dst = pkcs5UnPadding(dst, block.BlockSize()) return dst, nil } -func PKCS5UnPadding(src []byte) []byte { - length := len(src) - unpad := int(src[length-1]) - return src[:(length - unpad)] +func pkcs5UnPadding(src []byte, blockSize int) []byte { + n := len(src) + paddingNum := int(src[n-1]) + if n < paddingNum || paddingNum > blockSize { + return src + } + return src[:n-paddingNum] } // des3Decrypt use for decrypter firefox PBE @@ -198,7 +201,7 @@ func des3Decrypt(key, iv []byte, src []byte) ([]byte, error) { blockMode := cipher.NewCBCDecrypter(block, iv) sq := make([]byte, len(src)) blockMode.CryptBlocks(sq, src) - return sq, nil + return pkcs5UnPadding(sq, block.BlockSize()), nil } func paddingZero(s []byte, l int) []byte { diff --git a/internal/log/log.go b/internal/log/log.go index 09b302d..b5bbd0b 100644 --- a/internal/log/log.go +++ b/internal/log/log.go @@ -13,11 +13,11 @@ func Init(l string) { if l == "debug" { std = newStdLogger(slog.DebugLevel) } else { - std = newStdLogger(slog.ErrorLevel) + std = newStdLogger(slog.NoticeLevel) } } -const template = "hack-browser-data [{{level}}] [{{caller}}] {{message}} {{data}} {{extra}}\n" +const template = "[{{level}}] [{{caller}}] {{message}} {{data}} {{extra}}\n" // NewStdLogger instance func newStdLogger(level slog.Level) *slog.SugaredLogger { diff --git a/internal/utils/fileutil/filetutil.go b/internal/utils/fileutil/filetutil.go index 6b7bf60..e2681a0 100644 --- a/internal/utils/fileutil/filetutil.go +++ b/internal/utils/fileutil/filetutil.go @@ -8,6 +8,7 @@ import ( "os" "path" "path/filepath" + "strings" "hack-browser-data/internal/item" "hack-browser-data/internal/log" @@ -51,7 +52,7 @@ func CopyItemToLocal(itemPaths map[item.Item]string) error { // TODO: Handle read file error d, err := ioutil.ReadFile(p) if err != nil { - fmt.Println(err.Error()) + log.Error(err.Error()) } err = ioutil.WriteFile(filename, d, 0777) if err != nil { @@ -61,6 +62,11 @@ func CopyItemToLocal(itemPaths map[item.Item]string) error { return nil } +func Filename(browser, item, ext string) string { + replace := strings.NewReplacer(" ", "_", ".", "_", "-", "_") + return strings.ToLower(fmt.Sprintf("%s_%s.%s", replace.Replace(browser), item, ext)) +} + func ParentDir(p string) string { return filepath.Dir(p) } @@ -101,7 +107,7 @@ func CompressDir(dir string) error { if err := zw.Close(); err != nil { return err } - filename := filepath.Join(dir, "archive.zip") + filename := filepath.Join(dir, fmt.Sprintf("%s.zip", dir)) outFile, err := os.Create(filename) if err != nil { return err @@ -110,6 +116,6 @@ func CompressDir(dir string) error { if err != nil { return err } - log.Debugf("Compress success, zip filename is %s", filename) + log.Noticef("compress success, zip filename is %s", filename) return nil }