Skip to content

Commit ae1ec66

Browse files
authored
refactor(windows): split Windows code into winapi (#575)
1 parent 76e2615 commit ae1ec66

21 files changed

Lines changed: 876 additions & 456 deletions

browser/browser_windows_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//go:build windows
2+
3+
package browser
4+
5+
import (
6+
"testing"
7+
8+
"github.com/moond4rk/hackbrowserdata/utils/winutil"
9+
)
10+
11+
// TestWinUtilTableCoversABEBrowsers verifies that every Windows browser
12+
// with ABE support in winutil.Table has a matching Storage key in
13+
// platformBrowsers(). A mismatch means adding a new Chromium fork was
14+
// incomplete: either the BrowserConfig row lacks Storage: "<key>", or
15+
// winutil.Table has a stale entry nobody retrieves keys for.
16+
func TestWinUtilTableCoversABEBrowsers(t *testing.T) {
17+
storages := make(map[string]struct{})
18+
for _, b := range platformBrowsers() {
19+
if b.Storage != "" {
20+
storages[b.Storage] = struct{}{}
21+
}
22+
}
23+
24+
for key, entry := range winutil.Table {
25+
if entry.ABE == winutil.ABENone {
26+
continue
27+
}
28+
if _, ok := storages[key]; !ok {
29+
t.Errorf("winutil.Table[%q] declares ABE support but no BrowserConfig.Storage matches — either fix the table or set Storage: %q in platformBrowsers()", key, key)
30+
}
31+
}
32+
}

crypto/abe_embed_windows.go

Lines changed: 0 additions & 25 deletions
This file was deleted.

crypto/abe_stub_windows.go

Lines changed: 0 additions & 12 deletions
This file was deleted.

crypto/crypto_windows.go

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
package crypto
44

55
import (
6-
"fmt"
7-
"syscall"
8-
"unsafe"
6+
"github.com/moond4rk/hackbrowserdata/utils/winapi"
97
)
108

119
// gcmNonceSize is defined in crypto.go (cross-platform).
@@ -32,47 +30,10 @@ func DecryptYandex(key, ciphertext []byte) ([]byte, error) {
3230
return AESGCMDecrypt(key, nonce, payload)
3331
}
3432

35-
type dataBlob struct {
36-
cbData uint32
37-
pbData *byte
38-
}
39-
40-
func newBlob(d []byte) *dataBlob {
41-
if len(d) == 0 {
42-
return &dataBlob{}
43-
}
44-
return &dataBlob{
45-
pbData: &d[0],
46-
cbData: uint32(len(d)),
47-
}
48-
}
49-
50-
func (b *dataBlob) bytes() []byte {
51-
d := make([]byte, b.cbData)
52-
copy(d, (*[1 << 30]byte)(unsafe.Pointer(b.pbData))[:])
53-
return d
54-
}
55-
56-
// DecryptDPAPI (Data Protection Application Programming Interface)
57-
// is a simple cryptographic application programming interface
58-
// available as a built-in component in Windows 2000 and
59-
// later versions of Microsoft Windows operating systems
33+
// DecryptDPAPI decrypts a DPAPI-protected blob using the current user's
34+
// master key. The actual Win32 call (and its DATA_BLOB / LocalFree dance)
35+
// lives in utils/winapi so every package that needs a syscall handle
36+
// shares a single declaration instead of re-opening Crypt32.dll per call.
6037
func DecryptDPAPI(ciphertext []byte) ([]byte, error) {
61-
crypt32 := syscall.NewLazyDLL("Crypt32.dll")
62-
kernel32 := syscall.NewLazyDLL("Kernel32.dll")
63-
unprotectDataProc := crypt32.NewProc("CryptUnprotectData")
64-
localFreeProc := kernel32.NewProc("LocalFree")
65-
66-
var outBlob dataBlob
67-
r, _, err := unprotectDataProc.Call(
68-
uintptr(unsafe.Pointer(newBlob(ciphertext))),
69-
0, 0, 0, 0, 0,
70-
uintptr(unsafe.Pointer(&outBlob)),
71-
)
72-
if r == 0 {
73-
return nil, fmt.Errorf("CryptUnprotectData failed with error %w", err)
74-
}
75-
76-
defer localFreeProc.Call(uintptr(unsafe.Pointer(outBlob.pbData)))
77-
return outBlob.bytes(), nil
38+
return winapi.DecryptDPAPI(ciphertext)
7839
}

crypto/keyretriever/abe_windows.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import (
1111

1212
"github.com/tidwall/gjson"
1313

14-
"github.com/moond4rk/hackbrowserdata/crypto"
14+
"github.com/moond4rk/hackbrowserdata/crypto/windows/payload"
1515
"github.com/moond4rk/hackbrowserdata/log"
16-
"github.com/moond4rk/hackbrowserdata/utils/browserutil"
1716
"github.com/moond4rk/hackbrowserdata/utils/injector"
17+
"github.com/moond4rk/hackbrowserdata/utils/winutil"
1818
)
1919

2020
const envEncKeyB64 = "HBD_ABE_ENC_B64"
@@ -36,12 +36,12 @@ func (r *ABERetriever) RetrieveKey(storage, localStatePath string) ([]byte, erro
3636
return nil, err
3737
}
3838

39-
payload, err := crypto.ABEPayload("amd64")
39+
pl, err := payload.Get("amd64")
4040
if err != nil {
4141
return nil, fmt.Errorf("abe: %w", err)
4242
}
4343

44-
exePath, err := browserutil.ExecutablePath(browserKey)
44+
exePath, err := winutil.ExecutablePath(browserKey)
4545
if err != nil {
4646
return nil, fmt.Errorf("abe: %w", err)
4747
}
@@ -51,7 +51,7 @@ func (r *ABERetriever) RetrieveKey(storage, localStatePath string) ([]byte, erro
5151
}
5252

5353
inj := &injector.Reflective{}
54-
key, err := inj.Inject(exePath, payload, env)
54+
key, err := inj.Inject(exePath, pl, env)
5555
if err != nil {
5656
return nil, fmt.Errorf("abe: inject into %s: %w", exePath, err)
5757
}

crypto/windows/abe_native/Makefile.frag

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ ABE_ARCH ?= amd64
33
ABE_TARGET ?= x86_64-windows-gnu
44

55
ABE_SRC_DIR = crypto/windows/abe_native
6-
ABE_BIN_DIR = crypto
6+
ABE_BIN_DIR = crypto/windows/payload
77
ABE_BIN = $(ABE_BIN_DIR)/abe_extractor_$(ABE_ARCH).bin
88

99
ABE_CFLAGS = -shared -s -O2 \
@@ -38,7 +38,7 @@ payload-verify: $(ABE_BIN)
3838
fi
3939

4040
payload-clean:
41-
rm -f $(ABE_BIN_DIR)/abe_extractor_*.bin
41+
rm -f $(ABE_BIN_DIR)/abe_extractor_*.bin $(ABE_BIN_DIR)/abe_extractor*.lib
4242

4343
# Scratch-layout codegen. The C header bootstrap_layout.h is the single
4444
# source of truth; the Go constants in crypto/windows/abe_native/bootstrap
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//go:build windows && abe_embed
2+
3+
// Package payload holds the compiled reflective-injection ABE payload
4+
// binary and exposes it to the rest of HackBrowserData. The `abe_embed`
5+
// build tag selects between a real //go:embed'd binary (this file) and
6+
// a stub (stub_windows.go) so the default `go build ./...` succeeds on
7+
// machines without the zig toolchain.
8+
package payload
9+
10+
import (
11+
_ "embed"
12+
"fmt"
13+
)
14+
15+
//go:generate make -C ../../.. payload
16+
17+
//go:embed abe_extractor_amd64.bin
18+
var abePayloadAmd64 []byte
19+
20+
// Get returns the embedded ABE payload for the given architecture.
21+
// Only "amd64" is supported today; x86 / ARM64 payloads are future work.
22+
func Get(arch string) ([]byte, error) {
23+
switch arch {
24+
case "amd64":
25+
if len(abePayloadAmd64) == 0 {
26+
return nil, fmt.Errorf("abe: amd64 payload is empty (build system bug)")
27+
}
28+
return abePayloadAmd64, nil
29+
default:
30+
return nil, fmt.Errorf("abe: arch %q not supported in this build", arch)
31+
}
32+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//go:build windows && !abe_embed
2+
3+
package payload
4+
5+
import "fmt"
6+
7+
// Get returns an error in non-release builds so feature code that needs
8+
// the payload fails fast with a clear message. Release builds (built
9+
// with -tags abe_embed) replace this with the //go:embed'd binary.
10+
func Get(arch string) ([]byte, error) {
11+
return nil, fmt.Errorf(
12+
"abe: payload not embedded in this build (rebuild with -tags abe_embed; arch=%s)",
13+
arch,
14+
)
15+
}

0 commit comments

Comments
 (0)