mirror of
https://github.com/moonD4rk/HackBrowserData.git
synced 2026-05-19 18:58:03 +02:00
feat: support export multiple user from browser
Merge pull request #143 from moonD4rk/feat/multiple-user
This commit is contained in:
@@ -29,7 +29,7 @@ func Execute() {
|
||||
Name: "hack-browser-data",
|
||||
Usage: "Export passwords/cookies/history/bookmarks from browser",
|
||||
UsageText: "[hack-browser-data -b chrome -f json -dir results -cc]\nExport all browingdata(password/cookie/history/bookmark) from browser\nGithub Link: https://github.com/moonD4rk/HackBrowserData",
|
||||
Version: "0.4.2",
|
||||
Version: "0.4.3",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{Name: "verbose", Aliases: []string{"vv"}, Destination: &verbose, Value: false, Usage: "verbose"},
|
||||
&cli.BoolFlag{Name: "compress", Aliases: []string{"zip"}, Destination: &compress, Value: false, Usage: "compress result to zip"},
|
||||
|
||||
@@ -35,7 +35,6 @@ func New(sources []item.Item) *Data {
|
||||
}
|
||||
|
||||
func (d *Data) Recovery(masterKey []byte) error {
|
||||
|
||||
for _, source := range d.sources {
|
||||
if err := source.Parse(masterKey); err != nil {
|
||||
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)
|
||||
|
||||
for _, source := range d.sources {
|
||||
|
||||
filename := fileutil.Filename(browserName, source.Name(), output.Ext())
|
||||
|
||||
f, err := output.CreateFile(dir, filename)
|
||||
|
||||
@@ -71,6 +71,7 @@ func (c *ChromiumCreditCard) Parse(masterKey []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ChromiumCreditCard) Name() string {
|
||||
return "creditcard"
|
||||
}
|
||||
@@ -122,6 +123,7 @@ func (c *YandexCreditCard) Parse(masterKey []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *YandexCreditCard) Name() string {
|
||||
return "creditcard"
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func (c *ChromiumLocalStorage) Parse(masterKey []byte) error {
|
||||
if len(value) > 1024*5 {
|
||||
continue
|
||||
}
|
||||
var s = new(storage)
|
||||
s := new(storage)
|
||||
s.fillKey(key)
|
||||
s.fillValue(value)
|
||||
// don't save meta data
|
||||
@@ -114,13 +114,11 @@ func (f *FirefoxLocalStorage) Parse(masterKey []byte) error {
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var (
|
||||
originKey, key, value string
|
||||
)
|
||||
var originKey, key, value string
|
||||
if err = rows.Scan(&originKey, &key, &value); err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
var s = new(storage)
|
||||
s := new(storage)
|
||||
s.fillFirefox(originKey, key, value)
|
||||
*f = append(*f, *s)
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ func (o *OutPutter) CreateFile(dir, filename string) (*os.File, error) {
|
||||
|
||||
if dir != "" {
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
err := os.MkdirAll(dir, 0777)
|
||||
err := os.MkdirAll(dir, 0o777)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func (o *OutPutter) CreateFile(dir, filename string) (*os.File, error) {
|
||||
var file *os.File
|
||||
var err error
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -229,9 +229,7 @@ func (f *FirefoxPassword) Name() string {
|
||||
}
|
||||
|
||||
func getFirefoxDecryptKey(key4file string) (item1, item2, a11, a102 []byte, err error) {
|
||||
var (
|
||||
keyDB *sql.DB
|
||||
)
|
||||
var keyDB *sql.DB
|
||||
keyDB, err = sql.Open("sqlite3", key4file)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
|
||||
+14
-11
@@ -46,9 +46,12 @@ func pickChromium(name, profile string) []Browser {
|
||||
log.Noticef("find browser %s failed, profile folder does not exist", v.name)
|
||||
continue
|
||||
}
|
||||
if b, err := chromium.New(v.name, v.storage, v.profilePath, v.items); err == nil {
|
||||
log.Noticef("find browser %s success", b.Name())
|
||||
browsers = append(browsers, b)
|
||||
if multiChromium, err := chromium.New(v.name, v.storage, v.profilePath, v.items); err == nil {
|
||||
log.Noticef("find browser %s success", v.name)
|
||||
for _, b := range multiChromium {
|
||||
log.Noticef("find browser %s success", b.Name())
|
||||
browsers = append(browsers, b)
|
||||
}
|
||||
} else {
|
||||
log.Errorf("new chromium error: %s", err.Error())
|
||||
}
|
||||
@@ -61,12 +64,14 @@ func pickChromium(name, profile string) []Browser {
|
||||
if !fileutil.FolderExists(filepath.Clean(profile)) {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
@@ -106,10 +111,8 @@ func ListBrowser() []string {
|
||||
return l
|
||||
}
|
||||
|
||||
var (
|
||||
// home dir path for all platforms
|
||||
homeDir, _ = os.UserHomeDir()
|
||||
)
|
||||
// home dir path for all platforms
|
||||
var homeDir, _ = os.UserHomeDir()
|
||||
|
||||
const (
|
||||
chromeName = "Chrome"
|
||||
|
||||
@@ -15,7 +15,7 @@ var (
|
||||
}{
|
||||
"chrome": {
|
||||
name: chromeName,
|
||||
profilePath: chromeProfilePath,
|
||||
profilePath: chromeUserDataPath,
|
||||
items: item.DefaultChromium,
|
||||
},
|
||||
"edge": {
|
||||
@@ -25,12 +25,12 @@ var (
|
||||
},
|
||||
"chromium": {
|
||||
name: chromiumName,
|
||||
profilePath: chromiumProfilePath,
|
||||
profilePath: chromiumUserDataPath,
|
||||
items: item.DefaultChromium,
|
||||
},
|
||||
"chrome-beta": {
|
||||
name: chromeBetaName,
|
||||
profilePath: chromeBetaProfilePath,
|
||||
profilePath: chromeBetaUserDataPath,
|
||||
items: item.DefaultChromium,
|
||||
},
|
||||
"opera": {
|
||||
@@ -89,18 +89,18 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
chromeProfilePath = homeDir + "/AppData/Local/Google/Chrome/User Data/Default/"
|
||||
chromeBetaProfilePath = homeDir + "/AppData/Local/Google/Chrome Beta/User Data/Default/"
|
||||
chromiumProfilePath = homeDir + "/AppData/Local/Chromium/User Data/Default/"
|
||||
edgeProfilePath = homeDir + "/AppData/Local/Microsoft/Edge/User Data/Default/"
|
||||
braveProfilePath = homeDir + "/AppData/Local/BraveSoftware/Brave-Browser/User Data/Default/"
|
||||
speed360ProfilePath = homeDir + "/AppData/Local/360chrome/Chrome/User Data/Default/"
|
||||
qqBrowserProfilePath = homeDir + "/AppData/Local/Tencent/QQBrowser/User Data/Default/"
|
||||
operaProfilePath = homeDir + "/AppData/Roaming/Opera Software/Opera Stable/Default/"
|
||||
operaGXProfilePath = homeDir + "/AppData/Roaming/Opera Software/Opera GX Stable/Default/"
|
||||
vivaldiProfilePath = homeDir + "/AppData/Local/Vivaldi/User Data/Default/"
|
||||
coccocProfilePath = homeDir + "/AppData/Local/CocCoc/Browser/User Data/Default/"
|
||||
yandexProfilePath = homeDir + "/AppData/Local/Yandex/YandexBrowser/User Data/Default/"
|
||||
chromeUserDataPath = homeDir + "/AppData/Local/Google/Chrome/User Data/Default/"
|
||||
chromeBetaUserDataPath = homeDir + "/AppData/Local/Google/Chrome Beta/User Data/Default/"
|
||||
chromiumUserDataPath = homeDir + "/AppData/Local/Chromium/User Data/Default/"
|
||||
edgeProfilePath = homeDir + "/AppData/Local/Microsoft/Edge/User Data/Default/"
|
||||
braveProfilePath = homeDir + "/AppData/Local/BraveSoftware/Brave-Browser/User Data/Default/"
|
||||
speed360ProfilePath = homeDir + "/AppData/Local/360chrome/Chrome/User Data/Default/"
|
||||
qqBrowserProfilePath = homeDir + "/AppData/Local/Tencent/QQBrowser/User Data/Default/"
|
||||
operaProfilePath = homeDir + "/AppData/Roaming/Opera Software/Opera Stable/Default/"
|
||||
operaGXProfilePath = homeDir + "/AppData/Roaming/Opera Software/Opera GX Stable/"
|
||||
vivaldiProfilePath = homeDir + "/AppData/Local/Vivaldi/User Data/Default/"
|
||||
coccocProfilePath = homeDir + "/AppData/Local/CocCoc/Browser/User Data/Default/"
|
||||
yandexProfilePath = homeDir + "/AppData/Local/Yandex/YandexBrowser/User Data/Default/"
|
||||
|
||||
firefoxProfilePath = homeDir + "/AppData/Roaming/Mozilla/Firefox/Profiles/"
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package chromium
|
||||
|
||||
import (
|
||||
"os"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -21,19 +21,27 @@ type chromium struct {
|
||||
}
|
||||
|
||||
// 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{
|
||||
name: name,
|
||||
storage: storage,
|
||||
name: name,
|
||||
storage: storage,
|
||||
profilePath: profilePath,
|
||||
items: items,
|
||||
}
|
||||
itemsPaths, err := c.getItemPath(profilePath, items)
|
||||
multiItemPaths, err := c.getMultiItemPath(c.profilePath, c.items)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.profilePath = profilePath
|
||||
c.itemPaths = itemsPaths
|
||||
c.items = typeutil.Keys(itemsPaths)
|
||||
return c, err
|
||||
var chromiumList []*chromium
|
||||
for user, itemPaths := range multiItemPaths {
|
||||
chromiumList = append(chromiumList, &chromium{
|
||||
name: fileutil.BrowserName(name, user),
|
||||
items: typeutil.Keys(itemPaths),
|
||||
itemPaths: itemPaths,
|
||||
storage: storage,
|
||||
})
|
||||
}
|
||||
return chromiumList, nil
|
||||
}
|
||||
|
||||
func (c *chromium) Name() string {
|
||||
@@ -81,28 +89,49 @@ func (c *chromium) copyItemToLocal() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chromium) getItemPath(profilePath string, items []item.Item) (map[item.Item]string, error) {
|
||||
var itemPaths = make(map[item.Item]string)
|
||||
func (c *chromium) getMultiItemPath(profilePath string, items []item.Item) (map[string]map[item.Item]string, error) {
|
||||
// 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)
|
||||
baseDir := fileutil.BaseDir(profilePath)
|
||||
err := filepath.Walk(parentDir, chromiumWalkFunc(items, itemPaths, baseDir))
|
||||
err := filepath.Walk(parentDir, chromiumWalkFunc(items, multiItemPaths))
|
||||
if err != nil {
|
||||
return itemPaths, err
|
||||
return nil, err
|
||||
}
|
||||
fillLocalStoragePath(itemPaths, item.ChromiumLocalStorage)
|
||||
return itemPaths, nil
|
||||
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)
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func chromiumWalkFunc(items []item.Item, itemPaths map[item.Item]string, baseDir string) filepath.WalkFunc {
|
||||
return func(path string, info os.FileInfo, err error) error {
|
||||
for _, it := range items {
|
||||
switch {
|
||||
case it.FileName() == info.Name():
|
||||
if it == item.ChromiumKey {
|
||||
itemPaths[it] = path
|
||||
func chromiumWalkFunc(items []item.Item, multiItemPaths map[string]map[item.Item]string) filepath.WalkFunc {
|
||||
return func(path string, info fs.FileInfo, err error) error {
|
||||
for _, v := range items {
|
||||
if info.Name() == v.FileName() {
|
||||
parentBaseDir := fileutil.ParentBaseDir(path)
|
||||
if parentBaseDir == "System Profile" {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(path, baseDir) {
|
||||
itemPaths[it] = path
|
||||
if _, exist := multiItemPaths[parentBaseDir]; exist {
|
||||
multiItemPaths[parentBaseDir][v] = path
|
||||
} else {
|
||||
multiItemPaths[parentBaseDir] = map[item.Item]string{v: path}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build darwin
|
||||
|
||||
package chromium
|
||||
|
||||
import (
|
||||
@@ -46,7 +48,7 @@ func (c *chromium) GetMasterKey() ([]byte, error) {
|
||||
if chromeSecret == nil {
|
||||
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
|
||||
key := pbkdf2.Key(chromeSecret, chromeSalt, 1003, 16, sha1.New)
|
||||
if key == nil {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build linux
|
||||
|
||||
package chromium
|
||||
|
||||
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
|
||||
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
|
||||
key := pbkdf2.Key(chromiumSecret, chromiumSalt, 1, 16, sha1.New)
|
||||
c.masterKey = key
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build windows
|
||||
|
||||
package chromium
|
||||
|
||||
import (
|
||||
@@ -13,9 +15,7 @@ import (
|
||||
"hack-browser-data/internal/utils/fileutil"
|
||||
)
|
||||
|
||||
var (
|
||||
errDecodeMasterKeyFailed = errors.New("decode master key failed")
|
||||
)
|
||||
var errDecodeMasterKeyFailed = errors.New("decode master key failed")
|
||||
|
||||
func (c *chromium) GetMasterKey() ([]byte, error) {
|
||||
keyFile, err := fileutil.ReadFile(item.TempChromiumKey)
|
||||
|
||||
@@ -21,13 +21,10 @@ type firefox struct {
|
||||
itemPaths map[item.Item]string
|
||||
}
|
||||
|
||||
var (
|
||||
ErrProfilePathNotFound = errors.New("profile path not found")
|
||||
)
|
||||
var ErrProfilePathNotFound = errors.New("profile path not found")
|
||||
|
||||
// New returns a new firefox instance.
|
||||
func New(name, storage, profilePath string, items []item.Item) ([]*firefox, error) {
|
||||
|
||||
f := &firefox{
|
||||
name: name,
|
||||
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) {
|
||||
var multiItemPaths = make(map[string]map[item.Item]string)
|
||||
multiItemPaths := make(map[string]map[item.Item]string)
|
||||
err := filepath.Walk(profilePath, firefoxWalkFunc(items, multiItemPaths))
|
||||
return multiItemPaths, err
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ func Chromium(key, encryptPass []byte) ([]byte, error) {
|
||||
if len(key) == 0 {
|
||||
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:])
|
||||
} else {
|
||||
return nil, errDecryptFailed
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package decrypter
|
||||
|
||||
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(key) == 0 {
|
||||
return nil, errSecurityKeyIsEmpty
|
||||
|
||||
@@ -81,7 +81,7 @@ func CopyDirHasSuffix(src, dst, suffix string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(dst, 0755); err != nil {
|
||||
if err := os.MkdirAll(dst, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
for index, file := range filelist {
|
||||
@@ -102,7 +102,7 @@ func CopyFile(src, dst string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(dst, d, 0777)
|
||||
err = ioutil.WriteFile(dst, d, 0o777)
|
||||
if err != nil {
|
||||
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))
|
||||
}
|
||||
|
||||
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
|
||||
func ParentDir(p string) string {
|
||||
return filepath.Dir(filepath.Clean(p))
|
||||
@@ -136,7 +141,7 @@ func CompressDir(dir string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var b = new(bytes.Buffer)
|
||||
b := new(bytes.Buffer)
|
||||
zw := zip.NewWriter(b)
|
||||
for _, f := range files {
|
||||
fw, _ := zw.Create(f.Name())
|
||||
|
||||
@@ -24,7 +24,7 @@ func IntToBool[T constraints.Signed](a T) bool {
|
||||
}
|
||||
|
||||
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++ {
|
||||
h[i] = s[len(s)-i-1]
|
||||
}
|
||||
|
||||
@@ -4,13 +4,11 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
reverseTestCases = [][]any{
|
||||
[]any{1, 2, 3, 4, 5},
|
||||
[]any{"1", "2", "3", "4", "5"},
|
||||
[]any{"1", 2, "3", "4", 5},
|
||||
}
|
||||
)
|
||||
var reverseTestCases = [][]any{
|
||||
{1, 2, 3, 4, 5},
|
||||
{"1", "2", "3", "4", "5"},
|
||||
{"1", 2, "3", "4", 5},
|
||||
}
|
||||
|
||||
func TestReverse(t *testing.T) {
|
||||
for _, ts := range reverseTestCases {
|
||||
|
||||
Reference in New Issue
Block a user