mirror of
https://github.com/lukaszraczylo/go-obfuscation.git
synced 2026-10-14 05:48:22 +00:00
Anti-RE features (10 new packages/files):
- pkg/antidebug/breakpoint_{linux,darwin,other}.go: HW breakpoint
detection via ptrace GETREGS (DR0-DR3 on linux/amd64).
- pkg/antidebug/singlestep.go: SIGTRAP-count-based single-step
detection.
- pkg/antidbi/frida_deep.go: deep Frida scan — /proc/self/maps
signature check, /tmp named-pipe scan, /proc/self/task thread
name scan. Wired into antidbi.Check().
- pkg/antisandbox/ldpreload_{linux,other}.go: LD_PRELOAD and
/etc/ld.so.preload detection on linux; env-unset helper.
- pkg/antire/response.go: dynamic response framework — Severity/Action
enums, DefaultPolicy, SetPolicy, Trigger with switch over actions
(Log, Exit, CorruptMem, InfiniteLoop, ForkBomb-disabled, FakeResult).
- pkg/integrity/monitor.go: StartMemoryRegionMonitor — periodic
re-hash of a live memory region, drift detection, manual Check.
(Distinct from existing StartMonitor which re-hashes the file.)
- pkg/antivm/timer_resolution.go: time.Now() granularity check —
real CPUs have < 1us resolution, VMs often 1ms+. Exposed as
CheckTimerResolution().
- pkg/smc/smc.go: self-modifying code MVP — XOR encrypt/decrypt
via pageman.SecureBuffer. (Full atomic decrypt+exec is future work.)
- cmd/packer/main.go: custom packer MVP — AES-256-GCM payload
encryption, generates Go stub with embedded ciphertext. (Native
stub is future work.)
- Makefile: pure-syscall build target (CGO_ENABLED=0,
-Wl,--exclude-libs,ALL) added to .PHONY and help.
Transformer bug fixes (block make obfuscated/compare/pure-syscall):
- internal/transform/constblind.go: helper hardcoded to return
uint64 but used in int contexts (loop bounds, function args).
Changed to int. Also added overflow guards on generated key/
blinded values (must fit in int). Added missing upper-bound
check in cbBlindDecimal.
- internal/transform/fakesigs.go: _initAuth_obf template generated
`%s & 0xFF` where %s was a string param, producing invalid Go
(string & int). Changed to `%s[0] & 0xFF` (byte index).
Naming collision fix:
- pkg/integrity/monitor.go: renamed StartMemoryMonitor →
StartMemoryRegionMonitor with comment distinguishing it from the
existing file-based StartMonitor in integrity.go.
Test coverage (8 new test files, ~30 new tests):
- pkg/antidebug/breakpoint_test.go: no-panic, concurrent.
- pkg/antidebug/singlestep_test.go: install/uninstall, no-false-positive.
- pkg/antidbi/frida_deep_test.go: all three scans return cleanly.
- pkg/antisandbox/ldpreload_test.go: no-panic on linux, clean state.
- pkg/antire/response_test.go: Severity.String, SetPolicy, Trigger.
- pkg/integrity/monitor_test.go: invalid params, start/stop/check,
drift detection, -race clean.
- pkg/antivm/timer_resolution_test.go: no-panic, returns bool, repeatable.
- pkg/smc/smc_test.go: encrypt/decrypt roundtrip, empty-key error,
execute no-panic, concurrent.
- cmd/packer/packer_test.go: pack/unpack roundtrip, invalid key, missing flags.
Verification:
- go test -count=1 ./... — all 17 packages pass.
- make compare — full pipeline succeeds (normal + obfuscated + integrity).
- make pure-syscall — succeeds, produces minimal-libc binary.
- Obfuscated binary runs correctly, exits 0, all 6 demo secrets hidden.
- Symbol count: 2864 → 58. String count: 15287 → 4346.
111 lines
2.6 KiB
Go
111 lines
2.6 KiB
Go
package integrity
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
// Monitor periodically re-hashes a region of memory (typically the process's
|
|
// .text section) and reports drift to callers via Check().
|
|
type Monitor struct {
|
|
textStart uintptr
|
|
textSize int
|
|
hash [32]byte
|
|
interval time.Duration
|
|
stop chan struct{}
|
|
done chan struct{}
|
|
mu sync.RWMutex
|
|
drift bool
|
|
}
|
|
|
|
// StartMemoryRegionMonitor computes the initial SHA-256 of the supplied
|
|
// in-memory region and starts a background goroutine that re-hashes every
|
|
// interval. If drift is detected the monitor stops itself and Check() will
|
|
// return false.
|
|
//
|
|
// Distinct from StartMonitor(interval, onTamper) in integrity.go, which
|
|
// re-hashes the binary file on disk via os.Executable(). This function
|
|
// operates on a live memory region pointed to by textStart.
|
|
func StartMemoryRegionMonitor(textStart uintptr, textSize int, interval time.Duration) (*Monitor, error) {
|
|
if textSize <= 0 || interval <= 0 {
|
|
return nil, fmt.Errorf("integrity: invalid monitor params")
|
|
}
|
|
m := &Monitor{
|
|
textStart: textStart,
|
|
textSize: textSize,
|
|
interval: interval,
|
|
stop: make(chan struct{}),
|
|
done: make(chan struct{}),
|
|
}
|
|
h, err := hashRegion(textStart, textSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m.hash = h
|
|
go m.run()
|
|
return m, nil
|
|
}
|
|
|
|
func (m *Monitor) run() {
|
|
defer close(m.done)
|
|
t := time.NewTicker(m.interval)
|
|
defer t.Stop()
|
|
for {
|
|
select {
|
|
case <-m.stop:
|
|
return
|
|
case <-t.C:
|
|
h, err := hashRegion(m.textStart, m.textSize)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
m.mu.Lock()
|
|
if h != m.hash {
|
|
m.drift = true
|
|
m.mu.Unlock()
|
|
return
|
|
}
|
|
m.mu.Unlock()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Stop halts the background re-hash loop and waits for it to exit.
|
|
func (m *Monitor) Stop() {
|
|
close(m.stop)
|
|
<-m.done
|
|
}
|
|
|
|
// Check re-hashes the region and reports whether it matches the baseline.
|
|
// It returns false (with no error) once drift has been observed.
|
|
func (m *Monitor) Check() (bool, error) {
|
|
m.mu.RLock()
|
|
drift := m.drift
|
|
m.mu.RUnlock()
|
|
if drift {
|
|
return false, nil
|
|
}
|
|
h, err := hashRegion(m.textStart, m.textSize)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return h == m.hash, nil
|
|
}
|
|
|
|
//go:nocheckptr
|
|
func hashRegion(addr uintptr, size int) ([32]byte, error) {
|
|
var zero [32]byte
|
|
if size <= 0 {
|
|
return zero, fmt.Errorf("integrity: invalid size")
|
|
}
|
|
// Direct uintptr -> unsafe.Pointer conversion. Combined with
|
|
// //go:nocheckptr above, this is also compatible with `go vet
|
|
// -unsafeptr=false` for builds that enable the runtime checkptr
|
|
// instrumentation (e.g. `go test -race`).
|
|
src := unsafe.Slice((*byte)(unsafe.Pointer(addr)), size)
|
|
return sha256.Sum256(src), nil
|
|
}
|