refactor(windows): split Windows code into winapi (#575)

This commit is contained in:
Roger
2026-04-19 18:12:37 +08:00
committed by GitHub
parent 76e2615db2
commit ae1ec66ccb
21 changed files with 876 additions and 456 deletions
-25
View File
@@ -1,25 +0,0 @@
//go:build windows && abe_embed
package crypto
import (
_ "embed"
"fmt"
)
//go:generate make -C ../.. payload
//go:embed abe_extractor_amd64.bin
var abePayloadAmd64 []byte
func ABEPayload(arch string) ([]byte, error) {
switch arch {
case "amd64":
if len(abePayloadAmd64) == 0 {
return nil, fmt.Errorf("abe: amd64 payload is empty (build system bug)")
}
return abePayloadAmd64, nil
default:
return nil, fmt.Errorf("abe: arch %q not supported in this build", arch)
}
}
-12
View File
@@ -1,12 +0,0 @@
//go:build windows && !abe_embed
package crypto
import "fmt"
func ABEPayload(arch string) ([]byte, error) {
return nil, fmt.Errorf(
"abe: payload not embedded in this build (rebuild with -tags abe_embed; arch=%s)",
arch,
)
}
+6 -45
View File
@@ -3,9 +3,7 @@
package crypto
import (
"fmt"
"syscall"
"unsafe"
"github.com/moond4rk/hackbrowserdata/utils/winapi"
)
// gcmNonceSize is defined in crypto.go (cross-platform).
@@ -32,47 +30,10 @@ func DecryptYandex(key, ciphertext []byte) ([]byte, error) {
return AESGCMDecrypt(key, nonce, payload)
}
type dataBlob struct {
cbData uint32
pbData *byte
}
func newBlob(d []byte) *dataBlob {
if len(d) == 0 {
return &dataBlob{}
}
return &dataBlob{
pbData: &d[0],
cbData: uint32(len(d)),
}
}
func (b *dataBlob) bytes() []byte {
d := make([]byte, b.cbData)
copy(d, (*[1 << 30]byte)(unsafe.Pointer(b.pbData))[:])
return d
}
// DecryptDPAPI (Data Protection Application Programming Interface)
// is a simple cryptographic application programming interface
// available as a built-in component in Windows 2000 and
// later versions of Microsoft Windows operating systems
// DecryptDPAPI decrypts a DPAPI-protected blob using the current user's
// master key. The actual Win32 call (and its DATA_BLOB / LocalFree dance)
// lives in utils/winapi so every package that needs a syscall handle
// shares a single declaration instead of re-opening Crypt32.dll per call.
func DecryptDPAPI(ciphertext []byte) ([]byte, error) {
crypt32 := syscall.NewLazyDLL("Crypt32.dll")
kernel32 := syscall.NewLazyDLL("Kernel32.dll")
unprotectDataProc := crypt32.NewProc("CryptUnprotectData")
localFreeProc := kernel32.NewProc("LocalFree")
var outBlob dataBlob
r, _, err := unprotectDataProc.Call(
uintptr(unsafe.Pointer(newBlob(ciphertext))),
0, 0, 0, 0, 0,
uintptr(unsafe.Pointer(&outBlob)),
)
if r == 0 {
return nil, fmt.Errorf("CryptUnprotectData failed with error %w", err)
}
defer localFreeProc.Call(uintptr(unsafe.Pointer(outBlob.pbData)))
return outBlob.bytes(), nil
return winapi.DecryptDPAPI(ciphertext)
}
+5 -5
View File
@@ -11,10 +11,10 @@ import (
"github.com/tidwall/gjson"
"github.com/moond4rk/hackbrowserdata/crypto"
"github.com/moond4rk/hackbrowserdata/crypto/windows/payload"
"github.com/moond4rk/hackbrowserdata/log"
"github.com/moond4rk/hackbrowserdata/utils/browserutil"
"github.com/moond4rk/hackbrowserdata/utils/injector"
"github.com/moond4rk/hackbrowserdata/utils/winutil"
)
const envEncKeyB64 = "HBD_ABE_ENC_B64"
@@ -36,12 +36,12 @@ func (r *ABERetriever) RetrieveKey(storage, localStatePath string) ([]byte, erro
return nil, err
}
payload, err := crypto.ABEPayload("amd64")
pl, err := payload.Get("amd64")
if err != nil {
return nil, fmt.Errorf("abe: %w", err)
}
exePath, err := browserutil.ExecutablePath(browserKey)
exePath, err := winutil.ExecutablePath(browserKey)
if err != nil {
return nil, fmt.Errorf("abe: %w", err)
}
@@ -51,7 +51,7 @@ func (r *ABERetriever) RetrieveKey(storage, localStatePath string) ([]byte, erro
}
inj := &injector.Reflective{}
key, err := inj.Inject(exePath, payload, env)
key, err := inj.Inject(exePath, pl, env)
if err != nil {
return nil, fmt.Errorf("abe: inject into %s: %w", exePath, err)
}
+2 -2
View File
@@ -3,7 +3,7 @@ ABE_ARCH ?= amd64
ABE_TARGET ?= x86_64-windows-gnu
ABE_SRC_DIR = crypto/windows/abe_native
ABE_BIN_DIR = crypto
ABE_BIN_DIR = crypto/windows/payload
ABE_BIN = $(ABE_BIN_DIR)/abe_extractor_$(ABE_ARCH).bin
ABE_CFLAGS = -shared -s -O2 \
@@ -38,7 +38,7 @@ payload-verify: $(ABE_BIN)
fi
payload-clean:
rm -f $(ABE_BIN_DIR)/abe_extractor_*.bin
rm -f $(ABE_BIN_DIR)/abe_extractor_*.bin $(ABE_BIN_DIR)/abe_extractor*.lib
# Scratch-layout codegen. The C header bootstrap_layout.h is the single
# source of truth; the Go constants in crypto/windows/abe_native/bootstrap
+32
View File
@@ -0,0 +1,32 @@
//go:build windows && abe_embed
// Package payload holds the compiled reflective-injection ABE payload
// binary and exposes it to the rest of HackBrowserData. The `abe_embed`
// build tag selects between a real //go:embed'd binary (this file) and
// a stub (stub_windows.go) so the default `go build ./...` succeeds on
// machines without the zig toolchain.
package payload
import (
_ "embed"
"fmt"
)
//go:generate make -C ../../.. payload
//go:embed abe_extractor_amd64.bin
var abePayloadAmd64 []byte
// Get returns the embedded ABE payload for the given architecture.
// Only "amd64" is supported today; x86 / ARM64 payloads are future work.
func Get(arch string) ([]byte, error) {
switch arch {
case "amd64":
if len(abePayloadAmd64) == 0 {
return nil, fmt.Errorf("abe: amd64 payload is empty (build system bug)")
}
return abePayloadAmd64, nil
default:
return nil, fmt.Errorf("abe: arch %q not supported in this build", arch)
}
}
+15
View File
@@ -0,0 +1,15 @@
//go:build windows && !abe_embed
package payload
import "fmt"
// Get returns an error in non-release builds so feature code that needs
// the payload fails fast with a clear message. Release builds (built
// with -tags abe_embed) replace this with the //go:embed'd binary.
func Get(arch string) ([]byte, error) {
return nil, fmt.Errorf(
"abe: payload not embedded in this build (rebuild with -tags abe_embed; arch=%s)",
arch,
)
}