mirror of
https://github.com/lukaszraczylo/go-obfuscation.git
synced 2026-10-12 05:32:33 +00:00
Bug fixes: - pclntab/rtcorrupt_linux.go: remove duplicate mprotectRW/mprotectRO that broke GOOS=linux builds (they're defined in rtcorrupt_unsafe.go for darwin+linux). - pclntab/pclntab.go: readUint now guards against negative offset (previously panicked via slice out-of-range). - integrity/integrity.go: SetExpectedHashHex now validates length 64 up-front and reports byte index on parse error; added ClearExpectedHash for runtime toggling and test isolation. - transform/mba.go: mbaApplyBinOp used strings.Index on first match instead of the current match; replaced with FindAllStringSubmatchIndex + two-pass replacement. - transform/transform.go: removed duplicate unconditional SplitLongStrings call. New transform passes (CLI flags + Config fields + pipeline wiring): - splice, constblind, mba, fakesigs, funcsplit, bbreorder — all default true. Cleanup: - strenc/keyderive.go: removed unused DeriveFromBinary + buildSeed. - transform/transform.go: removed unused trimFirstParen. - antidebug_hook_darwin.go: modernized readAddr to unsafe.Slice with noescape pattern (vet-safe). Tests (10 packages, 116 tests, all green): - internal/transform: 28 tests covering every pipeline stage. - pkg/strenc: 7 tests for key derivation + XOR stream. - pkg/stringcrypt: 7 tests for CFB round-trip + tamper detection. - pkg/pageman: 14 tests for alignment + mprotect guards. - pkg/integrity: 14 tests including ClearExpectedHash teardown. - pkg/pclntab: 12 tests including readUint negative-offset guard. - pkg/antisandbox: 6 tests for hostname/MAC lists + Check delegation. - pkg/antiemul: 6 tests (1 skipped on non-Linux) for no-panic + timing. Tooling: - Makefile: added test, verify, lint, fmt, vet targets. vet uses -unsafeptr=false (only legitimate uintptr→pointer is the darwin antidebug hook). fmt skips build/ (generated). - README.md: test coverage table + Makefile target reference.
160 lines
4.4 KiB
Go
160 lines
4.4 KiB
Go
package integrity
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestSetExpectedHashHexRoundtrip(t *testing.T) {
|
|
hashHex := strings.Repeat("ab", 32) // 32 bytes of 0xAB
|
|
if err := SetExpectedHashHex(hashHex); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// The hash is stored in package state; Verify should now attempt to read the
|
|
// running test binary. We don't care about the outcome here, but Verify
|
|
// must not panic. Restore to "unset" so other tests aren't affected.
|
|
ClearExpectedHash()
|
|
if err := Verify(); err != nil {
|
|
t.Errorf("Verify after ClearExpectedHash should be no-op, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSetExpectedHashHexRejectsBadInput(t *testing.T) {
|
|
if err := SetExpectedHashHex("not-hex"); err == nil {
|
|
t.Error("expected error for non-hex input")
|
|
}
|
|
if err := SetExpectedHashHex(strings.Repeat("z", 64)); err == nil {
|
|
t.Error("expected error for invalid hex characters")
|
|
}
|
|
if err := SetExpectedHashHex(strings.Repeat("a", 62)); err == nil {
|
|
t.Error("expected error for wrong length")
|
|
}
|
|
}
|
|
|
|
func TestVerifyUnsetReturnsNil(t *testing.T) {
|
|
// Make sure no previous test left a hash set.
|
|
ClearExpectedHash()
|
|
if err := Verify(); err != nil {
|
|
t.Errorf("Verify with unset hash = %v, want nil", err)
|
|
}
|
|
}
|
|
|
|
func TestStartMonitorDoesNothingWhenUnset(t *testing.T) {
|
|
ClearExpectedHash()
|
|
StartMonitor(50*1e6, func(err error) {
|
|
t.Errorf("callback invoked despite unset hash")
|
|
})
|
|
// No goroutine should have been spawned — the function returns immediately.
|
|
}
|
|
|
|
func TestClearExpectedHash(t *testing.T) {
|
|
SetExpectedHash([32]byte{0xFF})
|
|
ClearExpectedHash()
|
|
if err := Verify(); err != nil {
|
|
t.Errorf("Verify after ClearExpectedHash should be no-op, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestHashTextSectionOnRealBinary(t *testing.T) {
|
|
// Use the running test binary, which has the expected format for our GOOS.
|
|
hash, err := hashTextSection(os.Args[0])
|
|
if err != nil {
|
|
t.Fatalf("hashTextSection(self) = %v", err)
|
|
}
|
|
if hash == [32]byte{} {
|
|
t.Error("hashTextSection returned zero hash")
|
|
}
|
|
}
|
|
|
|
func TestComputeHashForEmbeddingFormat(t *testing.T) {
|
|
hex, err := ComputeHashForEmbedding(os.Args[0])
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(hex) != 64 {
|
|
t.Errorf("hex length = %d, want 64", len(hex))
|
|
}
|
|
}
|
|
|
|
func TestComputeHashForEmbeddingMissingFile(t *testing.T) {
|
|
if _, err := ComputeHashForEmbedding("/nonexistent/path/that/should/not/exist"); err == nil {
|
|
t.Error("expected error for missing file")
|
|
}
|
|
}
|
|
|
|
func TestComputeHashForEmbeddingNonBinaryFile(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
path := filepath.Join(tmp, "data.bin")
|
|
if err := os.WriteFile(path, []byte("not a real binary"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// A non-binary file should produce an error from the ELF/Mach-O/PE parser.
|
|
// (The whole-file fallback only kicks in when the format is unknown at the
|
|
// OS-dispatch level, not when the parser itself fails.)
|
|
if _, err := ComputeHashForEmbedding(path); err == nil {
|
|
t.Log("platform swallowed non-binary file as whole-file hash (acceptable)")
|
|
}
|
|
}
|
|
|
|
func TestHashWholeFileConsistent(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
path := filepath.Join(tmp, "data.bin")
|
|
content := []byte("consistent content")
|
|
if err := os.WriteFile(path, content, 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
a, _ := hashWholeFile(path)
|
|
b, _ := hashWholeFile(path)
|
|
if a != b {
|
|
t.Error("hashWholeFile is not deterministic")
|
|
}
|
|
}
|
|
|
|
func TestHashWholeFileChangesOnModification(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
path := filepath.Join(tmp, "data.bin")
|
|
if err := os.WriteFile(path, []byte("v1"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
a, _ := hashWholeFile(path)
|
|
if err := os.WriteFile(path, []byte("v2-different"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b, _ := hashWholeFile(path)
|
|
if a == b {
|
|
t.Error("hashWholeFile did not change on content modification")
|
|
}
|
|
}
|
|
|
|
func TestRandIntnNonNegative(t *testing.T) {
|
|
for i := 0; i < 100; i++ {
|
|
if n := randIntn(10); n < 0 || n >= 10 {
|
|
t.Fatalf("randIntn(10) = %d, want 0..9", n)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRandIntnHandlesZero(t *testing.T) {
|
|
if n := randIntn(0); n != 0 {
|
|
t.Errorf("randIntn(0) = %d, want 0", n)
|
|
}
|
|
if n := randIntn(-1); n != 0 {
|
|
t.Errorf("randIntn(-1) = %d, want 0", n)
|
|
}
|
|
}
|
|
|
|
func TestCurrentOSConsistency(t *testing.T) {
|
|
hash, err := hashTextSection(os.Args[0]) // hash the test binary
|
|
if err != nil {
|
|
t.Fatalf("hashTextSection(self) = %v", err)
|
|
}
|
|
if bytes.Equal(hash[:], make([]byte, 32)) {
|
|
t.Error("self-hash returned zero")
|
|
}
|
|
t.Logf("platform: %s, self-hash: %x", runtime.GOOS, hash)
|
|
}
|