You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
HackBrowserData/chromium.go

204 lines
5.4 KiB

package hackbrowserdata
import (
"errors"
"fmt"
"io/fs"
"path/filepath"
"strings"
"github.com/moond4rk/hackbrowserdata/browserdata"
"github.com/moond4rk/hackbrowserdata/utils/fileutil"
)
type chromium struct {
name browser
storage string
profilePath string
enableAllUsers bool
profilePaths []string
masterKey []byte
// defaultDataTypes
supportedDataTypes []DataType
extractors map[DataType]browserdata.Extractor
extractedData map[DataType]interface{}
}
func NewChromium(options *Options) (Browser, error) {
if options.ProfilePath == "" {
return nil, errors.New("profile path is required")
}
if options.Name == "" {
return nil, errors.New("browser name is required")
}
c := &chromium{
name: options.Name,
profilePath: options.ProfilePath,
enableAllUsers: true,
supportedDataTypes: defaultDataTypes,
extractors: make(map[DataType]browserdata.Extractor),
extractedData: make(map[DataType]interface{}),
}
if !options.IsEnableAllUser {
c.enableAllUsers = false
}
if len(options.DataTypes) > 0 {
c.supportedDataTypes = options.DataTypes
}
if err := c.init(); err != nil {
return nil, err
}
return c, nil
}
func (c *chromium) init() error {
if err := c.initProfiles(); err != nil {
return fmt.Errorf("profile path '%s' does not exist %w", c.profilePath, ErrBrowserNotExists)
}
if err := c.initExtractors(); err != nil {
return err
}
return c.initMasterKey()
}
func (c *chromium) ExtractBrowserData(dataTypes []DataType) (map[DataType]interface{}, error) {
for _, dataType := range dataTypes {
if extractor, ok := c.extractors[dataType]; ok {
data, err := extractor.Extract()
if err != nil {
fmt.Printf("extract %s data failed: %v", dataType, err)
continue
}
c.extractedData[dataType] = data
}
}
return c.extractedData, nil
}
// func (c *chromium) Passwords() ([]password.Password, error) {
// // browserData, err := c.ExtractBrowserData([]DataType{TypePassword})
// // if err != nil {
// // return nil, err
// // }
// dataType := TypePassword
// if data, ok := c.extractedData[dataType]; ok {
// return data.([]password.Password), nil
// }
// extractor, ok := c.extractors[dataType]
// if !ok {
// return nil, fmt.Errorf("%s extractor for %s not found", dataType, c.name)
// }
// data, err := extractor.ExtractChromium()
// if err != nil {
// return nil, err
// }
// return data.([]password.Password), nil
// }
func (c *chromium) filterExistDataPaths(dataTypes []DataType) (map[DataType][]string, error) {
// exporters := make(map[DataType]BrowserData)
dataPaths := make(map[DataType][]string)
var errs []error
for _, profile := range c.profilePaths {
for _, dataType := range dataTypes {
dataTypeFile := filepath.Join(profile, dataType.Filename(c.name))
if !fileutil.IsFileExists(dataTypeFile) {
errs = append(errs, ErrBrowsingDataNotExists)
}
dataPaths[dataType] = append(dataPaths[dataType], dataTypeFile)
}
}
return dataPaths, nil
}
func (c *chromium) Passwords() ([]browserdata.Password, error) {
dataType := TypePassword
if data, ok := c.extractedData[dataType]; ok {
return data.([]browserdata.Password), nil
}
extractor, ok := c.extractors[dataType]
if !ok {
return nil, fmt.Errorf("%s extractor for %s not found", dataType, c.name)
}
data, err := extractor.Extract()
if err != nil {
return nil, err
}
return data.([]browserdata.Password), nil
}
func (c *chromium) Cookies() ([]browserdata.Cookie, error) {
dataType := TypeCookie
if data, ok := c.extractedData[dataType]; ok {
return data.([]browserdata.Cookie), nil
}
extractor, ok := c.extractors[dataType]
if !ok {
return nil, fmt.Errorf("%s extractor for %s not found", dataType, c.name)
}
data, err := extractor.Extract()
if err != nil {
return nil, err
}
return data.([]browserdata.Cookie), nil
}
func (c *chromium) initExtractors() error {
dataPaths, err := c.filterExistDataPaths(c.supportedDataTypes)
if err != nil {
return err
}
for _, dataType := range c.supportedDataTypes {
if _, ok := dataPaths[dataType]; !ok {
continue
}
c.extractors[dataType] = dataType.NewExtractor(c.name.Type(), c.masterKey, dataPaths[dataType])
}
return nil
}
func (c *chromium) initProfiles() error {
if !fileutil.IsDirExists(c.profilePath) {
return ErrBrowserNotExists
}
if c.enableAllUsers {
profilesPaths, err := c.findAllProfiles()
if err != nil {
return err
}
c.profilePaths = profilesPaths
} else {
c.profilePaths = []string{c.profilePath}
}
return nil
}
// TODO: mix it as firefox's find All Profiles
func (c *chromium) findAllProfiles() ([]string, error) {
var profiles []string
root := fileutil.ParentDir(c.profilePath)
err := filepath.Walk(root, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
// if the path ends with "History", add it to the list
if strings.HasSuffix(path, TypeHistory.Filename(c.name)) {
// skip the "System Profile" directory
if !strings.Contains(path, "System Profile") {
profiles = append(profiles, filepath.Dir(path))
}
}
// calculate the depth of the current path
depth := len(strings.Split(path, string(filepath.Separator))) - len(strings.Split(root, string(filepath.Separator)))
// if the depth is more than 2 and it's a directory, skip it
if info.IsDir() && path != root && depth >= 2 {
return filepath.SkipDir
}
return err
})
if err != nil {
return nil, err
}
return profiles, err
}