mirror of
https://github.com/moonD4rk/HackBrowserData.git
synced 2026-06-02 19:41:36 +02:00
refactor: naming cleanup and crypto package improvements (#551)
* refactor: naming cleanup across all packages
This commit is contained in:
@@ -3,6 +3,7 @@ package chromium
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/moond4rk/hackbrowserdata/crypto/keyretriever"
|
||||
"github.com/moond4rk/hackbrowserdata/filemanager"
|
||||
@@ -125,7 +126,7 @@ func (b *Browser) getMasterKey(session *filemanager.Session) ([]byte, error) {
|
||||
var localStateDst string
|
||||
for _, dir := range []string{filepath.Dir(b.profileDir), b.profileDir} {
|
||||
candidate := filepath.Join(dir, "Local State")
|
||||
if fileutil.IsFileExists(candidate) {
|
||||
if fileutil.FileExists(candidate) {
|
||||
localStateDst = filepath.Join(session.TempDir(), "Local State")
|
||||
if err := session.Acquire(candidate, localStateDst, false); err != nil {
|
||||
return nil, err
|
||||
@@ -273,3 +274,18 @@ func isSkippedDir(name string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// timeEpoch converts a WebKit/Chromium epoch timestamp (microseconds since
|
||||
// 1601-01-01) to a time.Time.
|
||||
func timeEpoch(epoch int64) time.Time {
|
||||
maxTime := int64(99633311740000000)
|
||||
if epoch > maxTime {
|
||||
return time.Date(2049, 1, 1, 1, 1, 1, 1, time.Local)
|
||||
}
|
||||
t := time.Date(1601, 1, 1, 0, 0, 0, 0, time.Local)
|
||||
d := time.Duration(epoch)
|
||||
for i := 0; i < 1000; i++ {
|
||||
t = t.Add(d)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ func TestNewBrowsers(t *testing.T) {
|
||||
{
|
||||
name: "chrome multi-profile",
|
||||
dir: fixture.chrome,
|
||||
kind: types.KindChromium,
|
||||
kind: types.Chromium,
|
||||
wantProfiles: []string{"Default", "Profile 1", "Profile 3"},
|
||||
wantCats: map[string][]string{
|
||||
"Default": {"Login Data", "Cookies", "History", "Bookmarks", "Web Data", "Secure Preferences", "leveldb", "Session Storage"},
|
||||
@@ -153,7 +153,7 @@ func TestNewBrowsers(t *testing.T) {
|
||||
{
|
||||
name: "opera with Default",
|
||||
dir: fixture.opera,
|
||||
kind: types.KindChromium,
|
||||
kind: types.Chromium,
|
||||
wantProfiles: []string{"Default"},
|
||||
wantCats: map[string][]string{
|
||||
"Default": {"Login Data", "History", "Bookmarks", "Cookies"},
|
||||
@@ -162,7 +162,7 @@ func TestNewBrowsers(t *testing.T) {
|
||||
{
|
||||
name: "opera flat layout",
|
||||
dir: fixture.operaFlat,
|
||||
kind: types.KindChromium,
|
||||
kind: types.Chromium,
|
||||
wantProfiles: []string{filepath.Base(fixture.operaFlat)}, // userDataDir itself
|
||||
wantCats: map[string][]string{
|
||||
filepath.Base(fixture.operaFlat): {"Login Data", "History", "Cookies"},
|
||||
@@ -171,7 +171,7 @@ func TestNewBrowsers(t *testing.T) {
|
||||
{
|
||||
name: "yandex custom files",
|
||||
dir: fixture.yandex,
|
||||
kind: types.KindChromiumYandex,
|
||||
kind: types.ChromiumYandex,
|
||||
wantProfiles: []string{"Default"},
|
||||
wantCats: map[string][]string{
|
||||
"Default": {"Ya Passman Data", "Ya Credit Cards", "History", "Cookies", "Bookmarks"},
|
||||
@@ -180,38 +180,38 @@ func TestNewBrowsers(t *testing.T) {
|
||||
{
|
||||
name: "old cookies fallback",
|
||||
dir: fixture.oldCookies,
|
||||
kind: types.KindChromium,
|
||||
kind: types.Chromium,
|
||||
wantProfiles: []string{"Default"},
|
||||
},
|
||||
{
|
||||
name: "cookie priority",
|
||||
dir: fixture.bothCookies,
|
||||
kind: types.KindChromium,
|
||||
kind: types.Chromium,
|
||||
wantProfiles: []string{"Default"},
|
||||
},
|
||||
{
|
||||
name: "leveldb directories",
|
||||
dir: fixture.leveldb,
|
||||
kind: types.KindChromium,
|
||||
kind: types.Chromium,
|
||||
wantProfiles: []string{"Default"},
|
||||
wantDirs: []types.Category{types.LocalStorage, types.SessionStorage},
|
||||
},
|
||||
{
|
||||
name: "leveldb only",
|
||||
dir: fixture.leveldbOnly,
|
||||
kind: types.KindChromium,
|
||||
kind: types.Chromium,
|
||||
wantProfiles: []string{"Default"},
|
||||
wantDirs: []types.Category{types.LocalStorage, types.SessionStorage},
|
||||
},
|
||||
{
|
||||
name: "empty dir",
|
||||
dir: fixture.empty,
|
||||
kind: types.KindChromium,
|
||||
kind: types.Chromium,
|
||||
},
|
||||
{
|
||||
name: "nonexistent dir",
|
||||
dir: "/nonexistent/path",
|
||||
kind: types.KindChromium,
|
||||
kind: types.Chromium,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -321,8 +321,8 @@ func TestSharedSourceFile(t *testing.T) {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestSourcesForKind(t *testing.T) {
|
||||
chromium := sourcesForKind(types.KindChromium)
|
||||
yandex := sourcesForKind(types.KindChromiumYandex)
|
||||
chromium := sourcesForKind(types.Chromium)
|
||||
yandex := sourcesForKind(types.ChromiumYandex)
|
||||
|
||||
assert.Equal(t, "Login Data", chromium[types.Password][0].rel)
|
||||
assert.Equal(t, "Ya Passman Data", yandex[types.Password][0].rel)
|
||||
@@ -331,13 +331,13 @@ func TestSourcesForKind(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractorsForKind(t *testing.T) {
|
||||
assert.Nil(t, extractorsForKind(types.KindChromium))
|
||||
assert.Nil(t, extractorsForKind(types.Chromium))
|
||||
|
||||
yandexExt := extractorsForKind(types.KindChromiumYandex)
|
||||
yandexExt := extractorsForKind(types.ChromiumYandex)
|
||||
require.NotNil(t, yandexExt)
|
||||
assert.Contains(t, yandexExt, types.Password)
|
||||
|
||||
operaExt := extractorsForKind(types.KindChromiumOpera)
|
||||
operaExt := extractorsForKind(types.ChromiumOpera)
|
||||
require.NotNil(t, operaExt)
|
||||
assert.Contains(t, operaExt, types.Extension)
|
||||
}
|
||||
@@ -425,7 +425,7 @@ func TestLocalStatePath(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
browsers, err := NewBrowsers(types.BrowserConfig{Name: "Test", Kind: types.KindChromium, UserDataDir: tt.dir})
|
||||
browsers, err := NewBrowsers(types.BrowserConfig{Name: "Test", Kind: types.Chromium, UserDataDir: tt.dir})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, browsers)
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@ func decryptValue(masterKey, ciphertext []byte) ([]byte, error) {
|
||||
version := crypto.DetectVersion(ciphertext)
|
||||
switch version {
|
||||
case crypto.CipherV10:
|
||||
return crypto.DecryptWithChromium(masterKey, ciphertext)
|
||||
return crypto.DecryptChromium(masterKey, ciphertext)
|
||||
case crypto.CipherV20:
|
||||
// TODO: implement App-Bound Encryption (Chrome 127+)
|
||||
return nil, fmt.Errorf("v20 App-Bound Encryption not yet supported")
|
||||
case crypto.CipherDPAPI:
|
||||
return crypto.DecryptWithDPAPI(ciphertext)
|
||||
return crypto.DecryptDPAPI(ciphertext)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported cipher version: %s", version)
|
||||
}
|
||||
|
||||
@@ -12,14 +12,12 @@ import (
|
||||
"github.com/moond4rk/hackbrowserdata/crypto"
|
||||
)
|
||||
|
||||
// testCBCIV is the fixed IV Chrome uses on macOS/Linux (16 space bytes).
|
||||
var testCBCIV = bytes.Repeat([]byte{0x20}, 16)
|
||||
|
||||
func TestDecryptValue_V10(t *testing.T) {
|
||||
plaintext := []byte("test_secret_value")
|
||||
encrypted, err := crypto.AES128CBCEncrypt(testAESKey, testCBCIV, plaintext)
|
||||
testCBCIV := bytes.Repeat([]byte{0x20}, 16)
|
||||
cbcEncrypted, err := crypto.AESCBCEncrypt(testAESKey, testCBCIV, plaintext)
|
||||
require.NoError(t, err)
|
||||
v10Ciphertext := append([]byte("v10"), encrypted...)
|
||||
v10Ciphertext := append([]byte("v10"), cbcEncrypted...)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -35,7 +33,7 @@ func TestDecryptValue_V10(t *testing.T) {
|
||||
{
|
||||
name: "wrong key returns padding error",
|
||||
key: []byte("wrong_key_1234!!"),
|
||||
wantErrMsg: "pkcs5UnPadding",
|
||||
wantErrMsg: "invalid PKCS5 padding",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
// encryptWithDPAPI encrypts data using Windows DPAPI (CryptProtectData).
|
||||
// This is the reverse of crypto.DecryptWithDPAPI, used only for testing.
|
||||
// This is the reverse of DecryptDPAPI, used only for testing.
|
||||
func encryptWithDPAPI(plaintext []byte) ([]byte, error) {
|
||||
crypt32 := syscall.NewLazyDLL("Crypt32.dll")
|
||||
kernel32 := syscall.NewLazyDLL("Kernel32.dll")
|
||||
@@ -57,11 +57,11 @@ func TestDecryptValue_V10_Windows(t *testing.T) {
|
||||
plaintext := []byte("test_secret_value")
|
||||
nonce := []byte("123456789012") // 12-byte nonce
|
||||
|
||||
encrypted, err := crypto.AESGCMEncrypt(testAESKey, nonce, plaintext)
|
||||
gcmEncrypted, err := crypto.AESGCMEncrypt(testAESKey, nonce, plaintext)
|
||||
require.NoError(t, err)
|
||||
|
||||
// v10 format on Windows: "v10" + nonce(12) + encrypted
|
||||
ciphertext := append([]byte("v10"), append(nonce, encrypted...)...)
|
||||
ciphertext := append([]byte("v10"), append(nonce, gcmEncrypted...)...)
|
||||
|
||||
got, err := decryptValue(testAESKey, ciphertext)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/moond4rk/hackbrowserdata/types"
|
||||
"github.com/moond4rk/hackbrowserdata/utils/typeutil"
|
||||
)
|
||||
|
||||
func extractBookmarks(path string) ([]types.BookmarkEntry, error) {
|
||||
@@ -39,7 +38,7 @@ func walkBookmarks(node gjson.Result, folder string, out *[]types.BookmarkEntry)
|
||||
Type: nodeType,
|
||||
URL: node.Get("url").String(),
|
||||
Folder: folder,
|
||||
CreatedAt: typeutil.TimeEpoch(node.Get("date_added").Int()),
|
||||
CreatedAt: timeEpoch(node.Get("date_added").Int()),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/moond4rk/hackbrowserdata/log"
|
||||
"github.com/moond4rk/hackbrowserdata/types"
|
||||
"github.com/moond4rk/hackbrowserdata/utils/sqliteutil"
|
||||
"github.com/moond4rk/hackbrowserdata/utils/typeutil"
|
||||
)
|
||||
|
||||
const defaultCookieQuery = `SELECT name, encrypted_value, host_key, path,
|
||||
@@ -46,8 +45,8 @@ func extractCookies(masterKey []byte, path string) ([]types.CookieEntry, error)
|
||||
IsHTTPOnly: isHTTPOnly != 0,
|
||||
HasExpire: hasExpire != 0,
|
||||
IsPersistent: isPersistent != 0,
|
||||
ExpireAt: typeutil.TimeEpoch(expireAt),
|
||||
CreatedAt: typeutil.TimeEpoch(createdAt),
|
||||
ExpireAt: timeEpoch(expireAt),
|
||||
CreatedAt: timeEpoch(createdAt),
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -14,9 +14,9 @@ const defaultCreditCardQuery = `SELECT COALESCE(guid, ''), name_on_card, expirat
|
||||
func extractCreditCards(masterKey []byte, path string) ([]types.CreditCardEntry, error) {
|
||||
return sqliteutil.QueryRows(path, false, defaultCreditCardQuery,
|
||||
func(rows *sql.Rows) (types.CreditCardEntry, error) {
|
||||
var guid, name, month, year, nickName, address string
|
||||
var guid, name, month, year, nickname, address string
|
||||
var encNumber []byte
|
||||
if err := rows.Scan(&guid, &name, &month, &year, &encNumber, &nickName, &address); err != nil {
|
||||
if err := rows.Scan(&guid, &name, &month, &year, &encNumber, &nickname, &address); err != nil {
|
||||
return types.CreditCardEntry{}, err
|
||||
}
|
||||
number, err := decryptValue(masterKey, encNumber)
|
||||
@@ -29,7 +29,7 @@ func extractCreditCards(masterKey []byte, path string) ([]types.CreditCardEntry,
|
||||
Number: string(number),
|
||||
ExpMonth: month,
|
||||
ExpYear: year,
|
||||
NickName: nickName,
|
||||
NickName: nickname,
|
||||
Address: address,
|
||||
}, nil
|
||||
})
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/moond4rk/hackbrowserdata/types"
|
||||
"github.com/moond4rk/hackbrowserdata/utils/sqliteutil"
|
||||
"github.com/moond4rk/hackbrowserdata/utils/typeutil"
|
||||
)
|
||||
|
||||
const defaultDownloadQuery = `SELECT target_path, tab_url, total_bytes, start_time, end_time,
|
||||
@@ -25,8 +24,8 @@ func extractDownloads(path string) ([]types.DownloadEntry, error) {
|
||||
TargetPath: targetPath,
|
||||
MimeType: mimeType,
|
||||
TotalBytes: totalBytes,
|
||||
StartTime: typeutil.TimeEpoch(startTime),
|
||||
EndTime: typeutil.TimeEpoch(endTime),
|
||||
StartTime: timeEpoch(startTime),
|
||||
EndTime: timeEpoch(endTime),
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/moond4rk/hackbrowserdata/types"
|
||||
"github.com/moond4rk/hackbrowserdata/utils/sqliteutil"
|
||||
"github.com/moond4rk/hackbrowserdata/utils/typeutil"
|
||||
)
|
||||
|
||||
const defaultHistoryQuery = `SELECT url, title, visit_count, last_visit_time FROM urls`
|
||||
@@ -24,7 +23,7 @@ func extractHistories(path string) ([]types.HistoryEntry, error) {
|
||||
URL: url,
|
||||
Title: title,
|
||||
VisitCount: visitCount,
|
||||
LastVisit: typeutil.TimeEpoch(lastVisit),
|
||||
LastVisit: timeEpoch(lastVisit),
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/moond4rk/hackbrowserdata/log"
|
||||
"github.com/moond4rk/hackbrowserdata/types"
|
||||
"github.com/moond4rk/hackbrowserdata/utils/sqliteutil"
|
||||
"github.com/moond4rk/hackbrowserdata/utils/typeutil"
|
||||
)
|
||||
|
||||
const defaultLoginQuery = `SELECT origin_url, username_value, password_value, date_created FROM logins`
|
||||
@@ -33,7 +32,7 @@ func extractPasswordsWithQuery(masterKey []byte, path, query string) ([]types.Lo
|
||||
URL: url,
|
||||
Username: username,
|
||||
Password: string(password),
|
||||
CreatedAt: typeutil.TimeEpoch(created),
|
||||
CreatedAt: timeEpoch(created),
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -52,7 +52,7 @@ func yandexSources() map[types.Category][]sourcePath {
|
||||
// sourcesForKind returns the source mapping for a browser kind.
|
||||
func sourcesForKind(kind types.BrowserKind) map[types.Category][]sourcePath {
|
||||
switch kind {
|
||||
case types.KindChromiumYandex:
|
||||
case types.ChromiumYandex:
|
||||
return yandexSources()
|
||||
default:
|
||||
return chromiumSources
|
||||
@@ -109,9 +109,9 @@ var operaExtractors = map[types.Category]categoryExtractor{
|
||||
// nil means all categories use the default extractCategory switch logic.
|
||||
func extractorsForKind(kind types.BrowserKind) map[types.Category]categoryExtractor {
|
||||
switch kind {
|
||||
case types.KindChromiumYandex:
|
||||
case types.ChromiumYandex:
|
||||
return yandexExtractors
|
||||
case types.KindChromiumOpera:
|
||||
case types.ChromiumOpera:
|
||||
return operaExtractors
|
||||
default:
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user