mirror of
https://github.com/moonD4rk/HackBrowserData.git
synced 2026-05-19 18:58:03 +02:00
235 lines
5.8 KiB
Go
235 lines
5.8 KiB
Go
package localstorage
|
|
|
|
import (
|
|
"bytes"
|
|
"database/sql"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/syndtr/goleveldb/leveldb"
|
|
"golang.org/x/text/encoding/unicode"
|
|
"golang.org/x/text/transform"
|
|
|
|
"github.com/moond4rk/hackbrowserdata/extractor"
|
|
"github.com/moond4rk/hackbrowserdata/log"
|
|
"github.com/moond4rk/hackbrowserdata/types"
|
|
"github.com/moond4rk/hackbrowserdata/utils/typeutil"
|
|
)
|
|
|
|
func init() {
|
|
extractor.RegisterExtractor(types.ChromiumLocalStorage, func() extractor.Extractor {
|
|
return new(ChromiumLocalStorage)
|
|
})
|
|
extractor.RegisterExtractor(types.FirefoxLocalStorage, func() extractor.Extractor {
|
|
return new(FirefoxLocalStorage)
|
|
})
|
|
}
|
|
|
|
type ChromiumLocalStorage []storage
|
|
|
|
type storage struct {
|
|
IsMeta bool
|
|
URL string
|
|
Key string
|
|
Value string
|
|
}
|
|
|
|
const maxLocalStorageValueLength = 1024 * 2
|
|
|
|
const (
|
|
chromiumLocalStorageVersionKey = "VERSION"
|
|
chromiumLocalStorageMetaPrefix = "META:"
|
|
chromiumLocalStorageMetaAccessKey = "METAACCESS:"
|
|
chromiumLocalStorageDataPrefix = '_'
|
|
chromiumStringUTF16Format = 0
|
|
chromiumStringLatin1Format = 1
|
|
)
|
|
|
|
func (c *ChromiumLocalStorage) Extract(_ []byte) error {
|
|
entries, err := extractChromiumLocalStorage(types.ChromiumLocalStorage.TempFilename())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer os.RemoveAll(types.ChromiumLocalStorage.TempFilename())
|
|
*c = append(*c, entries...)
|
|
return nil
|
|
}
|
|
|
|
func (c *ChromiumLocalStorage) Name() string {
|
|
return "localStorage"
|
|
}
|
|
|
|
func (c *ChromiumLocalStorage) Len() int {
|
|
return len(*c)
|
|
}
|
|
|
|
func extractChromiumLocalStorage(path string) (ChromiumLocalStorage, error) {
|
|
db, err := leveldb.OpenFile(path, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer db.Close()
|
|
|
|
var entries ChromiumLocalStorage
|
|
iter := db.NewIterator(nil, nil)
|
|
defer iter.Release()
|
|
|
|
for iter.Next() {
|
|
entry, ok := parseChromiumLocalStorageEntry(iter.Key(), iter.Value())
|
|
if !ok {
|
|
continue
|
|
}
|
|
entries = append(entries, entry)
|
|
}
|
|
return entries, iter.Error()
|
|
}
|
|
|
|
func parseChromiumLocalStorageEntry(key, value []byte) (storage, bool) {
|
|
switch {
|
|
case bytes.Equal(key, []byte(chromiumLocalStorageVersionKey)):
|
|
return storage{}, false
|
|
case bytes.HasPrefix(key, []byte(chromiumLocalStorageMetaAccessKey)):
|
|
return storage{
|
|
IsMeta: true,
|
|
URL: string(bytes.TrimPrefix(key, []byte(chromiumLocalStorageMetaAccessKey))),
|
|
Value: fmt.Sprintf("meta data, value bytes is %v", value),
|
|
}, true
|
|
case bytes.HasPrefix(key, []byte(chromiumLocalStorageMetaPrefix)):
|
|
return storage{
|
|
IsMeta: true,
|
|
URL: string(bytes.TrimPrefix(key, []byte(chromiumLocalStorageMetaPrefix))),
|
|
Value: fmt.Sprintf("meta data, value bytes is %v", value),
|
|
}, true
|
|
case len(key) > 0 && key[0] == chromiumLocalStorageDataPrefix:
|
|
return parseChromiumLocalStorageDataEntry(key[1:], value), true
|
|
default:
|
|
return storage{}, false
|
|
}
|
|
}
|
|
|
|
func parseChromiumLocalStorageDataEntry(key, value []byte) storage {
|
|
entry := storage{
|
|
Value: decodeChromiumLocalStorageValue(value),
|
|
}
|
|
|
|
separator := bytes.IndexByte(key, 0)
|
|
if separator < 0 {
|
|
entry.Key = "unsupported chromium localStorage key encoding: missing origin separator"
|
|
return entry
|
|
}
|
|
|
|
entry.URL = string(key[:separator])
|
|
scriptKey, err := decodeChromiumString(key[separator+1:])
|
|
if err != nil {
|
|
entry.Key = fmt.Sprintf("unsupported chromium localStorage key encoding: %v", err)
|
|
return entry
|
|
}
|
|
entry.Key = scriptKey
|
|
return entry
|
|
}
|
|
|
|
func convertUTF16toUTF8(source []byte, endian unicode.Endianness) ([]byte, error) {
|
|
r, _, err := transform.Bytes(unicode.UTF16(endian, unicode.IgnoreBOM).NewDecoder(), source)
|
|
return r, err
|
|
}
|
|
|
|
func decodeChromiumString(b []byte) (string, error) {
|
|
if len(b) == 0 {
|
|
return "", fmt.Errorf("empty chromium string")
|
|
}
|
|
|
|
switch b[0] {
|
|
case chromiumStringLatin1Format:
|
|
return string(b[1:]), nil
|
|
case chromiumStringUTF16Format:
|
|
if len(b) == 1 {
|
|
return "", nil
|
|
}
|
|
if (len(b)-1)%2 != 0 {
|
|
return "", fmt.Errorf("invalid UTF-16 byte length %d", len(b)-1)
|
|
}
|
|
value, err := convertUTF16toUTF8(b[1:], unicode.LittleEndian)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(value), nil
|
|
default:
|
|
return "", fmt.Errorf("unknown chromium string format 0x%02x", b[0])
|
|
}
|
|
}
|
|
|
|
func decodeChromiumLocalStorageValue(value []byte) string {
|
|
if len(value) >= maxLocalStorageValueLength {
|
|
return fmt.Sprintf(
|
|
"value is too long, length is %d, supported max length is %d",
|
|
len(value),
|
|
maxLocalStorageValueLength,
|
|
)
|
|
}
|
|
|
|
decoded, err := decodeChromiumString(value)
|
|
if err != nil {
|
|
return fmt.Sprintf("unsupported chromium localStorage value encoding: %v", err)
|
|
}
|
|
return decoded
|
|
}
|
|
|
|
type FirefoxLocalStorage []storage
|
|
|
|
const (
|
|
queryLocalStorage = `SELECT originKey, key, value FROM webappsstore2`
|
|
closeJournalMode = `PRAGMA journal_mode=off`
|
|
)
|
|
|
|
func (f *FirefoxLocalStorage) Extract(_ []byte) error {
|
|
db, err := sql.Open("sqlite", types.FirefoxLocalStorage.TempFilename())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer os.Remove(types.FirefoxLocalStorage.TempFilename())
|
|
defer db.Close()
|
|
|
|
_, err = db.Exec(closeJournalMode)
|
|
if err != nil {
|
|
log.Debugf("close journal mode error: %v", err)
|
|
}
|
|
rows, err := db.Query(queryLocalStorage)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer rows.Close()
|
|
for rows.Next() {
|
|
var originKey, key, value string
|
|
if err = rows.Scan(&originKey, &key, &value); err != nil {
|
|
log.Debugf("scan firefox local storage error: %v", err)
|
|
}
|
|
s := new(storage)
|
|
s.fillFirefox(originKey, key, value)
|
|
*f = append(*f, *s)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *storage) fillFirefox(originKey, key, value string) {
|
|
// originKey = moc.buhtig.:https:443
|
|
p := strings.Split(originKey, ":")
|
|
h := typeutil.Reverse([]byte(p[0]))
|
|
if bytes.HasPrefix(h, []byte(".")) {
|
|
h = h[1:]
|
|
}
|
|
if len(p) == 3 {
|
|
s.URL = fmt.Sprintf("%s://%s:%s", p[1], string(h), p[2])
|
|
}
|
|
s.Key = key
|
|
s.Value = value
|
|
}
|
|
|
|
func (f *FirefoxLocalStorage) Name() string {
|
|
return "localStorage"
|
|
}
|
|
|
|
func (f *FirefoxLocalStorage) Len() int {
|
|
return len(*f)
|
|
}
|