refactor code Closes #13
parent
23b9cf97f7
commit
73b3075806
@ -0,0 +1,393 @@ |
|||||||
|
package core |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"hack-browser-data/core/common" |
||||||
|
"hack-browser-data/log" |
||||||
|
"hack-browser-data/utils" |
||||||
|
"path/filepath" |
||||||
|
"strings" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
chromeName = "Chrome" |
||||||
|
edgeName = "Microsoft Edge" |
||||||
|
firefoxName = "Firefox" |
||||||
|
speed360Name = "360speed" |
||||||
|
qqBrowserName = "qq" |
||||||
|
) |
||||||
|
|
||||||
|
type Browser interface { |
||||||
|
GetProfilePath(filename string) (err error) |
||||||
|
InitSecretKey() error |
||||||
|
ParseDB() |
||||||
|
OutPut(dir, format string) |
||||||
|
} |
||||||
|
|
||||||
|
type chromium struct { |
||||||
|
ProfilePath string |
||||||
|
KeyPath string |
||||||
|
Name string |
||||||
|
SecretKey []byte |
||||||
|
FileLists []FileList |
||||||
|
Data common.BrowserData |
||||||
|
} |
||||||
|
|
||||||
|
type firefox struct { |
||||||
|
ProfilePath string |
||||||
|
KeyPath string |
||||||
|
Name string |
||||||
|
FileLists []FileList |
||||||
|
Data common.BrowserData |
||||||
|
} |
||||||
|
|
||||||
|
type FileList struct { |
||||||
|
name string |
||||||
|
mainFile string |
||||||
|
mainPath string |
||||||
|
subFile string |
||||||
|
subPath string |
||||||
|
} |
||||||
|
|
||||||
|
const ( |
||||||
|
cookie = "cookie" |
||||||
|
history = "history" |
||||||
|
bookmark = "bookmark" |
||||||
|
password = "password" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
ErrDataNotSupported = errors.New(`not supported, default is "all", choose from history|password|bookmark|cookie`) |
||||||
|
ErrBrowserNotSupported = errors.New("browser not supported") |
||||||
|
chromiumParseList = map[string]FileList{ |
||||||
|
cookie: { |
||||||
|
name: cookie, |
||||||
|
mainFile: common.ChromeCookies, |
||||||
|
}, |
||||||
|
history: { |
||||||
|
name: history, |
||||||
|
mainFile: common.ChromeHistory, |
||||||
|
}, |
||||||
|
bookmark: { |
||||||
|
name: bookmark, |
||||||
|
mainFile: common.ChromeBookmarks, |
||||||
|
}, |
||||||
|
password: { |
||||||
|
name: password, |
||||||
|
mainFile: common.ChromePassword, |
||||||
|
}, |
||||||
|
} |
||||||
|
firefoxParseList = map[string]FileList{ |
||||||
|
cookie: { |
||||||
|
name: cookie, |
||||||
|
mainFile: common.FirefoxCookie, |
||||||
|
}, |
||||||
|
history: { |
||||||
|
name: history, |
||||||
|
mainFile: common.FirefoxData, |
||||||
|
}, |
||||||
|
bookmark: { |
||||||
|
name: bookmark, |
||||||
|
mainFile: common.FirefoxData, |
||||||
|
}, |
||||||
|
password: { |
||||||
|
name: password, |
||||||
|
mainFile: common.FirefoxKey4DB, |
||||||
|
subFile: common.FirefoxLoginData, |
||||||
|
}, |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
func (c *chromium) GetProfilePath(filename string) (err error) { |
||||||
|
filename = strings.ToLower(filename) |
||||||
|
if filename == "all" { |
||||||
|
for _, v := range chromiumParseList { |
||||||
|
m, err := filepath.Glob(c.ProfilePath + v.mainFile) |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
continue |
||||||
|
} |
||||||
|
if len(m) > 0 { |
||||||
|
log.Debugf("%s find %s File Success", c.Name, v.name) |
||||||
|
log.Debugf("%s file location is %s", v, m[0]) |
||||||
|
v.mainPath = m[0] |
||||||
|
c.FileLists = append(c.FileLists, v) |
||||||
|
} else { |
||||||
|
log.Errorf("%+v find %s failed", c.Name, v.name) |
||||||
|
} |
||||||
|
} |
||||||
|
} else if v, ok := chromiumParseList[filename]; ok { |
||||||
|
m, err := filepath.Glob(c.ProfilePath + v.mainFile) |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
if len(m) > 0 { |
||||||
|
log.Debugf("%s find %s File Success", c.Name, v) |
||||||
|
log.Debugf("%s file location is %s", v, m[0]) |
||||||
|
v.mainPath = m[0] |
||||||
|
c.FileLists = append(c.FileLists, v) |
||||||
|
} |
||||||
|
} else { |
||||||
|
return ErrDataNotSupported |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (c *chromium) ParseDB() { |
||||||
|
for _, v := range c.FileLists { |
||||||
|
err := utils.CopyDB(v.mainPath, filepath.Base(v.mainPath)) |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
switch v.name { |
||||||
|
case bookmark: |
||||||
|
if err := chromeParse(c.SecretKey, &c.Data.Bookmarks); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case history: |
||||||
|
if err := chromeParse(c.SecretKey, &c.Data.History); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case password: |
||||||
|
if err := chromeParse(c.SecretKey, &c.Data.Logins); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case cookie: |
||||||
|
if err := chromeParse(c.SecretKey, &c.Data.Cookies); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (c *chromium) OutPut(dir, format string) { |
||||||
|
c.Data.Sorted() |
||||||
|
switch format { |
||||||
|
case "json": |
||||||
|
for _, v := range c.FileLists { |
||||||
|
switch v.name { |
||||||
|
case bookmark: |
||||||
|
if err := outPutJson(c.Name, dir, &c.Data.Bookmarks); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case history: |
||||||
|
if err := outPutJson(c.Name, dir, &c.Data.History); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case password: |
||||||
|
if err := outPutJson(c.Name, dir, &c.Data.Logins); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case cookie: |
||||||
|
if err := outPutJson(c.Name, dir, &c.Data.Cookies); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
case "csv": |
||||||
|
for _, v := range c.FileLists { |
||||||
|
switch v.name { |
||||||
|
case bookmark: |
||||||
|
if err := outPutCsv(c.Name, dir, &c.Data.Bookmarks); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case history: |
||||||
|
if err := outPutCsv(c.Name, dir, &c.Data.History); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case password: |
||||||
|
if err := outPutCsv(c.Name, dir, &c.Data.Logins); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case cookie: |
||||||
|
if err := outPutCsv(c.Name, dir, &c.Data.Cookies); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func decryptChromium(profile, key, name string) (Browser, error) { |
||||||
|
return &chromium{ProfilePath: profile, KeyPath: key, Name: name}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (f *firefox) ParseDB() { |
||||||
|
for _, v := range f.FileLists { |
||||||
|
err := utils.CopyDB(v.mainPath, filepath.Base(v.mainPath)) |
||||||
|
if v.subPath != "" { |
||||||
|
err := utils.CopyDB(v.subPath, filepath.Base(v.subPath)) |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
switch v.name { |
||||||
|
case password: |
||||||
|
if err := firefoxParse(&f.Data.Logins); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case bookmark: |
||||||
|
if err := firefoxParse(&f.Data.Bookmarks); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case history: |
||||||
|
if err := firefoxParse(&f.Data.History); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case cookie: |
||||||
|
if err := firefoxParse(&f.Data.Cookies); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (f *firefox) GetProfilePath(filename string) (err error) { |
||||||
|
filename = strings.ToLower(filename) |
||||||
|
if filename == "all" { |
||||||
|
for _, v := range firefoxParseList { |
||||||
|
m, err := filepath.Glob(f.ProfilePath + v.mainFile) |
||||||
|
if v.subFile != "" { |
||||||
|
s, err := filepath.Glob(f.ProfilePath + v.subFile) |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
continue |
||||||
|
} |
||||||
|
if len(s) > 0 { |
||||||
|
log.Debugf("%s find %s File Success", f.Name, v.name) |
||||||
|
log.Debugf("%s file location is %s", v, s[0]) |
||||||
|
v.subPath = s[0] |
||||||
|
} |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
continue |
||||||
|
} |
||||||
|
if len(m) > 0 { |
||||||
|
log.Debugf("%s find %s File Success", f.Name, v.name) |
||||||
|
log.Debugf("%+v file location is %s", v, m[0]) |
||||||
|
v.mainPath = m[0] |
||||||
|
f.FileLists = append(f.FileLists, v) |
||||||
|
} else { |
||||||
|
log.Errorf("%s find %s failed", f.Name, v.name) |
||||||
|
} |
||||||
|
} |
||||||
|
} else if v, ok := firefoxParseList[filename]; ok { |
||||||
|
m, err := filepath.Glob(f.ProfilePath + v.mainFile) |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
if len(m) > 0 { |
||||||
|
log.Debugf("%s find %s File Success", f.Name, v) |
||||||
|
log.Debugf("%s file location is %s", v, m[0]) |
||||||
|
v.mainPath = m[0] |
||||||
|
f.FileLists = append(f.FileLists, v) |
||||||
|
} |
||||||
|
} else { |
||||||
|
return ErrDataNotSupported |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (f *firefox) OutPut(dir, format string) { |
||||||
|
f.Data.Sorted() |
||||||
|
switch format { |
||||||
|
case "json": |
||||||
|
for _, v := range f.FileLists { |
||||||
|
switch v.name { |
||||||
|
case bookmark: |
||||||
|
if err := outPutJson(f.Name, dir, &f.Data.Bookmarks); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case history: |
||||||
|
if err := outPutJson(f.Name, dir, &f.Data.History); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case password: |
||||||
|
if err := outPutJson(f.Name, dir, &f.Data.Logins); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case cookie: |
||||||
|
if err := outPutJson(f.Name, dir, &f.Data.Cookies); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
case "csv": |
||||||
|
for _, v := range f.FileLists { |
||||||
|
switch v.name { |
||||||
|
case bookmark: |
||||||
|
if err := outPutCsv(f.Name, dir, &f.Data.Bookmarks); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case history: |
||||||
|
if err := outPutCsv(f.Name, dir, &f.Data.History); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case password: |
||||||
|
if err := outPutCsv(f.Name, dir, &f.Data.Logins); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
case cookie: |
||||||
|
if err := outPutCsv(f.Name, dir, &f.Data.Cookies); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (f *firefox) InitSecretKey() error { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func decryptFirefox(profile, key, name string) (Browser, error) { |
||||||
|
return &firefox{ProfilePath: profile, KeyPath: key, Name: name}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func PickBrowsers(name string) ([]Browser, error) { |
||||||
|
var browsers []Browser |
||||||
|
name = strings.ToLower(name) |
||||||
|
if name == "all" { |
||||||
|
for _, v := range browserList { |
||||||
|
b, err := v.New(v.ProfilePath, v.KeyPath, v.Name) |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
browsers = append(browsers, b) |
||||||
|
} |
||||||
|
return browsers, nil |
||||||
|
} else if choice, ok := browserList[name]; ok { |
||||||
|
b, err := choice.New(choice.ProfilePath, choice.KeyPath, choice.Name) |
||||||
|
browsers = append(browsers, b) |
||||||
|
return browsers, err |
||||||
|
} |
||||||
|
return nil, ErrBrowserNotSupported |
||||||
|
} |
||||||
|
|
||||||
|
func chromeParse(key []byte, f common.Formatter) error { |
||||||
|
return f.ChromeParse(key) |
||||||
|
} |
||||||
|
|
||||||
|
func firefoxParse(f common.Formatter) error { |
||||||
|
return f.FirefoxParse() |
||||||
|
} |
||||||
|
|
||||||
|
func outPutJson(name, dir string, f common.Formatter) error { |
||||||
|
return f.OutPutJson(name, dir) |
||||||
|
} |
||||||
|
|
||||||
|
func outPutCsv(name, dir string, f common.Formatter) error { |
||||||
|
return f.OutPutCsv(name, dir) |
||||||
|
} |
||||||
|
|
||||||
|
func ListBrowser() []string { |
||||||
|
var l []string |
||||||
|
for k := range browserList { |
||||||
|
l = append(l, k) |
||||||
|
} |
||||||
|
return l |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
package core |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"crypto/sha1" |
||||||
|
"errors" |
||||||
|
"hack-browser-data/log" |
||||||
|
"os/exec" |
||||||
|
|
||||||
|
"golang.org/x/crypto/pbkdf2" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
chromeProfilePath = "/Users/*/Library/Application Support/Google/Chrome/*/" |
||||||
|
edgeProfilePath = "/Users/*/Library/Application Support/Microsoft Edge/*/" |
||||||
|
fireFoxProfilePath = "/Users/*/Library/Application Support/Firefox/Profiles/*.default-release/" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
browserList = map[string]struct { |
||||||
|
ProfilePath string |
||||||
|
Name string |
||||||
|
KeyPath string |
||||||
|
New func(profile, key, name string) (Browser, error) |
||||||
|
}{ |
||||||
|
"chrome": { |
||||||
|
ProfilePath: chromeProfilePath, |
||||||
|
Name: chromeName, |
||||||
|
New: decryptChromium, |
||||||
|
}, |
||||||
|
"edge": { |
||||||
|
ProfilePath: edgeProfilePath, |
||||||
|
Name: edgeName, |
||||||
|
New: decryptChromium, |
||||||
|
}, |
||||||
|
"firefox": { |
||||||
|
ProfilePath: fireFoxProfilePath, |
||||||
|
Name: firefoxName, |
||||||
|
New: decryptFirefox, |
||||||
|
}, |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
func (c *chromium) InitSecretKey() error { |
||||||
|
var ( |
||||||
|
cmd *exec.Cmd |
||||||
|
stdout, stderr bytes.Buffer |
||||||
|
) |
||||||
|
//➜ security find-generic-password -wa 'Chrome'
|
||||||
|
cmd = exec.Command("security", "find-generic-password", "-wa", c.Name) |
||||||
|
cmd.Stdout = &stdout |
||||||
|
cmd.Stderr = &stderr |
||||||
|
err := cmd.Run() |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
if stderr.Len() > 0 { |
||||||
|
err = errors.New(stderr.String()) |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
temp := stdout.Bytes() |
||||||
|
chromePass := temp[:len(temp)-1] |
||||||
|
var chromeSalt = []byte("saltysalt") |
||||||
|
// @https://source.chromium.org/chromium/chromium/src/+/master:components/os_crypt/os_crypt_mac.mm;l=157
|
||||||
|
key := pbkdf2.Key(chromePass, chromeSalt, 1003, 16, sha1.New) |
||||||
|
c.SecretKey = key |
||||||
|
return err |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
package core |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"crypto/sha1" |
||||||
|
"errors" |
||||||
|
"hack-browser-data/log" |
||||||
|
"os/exec" |
||||||
|
|
||||||
|
"golang.org/x/crypto/pbkdf2" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
fireFoxProfilePath = "/home/*/.mozilla/firefox/*.default-release/" |
||||||
|
fireFoxCommand = "" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
browserList = map[string]struct { |
||||||
|
ProfilePath string |
||||||
|
Name string |
||||||
|
KeyPath string |
||||||
|
New func(profile, key, name string) (Browser, error) |
||||||
|
}{ |
||||||
|
"firefox": { |
||||||
|
ProfilePath: fireFoxProfilePath, |
||||||
|
Name: fireFoxCommand, |
||||||
|
New: decryptFirefox, |
||||||
|
}, |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
func (c *chromium) InitSecretKey() error { |
||||||
|
var ( |
||||||
|
cmd *exec.Cmd |
||||||
|
stdout, stderr bytes.Buffer |
||||||
|
) |
||||||
|
//➜ security find-generic-password -wa 'Chrome'
|
||||||
|
cmd = exec.Command("security", "find-generic-password", "-wa", c.Name) |
||||||
|
cmd.Stdout = &stdout |
||||||
|
cmd.Stderr = &stderr |
||||||
|
err := cmd.Run() |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
if stderr.Len() > 0 { |
||||||
|
err = errors.New(stderr.String()) |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
temp := stdout.Bytes() |
||||||
|
chromePass := temp[:len(temp)-1] |
||||||
|
var chromeSalt = []byte("saltysalt") |
||||||
|
// @https://source.chromium.org/chromium/chromium/src/+/master:components/os_crypt/os_crypt_mac.mm;l=157
|
||||||
|
key := pbkdf2.Key(chromePass, chromeSalt, 1003, 16, sha1.New) |
||||||
|
c.SecretKey = key |
||||||
|
return err |
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
package core |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/base64" |
||||||
|
"errors" |
||||||
|
"hack-browser-data/core/decrypt" |
||||||
|
"hack-browser-data/utils" |
||||||
|
"os" |
||||||
|
|
||||||
|
"github.com/tidwall/gjson" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
chromeProfilePath = "/AppData/Local/Google/Chrome/User Data/*/" |
||||||
|
chromeKeyPath = "/AppData/Local/Google/Chrome/User Data/Local State" |
||||||
|
edgeProfilePath = "/AppData/Local/Microsoft/Edge/User Data/*/" |
||||||
|
edgeKeyPath = "/AppData/Local/Microsoft/Edge/User Data/Local State" |
||||||
|
speed360ProfilePath = "/AppData/Local/360chrome/Chrome/User Data/*/" |
||||||
|
speed360KeyPath = "" |
||||||
|
qqBrowserProfilePath = "/AppData/Local/Tencent/QQBrowser/User Data/*/" |
||||||
|
qqBrowserKeyPath = "" |
||||||
|
firefoxProfilePath = "/AppData/Roaming/Mozilla/Firefox/Profiles/*.default-release/" |
||||||
|
firefoxKeyPath = "" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
browserList = map[string]struct { |
||||||
|
ProfilePath string |
||||||
|
Name string |
||||||
|
KeyPath string |
||||||
|
New func(profile, key, name string) (Browser, error) |
||||||
|
}{ |
||||||
|
"chrome": { |
||||||
|
ProfilePath: os.Getenv("USERPROFILE") + chromeProfilePath, |
||||||
|
KeyPath: os.Getenv("USERPROFILE") + chromeKeyPath, |
||||||
|
Name: chromeName, |
||||||
|
New: decryptChromium, |
||||||
|
}, |
||||||
|
"edge": { |
||||||
|
ProfilePath: os.Getenv("USERPROFILE") + edgeProfilePath, |
||||||
|
KeyPath: os.Getenv("USERPROFILE") + edgeKeyPath, |
||||||
|
Name: edgeName, |
||||||
|
New: decryptChromium, |
||||||
|
}, |
||||||
|
"360": { |
||||||
|
ProfilePath: os.Getenv("USERPROFILE") + speed360ProfilePath, |
||||||
|
Name: speed360Name, |
||||||
|
New: decryptChromium, |
||||||
|
}, |
||||||
|
"qq": { |
||||||
|
ProfilePath: os.Getenv("USERPROFILE") + qqBrowserProfilePath, |
||||||
|
Name: qqBrowserName, |
||||||
|
New: decryptChromium, |
||||||
|
}, |
||||||
|
"firefox": { |
||||||
|
ProfilePath: os.Getenv("USERPROFILE") + firefoxProfilePath, |
||||||
|
Name: firefoxName, |
||||||
|
New: decryptFirefox, |
||||||
|
}, |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
errBase64DecodeFailed = errors.New("decode base64 failed") |
||||||
|
) |
||||||
|
|
||||||
|
func (c *chromium) InitSecretKey() error { |
||||||
|
if c.KeyPath == "" { |
||||||
|
return nil |
||||||
|
} |
||||||
|
keyFile, err := utils.ReadFile(c.KeyPath) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
encryptedKey := gjson.Get(keyFile, "os_crypt.encrypted_key") |
||||||
|
if encryptedKey.Exists() { |
||||||
|
pureKey, err := base64.StdEncoding.DecodeString(encryptedKey.String()) |
||||||
|
if err != nil { |
||||||
|
return errBase64DecodeFailed |
||||||
|
} |
||||||
|
c.SecretKey, err = decrypt.DPApi(pureKey[5:]) |
||||||
|
return err |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,218 @@ |
|||||||
|
package common |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
"hack-browser-data/log" |
||||||
|
"hack-browser-data/utils" |
||||||
|
"os" |
||||||
|
|
||||||
|
"github.com/jszwec/csvutil" |
||||||
|
) |
||||||
|
|
||||||
|
var utf8Bom = []byte{239, 187, 191} |
||||||
|
|
||||||
|
func (b *Bookmarks) OutPutJson(browser, dir string) error { |
||||||
|
filename := utils.FormatFileName(dir, browser, "bookmark", "json") |
||||||
|
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644) |
||||||
|
defer func() { |
||||||
|
if err := file.Close(); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
}() |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
log.Errorf("create file %s fail", filename) |
||||||
|
return err |
||||||
|
} |
||||||
|
w := new(bytes.Buffer) |
||||||
|
enc := json.NewEncoder(w) |
||||||
|
enc.SetEscapeHTML(false) |
||||||
|
enc.SetIndent("", "\t") |
||||||
|
enc.Encode(b.bookmarks) |
||||||
|
_, err = file.Write(w.Bytes()) |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
fmt.Printf("%s Get %d bookmarks, filename is %s \n", log.Prefix, len(b.bookmarks), filename) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (h *History) OutPutJson(browser, dir string) error { |
||||||
|
filename := utils.FormatFileName(dir, browser, "history", "json") |
||||||
|
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644) |
||||||
|
defer func() { |
||||||
|
if err := file.Close(); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
}() |
||||||
|
if err != nil { |
||||||
|
log.Errorf("create file %s fail", filename) |
||||||
|
return err |
||||||
|
} |
||||||
|
w := new(bytes.Buffer) |
||||||
|
enc := json.NewEncoder(w) |
||||||
|
enc.SetEscapeHTML(false) |
||||||
|
enc.SetIndent("", "\t") |
||||||
|
err = enc.Encode(h.history) |
||||||
|
if err != nil { |
||||||
|
log.Debug(err) |
||||||
|
} |
||||||
|
_, err = file.Write(w.Bytes()) |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
fmt.Printf("%s Get %d history, filename is %s \n", log.Prefix, len(h.history), filename) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (l *Logins) OutPutJson(browser, dir string) error { |
||||||
|
filename := utils.FormatFileName(dir, browser, "password", "json") |
||||||
|
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644) |
||||||
|
defer func() { |
||||||
|
if err := file.Close(); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
}() |
||||||
|
if err != nil { |
||||||
|
log.Errorf("create file %s fail", filename) |
||||||
|
return err |
||||||
|
} |
||||||
|
w := new(bytes.Buffer) |
||||||
|
enc := json.NewEncoder(w) |
||||||
|
enc.SetEscapeHTML(false) |
||||||
|
enc.SetIndent("", "\t") |
||||||
|
err = enc.Encode(l.logins) |
||||||
|
if err != nil { |
||||||
|
log.Debug(err) |
||||||
|
} |
||||||
|
_, err = file.Write(w.Bytes()) |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
fmt.Printf("%s Get %d history, filename is %s \n", log.Prefix, len(l.logins), filename) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (c *Cookies) OutPutJson(browser, dir string) error { |
||||||
|
filename := utils.FormatFileName(dir, browser, "cookie", "json") |
||||||
|
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644) |
||||||
|
defer func() { |
||||||
|
if err := file.Close(); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
}() |
||||||
|
if err != nil { |
||||||
|
log.Errorf("create file %s fail", filename) |
||||||
|
return err |
||||||
|
} |
||||||
|
w := new(bytes.Buffer) |
||||||
|
enc := json.NewEncoder(w) |
||||||
|
enc.SetEscapeHTML(false) |
||||||
|
enc.SetIndent("", "\t") |
||||||
|
err = enc.Encode(c.cookies) |
||||||
|
if err != nil { |
||||||
|
log.Debug(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
_, err = file.Write(w.Bytes()) |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
fmt.Printf("%s Get %d history, filename is %s \n", log.Prefix, len(c.cookies), filename) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (b *Bookmarks) OutPutCsv(browser, dir string) error { |
||||||
|
filename := utils.FormatFileName(dir, browser, "bookmark", "csv") |
||||||
|
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644) |
||||||
|
defer func() { |
||||||
|
if err := file.Close(); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
}() |
||||||
|
if err != nil { |
||||||
|
log.Errorf("create file %s fail %s", filename, err) |
||||||
|
return err |
||||||
|
} |
||||||
|
file.Write(utf8Bom) |
||||||
|
data, err := csvutil.Marshal(b.bookmarks) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
file.Write(data) |
||||||
|
fmt.Printf("%s Get %d bookmarks, filename is %s \n", log.Prefix, len(b.bookmarks), filename) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (h *History) OutPutCsv(browser, dir string) error { |
||||||
|
filename := utils.FormatFileName(dir, browser, "history", "csv") |
||||||
|
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644) |
||||||
|
defer func() { |
||||||
|
if err := file.Close(); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
}() |
||||||
|
if err != nil { |
||||||
|
log.Errorf("create file %s fail %s", filename, err) |
||||||
|
return err |
||||||
|
} |
||||||
|
file.Write(utf8Bom) |
||||||
|
data, err := csvutil.Marshal(h.history) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
file.Write(data) |
||||||
|
fmt.Printf("%s Get %d bookmarks, filename is %s \n", log.Prefix, len(h.history), filename) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (l *Logins) OutPutCsv(browser, dir string) error { |
||||||
|
filename := utils.FormatFileName(dir, browser, "password", "csv") |
||||||
|
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644) |
||||||
|
defer func() { |
||||||
|
if err := file.Close(); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
}() |
||||||
|
if err != nil { |
||||||
|
log.Errorf("create file %s fail %s", filename, err) |
||||||
|
return err |
||||||
|
} |
||||||
|
file.Write(utf8Bom) |
||||||
|
data, err := csvutil.Marshal(l.logins) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
file.Write(data) |
||||||
|
fmt.Printf("%s Get %d bookmarks, filename is %s \n", log.Prefix, len(l.logins), filename) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (c *Cookies) OutPutCsv(browser, dir string) error { |
||||||
|
filename := utils.FormatFileName(dir, browser, "cookie", "csv") |
||||||
|
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644) |
||||||
|
defer func() { |
||||||
|
if err := file.Close(); err != nil { |
||||||
|
log.Error(err) |
||||||
|
} |
||||||
|
}() |
||||||
|
if err != nil { |
||||||
|
log.Errorf("create file %s fail", filename) |
||||||
|
return err |
||||||
|
} |
||||||
|
var tempSlice []cookies |
||||||
|
for _, v := range c.cookies { |
||||||
|
tempSlice = append(tempSlice, v...) |
||||||
|
} |
||||||
|
file.Write(utf8Bom) |
||||||
|
data, err := csvutil.Marshal(tempSlice) |
||||||
|
file.Write(data) |
||||||
|
fmt.Printf("%s Get %d cookies, filename is %s \n", log.Prefix, len(c.cookies), filename) |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
package decrypt |
||||||
|
|
||||||
|
import ( |
||||||
|
"crypto/aes" |
||||||
|
"crypto/cipher" |
||||||
|
"crypto/des" |
||||||
|
"encoding/asn1" |
||||||
|
"errors" |
||||||
|
"hack-browser-data/log" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
errKeyIsEmpty = errors.New("input [security find-generic-password -wa 'Chrome'] in terminal") |
||||||
|
errPasswordIsEmpty = errors.New("password is empty") |
||||||
|
errDecryptFailed = errors.New("decrypt failed, password is empty") |
||||||
|
) |
||||||
|
|
||||||
|
func aes128CBCDecrypt(key, iv, encryptPass []byte) ([]byte, error) { |
||||||
|
block, err := aes.NewCipher(key) |
||||||
|
if err != nil { |
||||||
|
return []byte{}, err |
||||||
|
} |
||||||
|
dst := make([]byte, len(encryptPass)) |
||||||
|
mode := cipher.NewCBCDecrypter(block, iv) |
||||||
|
mode.CryptBlocks(dst, encryptPass) |
||||||
|
dst = PKCS5UnPadding(dst) |
||||||
|
return dst, nil |
||||||
|
} |
||||||
|
|
||||||
|
func PKCS5UnPadding(src []byte) []byte { |
||||||
|
length := len(src) |
||||||
|
unpad := int(src[length-1]) |
||||||
|
return src[:(length - unpad)] |
||||||
|
} |
||||||
|
func Des3Decrypt(key, iv []byte, src []byte) ([]byte, error) { |
||||||
|
block, err := des.NewTripleDESCipher(key) |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
blockMode := cipher.NewCBCDecrypter(block, iv) |
||||||
|
sq := make([]byte, len(src)) |
||||||
|
blockMode.CryptBlocks(sq, src) |
||||||
|
return sq, nil |
||||||
|
} |
||||||
|
|
||||||
|
func PaddingZero(s []byte, l int) []byte { |
||||||
|
h := l - len(s) |
||||||
|
if h <= 0 { |
||||||
|
return s |
||||||
|
} else { |
||||||
|
for i := len(s); i < l; i++ { |
||||||
|
s = append(s, 0) |
||||||
|
} |
||||||
|
return s |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
SEQUENCE (3 elem) |
||||||
|
OCTET STRING (16 byte) |
||||||
|
SEQUENCE (2 elem) |
||||||
|
OBJECT IDENTIFIER 1.2.840.113549.3.7 des-EDE3-CBC (RSADSI encryptionAlgorithm) |
||||||
|
OCTET STRING (8 byte) |
||||||
|
OCTET STRING (16 byte) |
||||||
|
*/ |
||||||
|
type LoginPBE struct { |
||||||
|
CipherText []byte |
||||||
|
SequenceLogin |
||||||
|
Encrypted []byte |
||||||
|
} |
||||||
|
|
||||||
|
type SequenceLogin struct { |
||||||
|
asn1.ObjectIdentifier |
||||||
|
Iv []byte |
||||||
|
} |
||||||
|
|
||||||
|
func DecodeLogin(decodeItem []byte) (pbe LoginPBE, err error) { |
||||||
|
_, err = asn1.Unmarshal(decodeItem, &pbe) |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
return |
||||||
|
} |
||||||
|
return pbe, nil |
||||||
|
} |
@ -0,0 +1,115 @@ |
|||||||
|
package decrypt |
||||||
|
|
||||||
|
import ( |
||||||
|
"crypto/hmac" |
||||||
|
"crypto/sha1" |
||||||
|
"encoding/asn1" |
||||||
|
"hack-browser-data/log" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
chromeIV = []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32} |
||||||
|
) |
||||||
|
|
||||||
|
func ChromePass(key, encryptPass []byte) ([]byte, error) { |
||||||
|
if len(encryptPass) > 3 { |
||||||
|
if len(key) == 0 { |
||||||
|
return nil, errKeyIsEmpty |
||||||
|
} |
||||||
|
m, err := aes128CBCDecrypt(key, chromeIV, encryptPass[3:]) |
||||||
|
return m, err |
||||||
|
} else { |
||||||
|
return nil, errDecryptFailed |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
SEQUENCE (2 elem) |
||||||
|
SEQUENCE (2 elem) |
||||||
|
OBJECT IDENTIFIER |
||||||
|
SEQUENCE (2 elem) |
||||||
|
OCTET STRING (20 byte) |
||||||
|
INTEGER 1 |
||||||
|
OCTET STRING (16 byte) |
||||||
|
*/ |
||||||
|
|
||||||
|
type NssPBE struct { |
||||||
|
SequenceA |
||||||
|
Encrypted []byte |
||||||
|
} |
||||||
|
|
||||||
|
type MetaPBE struct { |
||||||
|
SequenceA |
||||||
|
Encrypted []byte |
||||||
|
} |
||||||
|
|
||||||
|
type SequenceA struct { |
||||||
|
DecryptMethod asn1.ObjectIdentifier |
||||||
|
SequenceB |
||||||
|
} |
||||||
|
|
||||||
|
type SequenceB struct { |
||||||
|
EntrySalt []byte |
||||||
|
Len int |
||||||
|
} |
||||||
|
|
||||||
|
func DecodeMeta(metaBytes []byte) (pbe MetaPBE, err error) { |
||||||
|
_, err = asn1.Unmarshal(metaBytes, &pbe) |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
return |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func DPApi(data []byte) ([]byte, error) { |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
|
||||||
|
func DecodeNss(nssA11Bytes []byte) (pbe NssPBE, err error) { |
||||||
|
_, err = asn1.Unmarshal(nssA11Bytes, &pbe) |
||||||
|
if err != nil { |
||||||
|
log.Error(err) |
||||||
|
return |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func Meta(globalSalt, masterPwd []byte, pbe MetaPBE) ([]byte, error) { |
||||||
|
return decryptPBE(globalSalt, masterPwd, pbe.EntrySalt, pbe.Encrypted) |
||||||
|
} |
||||||
|
|
||||||
|
func Nss(globalSalt, masterPwd []byte, pbe NssPBE) ([]byte, error) { |
||||||
|
return decryptPBE(globalSalt, masterPwd, pbe.EntrySalt, pbe.Encrypted) |
||||||
|
} |
||||||
|
|
||||||
|
func decryptPBE(globalSalt, masterPwd, entrySalt, encrypted []byte) ([]byte, error) { |
||||||
|
//byte[] GLMP; // GlobalSalt + MasterPassword
|
||||||
|
//byte[] HP; // SHA1(GLMP)
|
||||||
|
//byte[] HPES; // HP + EntrySalt
|
||||||
|
//byte[] CHP; // SHA1(HPES)
|
||||||
|
//byte[] PES; // EntrySalt completed to 20 bytes by zero
|
||||||
|
//byte[] PESES; // PES + EntrySalt
|
||||||
|
//byte[] k1;
|
||||||
|
//byte[] tk;
|
||||||
|
//byte[] k2;
|
||||||
|
//byte[] k; // final value conytaining key and iv
|
||||||
|
glmp := append(globalSalt, masterPwd...) |
||||||
|
hp := sha1.Sum(glmp) |
||||||
|
s := append(hp[:], entrySalt...) |
||||||
|
chp := sha1.Sum(s) |
||||||
|
pes := PaddingZero(entrySalt, 20) |
||||||
|
tk := hmac.New(sha1.New, chp[:]) |
||||||
|
tk.Write(pes) |
||||||
|
pes = append(pes, entrySalt...) |
||||||
|
k1 := hmac.New(sha1.New, chp[:]) |
||||||
|
k1.Write(pes) |
||||||
|
tkPlus := append(tk.Sum(nil), entrySalt...) |
||||||
|
k2 := hmac.New(sha1.New, chp[:]) |
||||||
|
k2.Write(tkPlus) |
||||||
|
k := append(k1.Sum(nil), k2.Sum(nil)...) |
||||||
|
iv := k[len(k)-8:] |
||||||
|
key := k[:24] |
||||||
|
log.Debug("get firefox pbe key and iv success") |
||||||
|
return Des3Decrypt(key, iv, encrypted) |
||||||
|
} |
@ -1,156 +0,0 @@ |
|||||||
package core |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"encoding/json" |
|
||||||
"fmt" |
|
||||||
"hack-browser-data/log" |
|
||||||
"hack-browser-data/utils" |
|
||||||
"os" |
|
||||||
"sort" |
|
||||||
|
|
||||||
"github.com/jszwec/csvutil" |
|
||||||
) |
|
||||||
|
|
||||||
var utf8Bom = []byte{239, 187, 191} |
|
||||||
|
|
||||||
func (b BrowserData) OutPutCsv(dir, browser, format string) error { |
|
||||||
switch { |
|
||||||
case len(b.BookmarkSlice) != 0: |
|
||||||
filename := utils.FormatFileName(dir, browser, utils.Bookmarks, format) |
|
||||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644) |
|
||||||
defer file.Close() |
|
||||||
if err != nil { |
|
||||||
log.Errorf("create file %s fail %s", filename, err) |
|
||||||
} |
|
||||||
file.Write(utf8Bom) |
|
||||||
data, err := csvutil.Marshal(b.BookmarkSlice) |
|
||||||
file.Write(data) |
|
||||||
fmt.Printf("%s Get %d bookmarks, filename is %s \n", log.Prefix, len(b.BookmarkSlice), filename) |
|
||||||
fallthrough |
|
||||||
case len(b.LoginDataSlice) != 0: |
|
||||||
filename := utils.FormatFileName(dir, browser, utils.LoginData, format) |
|
||||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644) |
|
||||||
defer file.Close() |
|
||||||
if err != nil { |
|
||||||
log.Errorf("create file %s fail", filename) |
|
||||||
} |
|
||||||
file.Write(utf8Bom) |
|
||||||
data, err := csvutil.Marshal(b.LoginDataSlice) |
|
||||||
file.Write(data) |
|
||||||
fmt.Printf("%s Get %d login data, filename is %s \n", log.Prefix, len(b.LoginDataSlice), filename) |
|
||||||
fallthrough |
|
||||||
case len(b.CookieMap) != 0: |
|
||||||
filename := utils.FormatFileName(dir, browser, utils.Cookies, format) |
|
||||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644) |
|
||||||
defer file.Close() |
|
||||||
if err != nil { |
|
||||||
log.Errorf("create file %s fail", filename) |
|
||||||
} |
|
||||||
var tempSlice []cookies |
|
||||||
for _, v := range b.CookieMap { |
|
||||||
tempSlice = append(tempSlice, v...) |
|
||||||
} |
|
||||||
file.Write(utf8Bom) |
|
||||||
data, err := csvutil.Marshal(tempSlice) |
|
||||||
file.Write(data) |
|
||||||
fmt.Printf("%s Get %d cookies, filename is %s \n", log.Prefix, len(b.CookieMap), filename) |
|
||||||
fallthrough |
|
||||||
case len(b.HistorySlice) != 0: |
|
||||||
filename := utils.FormatFileName(dir, browser, utils.History, format) |
|
||||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644) |
|
||||||
defer file.Close() |
|
||||||
if err != nil { |
|
||||||
log.Errorf("create file %s fail", filename) |
|
||||||
} |
|
||||||
file.Write(utf8Bom) |
|
||||||
data, err := csvutil.Marshal(b.HistorySlice) |
|
||||||
file.Write(data) |
|
||||||
fmt.Printf("%s Get %d login data, filename is %s \n", log.Prefix, len(b.HistorySlice), filename) |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (b BrowserData) OutPutJson(dir, browser, format string) error { |
|
||||||
switch { |
|
||||||
case len(b.BookmarkSlice) != 0: |
|
||||||
filename := utils.FormatFileName(dir, browser, utils.Bookmarks, format) |
|
||||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644) |
|
||||||
defer file.Close() |
|
||||||
if err != nil { |
|
||||||
log.Errorf("create file %s fail", filename) |
|
||||||
} |
|
||||||
w := new(bytes.Buffer) |
|
||||||
enc := json.NewEncoder(w) |
|
||||||
enc.SetEscapeHTML(false) |
|
||||||
enc.SetIndent("", "\t") |
|
||||||
enc.Encode(b.BookmarkSlice) |
|
||||||
file.Write(w.Bytes()) |
|
||||||
fmt.Printf("%s Get %d bookmarks, filename is %s \n", log.Prefix, len(b.BookmarkSlice), filename) |
|
||||||
fallthrough |
|
||||||
case len(b.CookieMap) != 0: |
|
||||||
filename := utils.FormatFileName(dir, browser, utils.Cookies, format) |
|
||||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644) |
|
||||||
defer file.Close() |
|
||||||
if err != nil { |
|
||||||
log.Errorf("create file %s fail", filename) |
|
||||||
} |
|
||||||
w := new(bytes.Buffer) |
|
||||||
enc := json.NewEncoder(w) |
|
||||||
enc.SetEscapeHTML(false) |
|
||||||
enc.SetIndent("", "\t") |
|
||||||
err = enc.Encode(b.CookieMap) |
|
||||||
if err != nil { |
|
||||||
log.Debug(err) |
|
||||||
} |
|
||||||
file.Write(w.Bytes()) |
|
||||||
fmt.Printf("%s Get %d cookies, filename is %s \n", log.Prefix, len(b.CookieMap), filename) |
|
||||||
fallthrough |
|
||||||
case len(b.HistorySlice) != 0: |
|
||||||
filename := utils.FormatFileName(dir, browser, utils.History, format) |
|
||||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644) |
|
||||||
defer file.Close() |
|
||||||
if err != nil { |
|
||||||
log.Errorf("create file %s fail", filename) |
|
||||||
} |
|
||||||
w := new(bytes.Buffer) |
|
||||||
enc := json.NewEncoder(w) |
|
||||||
enc.SetEscapeHTML(false) |
|
||||||
enc.SetIndent("", "\t") |
|
||||||
err = enc.Encode(b.HistorySlice) |
|
||||||
if err != nil { |
|
||||||
log.Debug(err) |
|
||||||
} |
|
||||||
file.Write(w.Bytes()) |
|
||||||
fmt.Printf("%s Get %d history, filename is %s \n", log.Prefix, len(b.HistorySlice), filename) |
|
||||||
fallthrough |
|
||||||
case len(b.LoginDataSlice) != 0: |
|
||||||
filename := utils.FormatFileName(dir, browser, utils.LoginData, format) |
|
||||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644) |
|
||||||
defer file.Close() |
|
||||||
if err != nil { |
|
||||||
log.Errorf("create file %s fail", filename) |
|
||||||
} |
|
||||||
w := new(bytes.Buffer) |
|
||||||
enc := json.NewEncoder(w) |
|
||||||
enc.SetEscapeHTML(false) |
|
||||||
enc.SetIndent("", "\t") |
|
||||||
err = enc.Encode(b.LoginDataSlice) |
|
||||||
if err != nil { |
|
||||||
log.Debug(err) |
|
||||||
} |
|
||||||
file.Write(w.Bytes()) |
|
||||||
fmt.Printf("%s Get %d login data, filename is %s \n", log.Prefix, len(b.LoginDataSlice), filename) |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (b BrowserData) Sorted() { |
|
||||||
sort.Slice(b.BookmarkSlice, func(i, j int) bool { |
|
||||||
return b.BookmarkSlice[i].ID < b.BookmarkSlice[j].ID |
|
||||||
}) |
|
||||||
sort.Slice(b.HistorySlice, func(i, j int) bool { |
|
||||||
return b.HistorySlice[i].VisitCount > b.HistorySlice[j].VisitCount |
|
||||||
}) |
|
||||||
sort.Sort(b.LoginDataSlice) |
|
||||||
} |
|
@ -1,13 +0,0 @@ |
|||||||
package core |
|
||||||
|
|
||||||
func (l LoginDataSlice) Len() int { |
|
||||||
return len(l) |
|
||||||
} |
|
||||||
|
|
||||||
func (l LoginDataSlice) Less(i, j int) bool { |
|
||||||
return l[i].CreateDate.After(l[j].CreateDate) |
|
||||||
} |
|
||||||
|
|
||||||
func (l LoginDataSlice) Swap(i, j int) { |
|
||||||
l[i], l[j] = l[j], l[i] |
|
||||||
} |
|
@ -1,190 +0,0 @@ |
|||||||
package utils |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"crypto/hmac" |
|
||||||
"crypto/sha1" |
|
||||||
"encoding/asn1" |
|
||||||
"encoding/hex" |
|
||||||
"errors" |
|
||||||
"hack-browser-data/log" |
|
||||||
"os/exec" |
|
||||||
"strings" |
|
||||||
|
|
||||||
"golang.org/x/crypto/pbkdf2" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
chromeProfilePath = "/Users/*/Library/Application Support/Google/Chrome/*/" |
|
||||||
chromeCommand = "Chrome" |
|
||||||
edgeProfilePath = "/Users/*/Library/Application Support/Microsoft Edge/*/" |
|
||||||
edgeCommand = "Microsoft Edge" |
|
||||||
fireFoxProfilePath = "/Users/*/Library/Application Support/Firefox/Profiles/*.default-release/" |
|
||||||
fireFoxCommand = "" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
iv = []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32} |
|
||||||
command = []string{"security", "find-generic-password", "-wa"} |
|
||||||
chromeSalt = []byte("saltysalt") |
|
||||||
chromeKey []byte |
|
||||||
browserList = map[string]struct { |
|
||||||
ProfilePath string |
|
||||||
Command string |
|
||||||
}{ |
|
||||||
"chrome": { |
|
||||||
chromeProfilePath, |
|
||||||
chromeCommand, |
|
||||||
}, |
|
||||||
"edge": { |
|
||||||
edgeProfilePath, |
|
||||||
edgeCommand, |
|
||||||
}, |
|
||||||
"firefox": { |
|
||||||
fireFoxProfilePath, |
|
||||||
fireFoxCommand, |
|
||||||
}, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
func DecryptStringWithDPAPI(data []byte) (string, error) { |
|
||||||
return string(data), nil |
|
||||||
} |
|
||||||
|
|
||||||
func PickBrowser(name string) (browserDir, command string, err error) { |
|
||||||
name = strings.ToLower(name) |
|
||||||
if choice, ok := browserList[name]; ok { |
|
||||||
return choice.ProfilePath, choice.Command, err |
|
||||||
} |
|
||||||
return "", "", errBrowserNotSupported |
|
||||||
} |
|
||||||
|
|
||||||
func InitKey(key string) error { |
|
||||||
var ( |
|
||||||
cmd *exec.Cmd |
|
||||||
stdout, stderr bytes.Buffer |
|
||||||
) |
|
||||||
cmd = exec.Command(command[0], command[1], command[2], key) |
|
||||||
cmd.Stdout = &stdout |
|
||||||
cmd.Stderr = &stderr |
|
||||||
err := cmd.Run() |
|
||||||
if err != nil { |
|
||||||
log.Error(err) |
|
||||||
return err |
|
||||||
} |
|
||||||
if stderr.Len() > 0 { |
|
||||||
err = errors.New(stderr.String()) |
|
||||||
log.Error(err) |
|
||||||
} |
|
||||||
temp := stdout.Bytes() |
|
||||||
chromePass := temp[:len(temp)-1] |
|
||||||
decryptChromeKey(chromePass) |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
func decryptChromeKey(chromePass []byte) { |
|
||||||
chromeKey = pbkdf2.Key(chromePass, chromeSalt, 1003, 16, sha1.New) |
|
||||||
} |
|
||||||
|
|
||||||
func DecryptChromePass(encryptPass []byte) (string, error) { |
|
||||||
if len(encryptPass) > 3 { |
|
||||||
if len(chromeKey) == 0 { |
|
||||||
return "", errKeyIsEmpty |
|
||||||
} |
|
||||||
m, err := aes128CBCDecrypt(chromeKey, iv, encryptPass[3:]) |
|
||||||
return string(m), err |
|
||||||
} else { |
|
||||||
return "", &DecryptError{ |
|
||||||
err: errPasswordIsEmpty, |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/* |
|
||||||
SEQUENCE (2 elem) |
|
||||||
SEQUENCE (2 elem) |
|
||||||
OBJECT IDENTIFIER |
|
||||||
SEQUENCE (2 elem) |
|
||||||
OCTET STRING (20 byte) |
|
||||||
INTEGER 1 |
|
||||||
OCTET STRING (16 byte) |
|
||||||
*/ |
|
||||||
|
|
||||||
type NssPBE struct { |
|
||||||
SequenceA |
|
||||||
Encrypted []byte |
|
||||||
} |
|
||||||
|
|
||||||
type MetaPBE struct { |
|
||||||
SequenceA |
|
||||||
Encrypted []byte |
|
||||||
} |
|
||||||
|
|
||||||
type SequenceA struct { |
|
||||||
DecryptMethod asn1.ObjectIdentifier |
|
||||||
SequenceB |
|
||||||
} |
|
||||||
|
|
||||||
type SequenceB struct { |
|
||||||
EntrySalt []byte |
|
||||||
Len int |
|
||||||
} |
|
||||||
|
|
||||||
func DecodeMeta(metaBytes []byte) (pbe MetaPBE, err error) { |
|
||||||
log.Debug(hex.EncodeToString(metaBytes)) |
|
||||||
_, err = asn1.Unmarshal(metaBytes, &pbe) |
|
||||||
if err != nil { |
|
||||||
log.Error(err) |
|
||||||
return |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func DecodeNss(nssA11Bytes []byte) (pbe NssPBE, err error) { |
|
||||||
log.Debug(hex.EncodeToString(nssA11Bytes)) |
|
||||||
_, err = asn1.Unmarshal(nssA11Bytes, &pbe) |
|
||||||
if err != nil { |
|
||||||
log.Error(err) |
|
||||||
return |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func DecryptMeta(globalSalt, masterPwd []byte, pbe MetaPBE) ([]byte, error) { |
|
||||||
return decryptPBE(globalSalt, masterPwd, pbe.EntrySalt, pbe.Encrypted) |
|
||||||
} |
|
||||||
|
|
||||||
func DecryptNss(globalSalt, masterPwd []byte, pbe NssPBE) ([]byte, error) { |
|
||||||
return decryptPBE(globalSalt, masterPwd, pbe.EntrySalt, pbe.Encrypted) |
|
||||||
} |
|
||||||
|
|
||||||
func decryptPBE(globalSalt, masterPwd, entrySalt, encrypted []byte) ([]byte, error) { |
|
||||||
//byte[] GLMP; // GlobalSalt + MasterPassword
|
|
||||||
//byte[] HP; // SHA1(GLMP)
|
|
||||||
//byte[] HPES; // HP + EntrySalt
|
|
||||||
//byte[] CHP; // SHA1(HPES)
|
|
||||||
//byte[] PES; // EntrySalt completed to 20 bytes by zero
|
|
||||||
//byte[] PESES; // PES + EntrySalt
|
|
||||||
//byte[] k1;
|
|
||||||
//byte[] tk;
|
|
||||||
//byte[] k2;
|
|
||||||
//byte[] k; // final value conytaining key and iv
|
|
||||||
glmp := append(globalSalt, masterPwd...) |
|
||||||
hp := sha1.Sum(glmp) |
|
||||||
s := append(hp[:], entrySalt...) |
|
||||||
chp := sha1.Sum(s) |
|
||||||
pes := PaddingZero(entrySalt, 20) |
|
||||||
tk := hmac.New(sha1.New, chp[:]) |
|
||||||
tk.Write(pes) |
|
||||||
pes = append(pes, entrySalt...) |
|
||||||
k1 := hmac.New(sha1.New, chp[:]) |
|
||||||
k1.Write(pes) |
|
||||||
tkPlus := append(tk.Sum(nil), entrySalt...) |
|
||||||
k2 := hmac.New(sha1.New, chp[:]) |
|
||||||
k2.Write(tkPlus) |
|
||||||
k := append(k1.Sum(nil), k2.Sum(nil)...) |
|
||||||
iv := k[len(k)-8:] |
|
||||||
key := k[:24] |
|
||||||
log.Warnf("key=%s iv=%s", hex.EncodeToString(key), hex.EncodeToString(iv)) |
|
||||||
return Des3Decrypt(key, iv, encrypted) |
|
||||||
} |
|
Loading…
Reference in new issue