feat: support export multiple user from browser

Merge pull request #143 from moonD4rk/feat/multiple-user
pull/144/head
ᴍᴏᴏɴD4ʀᴋ 3 years ago committed by GitHub
commit 5526441e21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      cmd/hack-browser-data/main.go
  2. 2
      internal/browingdata/browsingdata.go
  3. 2
      internal/browingdata/creditcard/creditcard.go
  4. 8
      internal/browingdata/localstorage/localstorage.go
  5. 4
      internal/browingdata/outputter.go
  6. 4
      internal/browingdata/password/password.go
  7. 25
      internal/browser/browser.go
  8. 30
      internal/browser/browser_windows.go
  9. 79
      internal/browser/chromium/chromium.go
  10. 4
      internal/browser/chromium/chromium_darwin.go
  11. 4
      internal/browser/chromium/chromium_linux.go
  12. 6
      internal/browser/chromium/chromium_windows.go
  13. 7
      internal/browser/firefox/firefox.go
  14. 2
      internal/decrypter/decrypter_darwin.go
  15. 2
      internal/decrypter/decrypter_linux.go
  16. 11
      internal/utils/fileutil/filetutil.go
  17. 2
      internal/utils/typeutil/typeutil.go
  18. 12
      internal/utils/typeutil/typeutil_test.go

@ -29,7 +29,7 @@ func Execute() {
Name: "hack-browser-data", Name: "hack-browser-data",
Usage: "Export passwords/cookies/history/bookmarks from browser", 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", 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.2", Version: "0.4.3",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{Name: "verbose", Aliases: []string{"vv"}, Destination: &verbose, Value: false, Usage: "verbose"}, &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"}, &cli.BoolFlag{Name: "compress", Aliases: []string{"zip"}, Destination: &compress, Value: false, Usage: "compress result to zip"},

@ -35,7 +35,6 @@ func New(sources []item.Item) *Data {
} }
func (d *Data) Recovery(masterKey []byte) error { func (d *Data) Recovery(masterKey []byte) error {
for _, source := range d.sources { for _, source := range d.sources {
if err := source.Parse(masterKey); err != nil { if err := source.Parse(masterKey); err != nil {
log.Errorf("parse %s error %s", source.Name(), err.Error()) log.Errorf("parse %s error %s", source.Name(), err.Error())
@ -48,7 +47,6 @@ func (d *Data) Output(dir, browserName, flag string) {
output := NewOutPutter(flag) output := NewOutPutter(flag)
for _, source := range d.sources { for _, source := range d.sources {
filename := fileutil.Filename(browserName, source.Name(), output.Ext()) filename := fileutil.Filename(browserName, source.Name(), output.Ext())
f, err := output.CreateFile(dir, filename) f, err := output.CreateFile(dir, filename)

@ -71,6 +71,7 @@ func (c *ChromiumCreditCard) Parse(masterKey []byte) error {
} }
return nil return nil
} }
func (c *ChromiumCreditCard) Name() string { func (c *ChromiumCreditCard) Name() string {
return "creditcard" return "creditcard"
} }
@ -122,6 +123,7 @@ func (c *YandexCreditCard) Parse(masterKey []byte) error {
} }
return nil return nil
} }
func (c *YandexCreditCard) Name() string { func (c *YandexCreditCard) Name() string {
return "creditcard" return "creditcard"
} }

@ -40,7 +40,7 @@ func (c *ChromiumLocalStorage) Parse(masterKey []byte) error {
if len(value) > 1024*5 { if len(value) > 1024*5 {
continue continue
} }
var s = new(storage) s := new(storage)
s.fillKey(key) s.fillKey(key)
s.fillValue(value) s.fillValue(value)
// don't save meta data // don't save meta data
@ -114,13 +114,11 @@ func (f *FirefoxLocalStorage) Parse(masterKey []byte) error {
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
var ( var originKey, key, value string
originKey, key, value string
)
if err = rows.Scan(&originKey, &key, &value); err != nil { if err = rows.Scan(&originKey, &key, &value); err != nil {
log.Warn(err) log.Warn(err)
} }
var s = new(storage) s := new(storage)
s.fillFirefox(originKey, key, value) s.fillFirefox(originKey, key, value)
*f = append(*f, *s) *f = append(*f, *s)
} }

@ -50,7 +50,7 @@ func (o *OutPutter) CreateFile(dir, filename string) (*os.File, error) {
if dir != "" { if dir != "" {
if _, err := os.Stat(dir); os.IsNotExist(err) { if _, err := os.Stat(dir); os.IsNotExist(err) {
err := os.MkdirAll(dir, 0777) err := os.MkdirAll(dir, 0o777)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -60,7 +60,7 @@ func (o *OutPutter) CreateFile(dir, filename string) (*os.File, error) {
var file *os.File var file *os.File
var err error var err error
p := filepath.Join(dir, filename) p := filepath.Join(dir, filename)
file, err = os.OpenFile(p, os.O_TRUNC|os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) file, err = os.OpenFile(p, os.O_TRUNC|os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o666)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -229,9 +229,7 @@ func (f *FirefoxPassword) Name() string {
} }
func getFirefoxDecryptKey(key4file string) (item1, item2, a11, a102 []byte, err error) { func getFirefoxDecryptKey(key4file string) (item1, item2, a11, a102 []byte, err error) {
var ( var keyDB *sql.DB
keyDB *sql.DB
)
keyDB, err = sql.Open("sqlite3", key4file) keyDB, err = sql.Open("sqlite3", key4file)
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err

@ -46,9 +46,12 @@ func pickChromium(name, profile string) []Browser {
log.Noticef("find browser %s failed, profile folder does not exist", v.name) log.Noticef("find browser %s failed, profile folder does not exist", v.name)
continue continue
} }
if b, err := chromium.New(v.name, v.storage, v.profilePath, v.items); err == nil { if multiChromium, err := chromium.New(v.name, v.storage, v.profilePath, v.items); err == nil {
log.Noticef("find browser %s success", b.Name()) log.Noticef("find browser %s success", v.name)
browsers = append(browsers, b) for _, b := range multiChromium {
log.Noticef("find browser %s success", b.Name())
browsers = append(browsers, b)
}
} else { } else {
log.Errorf("new chromium error: %s", err.Error()) log.Errorf("new chromium error: %s", err.Error())
} }
@ -61,12 +64,14 @@ func pickChromium(name, profile string) []Browser {
if !fileutil.FolderExists(filepath.Clean(profile)) { if !fileutil.FolderExists(filepath.Clean(profile)) {
log.Fatalf("find browser %s failed, profile folder does not exist", c.name) log.Fatalf("find browser %s failed, profile folder does not exist", c.name)
} }
b, err := chromium.New(c.name, c.storage, profile, c.items) chromiumList, err := chromium.New(c.name, c.storage, profile, c.items)
if err != nil { if err != nil {
log.Fatalf("new chromium error:", err) log.Fatalf("new chromium error: %s", err)
}
for _, b := range chromiumList {
log.Noticef("find browser %s success", b.Name())
browsers = append(browsers, b)
} }
log.Noticef("find browser %s success", b.Name())
browsers = append(browsers, b)
} }
return browsers return browsers
} }
@ -106,10 +111,8 @@ func ListBrowser() []string {
return l return l
} }
var ( // home dir path for all platforms
// home dir path for all platforms var homeDir, _ = os.UserHomeDir()
homeDir, _ = os.UserHomeDir()
)
const ( const (
chromeName = "Chrome" chromeName = "Chrome"

@ -15,7 +15,7 @@ var (
}{ }{
"chrome": { "chrome": {
name: chromeName, name: chromeName,
profilePath: chromeProfilePath, profilePath: chromeUserDataPath,
items: item.DefaultChromium, items: item.DefaultChromium,
}, },
"edge": { "edge": {
@ -25,12 +25,12 @@ var (
}, },
"chromium": { "chromium": {
name: chromiumName, name: chromiumName,
profilePath: chromiumProfilePath, profilePath: chromiumUserDataPath,
items: item.DefaultChromium, items: item.DefaultChromium,
}, },
"chrome-beta": { "chrome-beta": {
name: chromeBetaName, name: chromeBetaName,
profilePath: chromeBetaProfilePath, profilePath: chromeBetaUserDataPath,
items: item.DefaultChromium, items: item.DefaultChromium,
}, },
"opera": { "opera": {
@ -89,18 +89,18 @@ var (
) )
var ( var (
chromeProfilePath = homeDir + "/AppData/Local/Google/Chrome/User Data/Default/" chromeUserDataPath = homeDir + "/AppData/Local/Google/Chrome/User Data/Default/"
chromeBetaProfilePath = homeDir + "/AppData/Local/Google/Chrome Beta/User Data/Default/" chromeBetaUserDataPath = homeDir + "/AppData/Local/Google/Chrome Beta/User Data/Default/"
chromiumProfilePath = homeDir + "/AppData/Local/Chromium/User Data/Default/" chromiumUserDataPath = homeDir + "/AppData/Local/Chromium/User Data/Default/"
edgeProfilePath = homeDir + "/AppData/Local/Microsoft/Edge/User Data/Default/" edgeProfilePath = homeDir + "/AppData/Local/Microsoft/Edge/User Data/Default/"
braveProfilePath = homeDir + "/AppData/Local/BraveSoftware/Brave-Browser/User Data/Default/" braveProfilePath = homeDir + "/AppData/Local/BraveSoftware/Brave-Browser/User Data/Default/"
speed360ProfilePath = homeDir + "/AppData/Local/360chrome/Chrome/User Data/Default/" speed360ProfilePath = homeDir + "/AppData/Local/360chrome/Chrome/User Data/Default/"
qqBrowserProfilePath = homeDir + "/AppData/Local/Tencent/QQBrowser/User Data/Default/" qqBrowserProfilePath = homeDir + "/AppData/Local/Tencent/QQBrowser/User Data/Default/"
operaProfilePath = homeDir + "/AppData/Roaming/Opera Software/Opera Stable/Default/" operaProfilePath = homeDir + "/AppData/Roaming/Opera Software/Opera Stable/Default/"
operaGXProfilePath = homeDir + "/AppData/Roaming/Opera Software/Opera GX Stable/Default/" operaGXProfilePath = homeDir + "/AppData/Roaming/Opera Software/Opera GX Stable/"
vivaldiProfilePath = homeDir + "/AppData/Local/Vivaldi/User Data/Default/" vivaldiProfilePath = homeDir + "/AppData/Local/Vivaldi/User Data/Default/"
coccocProfilePath = homeDir + "/AppData/Local/CocCoc/Browser/User Data/Default/" coccocProfilePath = homeDir + "/AppData/Local/CocCoc/Browser/User Data/Default/"
yandexProfilePath = homeDir + "/AppData/Local/Yandex/YandexBrowser/User Data/Default/" yandexProfilePath = homeDir + "/AppData/Local/Yandex/YandexBrowser/User Data/Default/"
firefoxProfilePath = homeDir + "/AppData/Roaming/Mozilla/Firefox/Profiles/" firefoxProfilePath = homeDir + "/AppData/Roaming/Mozilla/Firefox/Profiles/"
) )

@ -1,7 +1,7 @@
package chromium package chromium
import ( import (
"os" "io/fs"
"path/filepath" "path/filepath"
"strings" "strings"
@ -21,19 +21,27 @@ type chromium struct {
} }
// New create instance of chromium browser, fill item's path if item is existed. // New create instance of chromium browser, fill item's path if item is existed.
func New(name, storage, profilePath string, items []item.Item) (*chromium, error) { func New(name, storage, profilePath string, items []item.Item) ([]*chromium, error) {
c := &chromium{ c := &chromium{
name: name, name: name,
storage: storage, storage: storage,
profilePath: profilePath,
items: items,
} }
itemsPaths, err := c.getItemPath(profilePath, items) multiItemPaths, err := c.getMultiItemPath(c.profilePath, c.items)
if err != nil { if err != nil {
return nil, err return nil, err
} }
c.profilePath = profilePath var chromiumList []*chromium
c.itemPaths = itemsPaths for user, itemPaths := range multiItemPaths {
c.items = typeutil.Keys(itemsPaths) chromiumList = append(chromiumList, &chromium{
return c, err name: fileutil.BrowserName(name, user),
items: typeutil.Keys(itemPaths),
itemPaths: itemPaths,
storage: storage,
})
}
return chromiumList, nil
} }
func (c *chromium) Name() string { func (c *chromium) Name() string {
@ -81,28 +89,49 @@ func (c *chromium) copyItemToLocal() error {
return nil return nil
} }
func (c *chromium) getItemPath(profilePath string, items []item.Item) (map[item.Item]string, error) { func (c *chromium) getMultiItemPath(profilePath string, items []item.Item) (map[string]map[item.Item]string, error) {
var itemPaths = make(map[item.Item]string) // multiItemPaths is a map of user to item path, map[profile 1][item's name & path key pair]
multiItemPaths := make(map[string]map[item.Item]string)
parentDir := fileutil.ParentDir(profilePath) parentDir := fileutil.ParentDir(profilePath)
baseDir := fileutil.BaseDir(profilePath) err := filepath.Walk(parentDir, chromiumWalkFunc(items, multiItemPaths))
err := filepath.Walk(parentDir, chromiumWalkFunc(items, itemPaths, baseDir))
if err != nil { if err != nil {
return itemPaths, err return nil, err
}
var keyPath string
var dir string
for userDir, v := range multiItemPaths {
for _, p := range v {
if strings.HasSuffix(p, item.ChromiumKey.FileName()) {
keyPath = p
dir = userDir
break
}
}
}
t := make(map[string]map[item.Item]string)
for userDir, v := range multiItemPaths {
if userDir == dir {
continue
}
t[userDir] = v
t[userDir][item.ChromiumKey] = keyPath
fillLocalStoragePath(t[userDir], item.ChromiumLocalStorage)
} }
fillLocalStoragePath(itemPaths, item.ChromiumLocalStorage) return t, nil
return itemPaths, nil
} }
func chromiumWalkFunc(items []item.Item, itemPaths map[item.Item]string, baseDir string) filepath.WalkFunc { func chromiumWalkFunc(items []item.Item, multiItemPaths map[string]map[item.Item]string) filepath.WalkFunc {
return func(path string, info os.FileInfo, err error) error { return func(path string, info fs.FileInfo, err error) error {
for _, it := range items { for _, v := range items {
switch { if info.Name() == v.FileName() {
case it.FileName() == info.Name(): parentBaseDir := fileutil.ParentBaseDir(path)
if it == item.ChromiumKey { if parentBaseDir == "System Profile" {
itemPaths[it] = path continue
} }
if strings.Contains(path, baseDir) { if _, exist := multiItemPaths[parentBaseDir]; exist {
itemPaths[it] = path multiItemPaths[parentBaseDir][v] = path
} else {
multiItemPaths[parentBaseDir] = map[item.Item]string{v: path}
} }
} }
} }

@ -1,3 +1,5 @@
//go:build darwin
package chromium package chromium
import ( import (
@ -46,7 +48,7 @@ func (c *chromium) GetMasterKey() ([]byte, error) {
if chromeSecret == nil { if chromeSecret == nil {
return nil, ErrWrongSecurityCommand return nil, ErrWrongSecurityCommand
} }
var chromeSalt = []byte("saltysalt") chromeSalt := []byte("saltysalt")
// @https://source.chromium.org/chromium/chromium/src/+/master:components/os_crypt/os_crypt_mac.mm;l=157 // @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) key := pbkdf2.Key(chromeSecret, chromeSalt, 1003, 16, sha1.New)
if key == nil { if key == nil {

@ -1,3 +1,5 @@
//go:build linux
package chromium package chromium
import ( import (
@ -60,7 +62,7 @@ func (c *chromium) GetMasterKey() ([]byte, error) {
// @https://source.chromium.org/chromium/chromium/src/+/main:components/os_crypt/os_crypt_linux.cc;l=100 // @https://source.chromium.org/chromium/chromium/src/+/main:components/os_crypt/os_crypt_linux.cc;l=100
chromiumSecret = []byte("peanuts") chromiumSecret = []byte("peanuts")
} }
var chromiumSalt = []byte("saltysalt") chromiumSalt := []byte("saltysalt")
// @https://source.chromium.org/chromium/chromium/src/+/master:components/os_crypt/os_crypt_linux.cc // @https://source.chromium.org/chromium/chromium/src/+/master:components/os_crypt/os_crypt_linux.cc
key := pbkdf2.Key(chromiumSecret, chromiumSalt, 1, 16, sha1.New) key := pbkdf2.Key(chromiumSecret, chromiumSalt, 1, 16, sha1.New)
c.masterKey = key c.masterKey = key

@ -1,3 +1,5 @@
//go:build windows
package chromium package chromium
import ( import (
@ -13,9 +15,7 @@ import (
"hack-browser-data/internal/utils/fileutil" "hack-browser-data/internal/utils/fileutil"
) )
var ( var errDecodeMasterKeyFailed = errors.New("decode master key failed")
errDecodeMasterKeyFailed = errors.New("decode master key failed")
)
func (c *chromium) GetMasterKey() ([]byte, error) { func (c *chromium) GetMasterKey() ([]byte, error) {
keyFile, err := fileutil.ReadFile(item.TempChromiumKey) keyFile, err := fileutil.ReadFile(item.TempChromiumKey)

@ -21,13 +21,10 @@ type firefox struct {
itemPaths map[item.Item]string itemPaths map[item.Item]string
} }
var ( var ErrProfilePathNotFound = errors.New("profile path not found")
ErrProfilePathNotFound = errors.New("profile path not found")
)
// New returns a new firefox instance. // New returns a new firefox instance.
func New(name, storage, profilePath string, items []item.Item) ([]*firefox, error) { func New(name, storage, profilePath string, items []item.Item) ([]*firefox, error) {
f := &firefox{ f := &firefox{
name: name, name: name,
storage: storage, storage: storage,
@ -50,7 +47,7 @@ func New(name, storage, profilePath string, items []item.Item) ([]*firefox, erro
} }
func (f *firefox) getMultiItemPath(profilePath string, items []item.Item) (map[string]map[item.Item]string, error) { func (f *firefox) getMultiItemPath(profilePath string, items []item.Item) (map[string]map[item.Item]string, error) {
var multiItemPaths = make(map[string]map[item.Item]string) multiItemPaths := make(map[string]map[item.Item]string)
err := filepath.Walk(profilePath, firefoxWalkFunc(items, multiItemPaths)) err := filepath.Walk(profilePath, firefoxWalkFunc(items, multiItemPaths))
return multiItemPaths, err return multiItemPaths, err
} }

@ -5,7 +5,7 @@ func Chromium(key, encryptPass []byte) ([]byte, error) {
if len(key) == 0 { if len(key) == 0 {
return nil, errSecurityKeyIsEmpty return nil, errSecurityKeyIsEmpty
} }
var chromeIV = []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32} chromeIV := []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}
return aes128CBCDecrypt(key, chromeIV, encryptPass[3:]) return aes128CBCDecrypt(key, chromeIV, encryptPass[3:])
} else { } else {
return nil, errDecryptFailed return nil, errDecryptFailed

@ -1,7 +1,7 @@
package decrypter package decrypter
func Chromium(key, encryptPass []byte) ([]byte, error) { func Chromium(key, encryptPass []byte) ([]byte, error) {
var chromeIV = []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32} chromeIV := []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}
if len(encryptPass) > 3 { if len(encryptPass) > 3 {
if len(key) == 0 { if len(key) == 0 {
return nil, errSecurityKeyIsEmpty return nil, errSecurityKeyIsEmpty

@ -81,7 +81,7 @@ func CopyDirHasSuffix(src, dst, suffix string) error {
if err != nil { if err != nil {
return err return err
} }
if err := os.MkdirAll(dst, 0755); err != nil { if err := os.MkdirAll(dst, 0o755); err != nil {
return err return err
} }
for index, file := range filelist { for index, file := range filelist {
@ -102,7 +102,7 @@ func CopyFile(src, dst string) error {
if err != nil { if err != nil {
return err return err
} }
err = ioutil.WriteFile(dst, d, 0777) err = ioutil.WriteFile(dst, d, 0o777)
if err != nil { if err != nil {
return err return err
} }
@ -115,6 +115,11 @@ func Filename(browser, item, ext string) string {
return strings.ToLower(fmt.Sprintf("%s_%s.%s", replace.Replace(browser), item, ext)) return strings.ToLower(fmt.Sprintf("%s_%s.%s", replace.Replace(browser), item, ext))
} }
func BrowserName(browser, user string) string {
replace := strings.NewReplacer(" ", "_", ".", "_", "-", "_", "Profile", "User")
return strings.ToLower(fmt.Sprintf("%s_%s", replace.Replace(browser), replace.Replace(user)))
}
// ParentDir returns the parent directory of the provided path // ParentDir returns the parent directory of the provided path
func ParentDir(p string) string { func ParentDir(p string) string {
return filepath.Dir(filepath.Clean(p)) return filepath.Dir(filepath.Clean(p))
@ -136,7 +141,7 @@ func CompressDir(dir string) error {
if err != nil { if err != nil {
return err return err
} }
var b = new(bytes.Buffer) b := new(bytes.Buffer)
zw := zip.NewWriter(b) zw := zip.NewWriter(b)
for _, f := range files { for _, f := range files {
fw, _ := zw.Create(f.Name()) fw, _ := zw.Create(f.Name())

@ -24,7 +24,7 @@ func IntToBool[T constraints.Signed](a T) bool {
} }
func Reverse[T any](s []T) []T { func Reverse[T any](s []T) []T {
var h = make([]T, len(s)) h := make([]T, len(s))
for i := 0; i < len(s); i++ { for i := 0; i < len(s); i++ {
h[i] = s[len(s)-i-1] h[i] = s[len(s)-i-1]
} }

@ -4,13 +4,11 @@ import (
"testing" "testing"
) )
var ( var reverseTestCases = [][]any{
reverseTestCases = [][]any{ {1, 2, 3, 4, 5},
[]any{1, 2, 3, 4, 5}, {"1", "2", "3", "4", "5"},
[]any{"1", "2", "3", "4", "5"}, {"1", 2, "3", "4", 5},
[]any{"1", 2, "3", "4", 5}, }
}
)
func TestReverse(t *testing.T) { func TestReverse(t *testing.T) {
for _, ts := range reverseTestCases { for _, ts := range reverseTestCases {

Loading…
Cancel
Save