mirror of
https://github.com/lukaszraczylo/go-obfuscation.git
synced 2026-10-16 06:07:50 +00:00
Anti-RE depth: 10 new features, transformer bug fixes, Frida integration
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.
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
package antire
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type Severity int
|
||||
|
||||
const (
|
||||
SevInfo Severity = iota
|
||||
SevWarn
|
||||
SevCritical
|
||||
)
|
||||
|
||||
func (s Severity) String() string {
|
||||
switch s {
|
||||
case SevInfo:
|
||||
return "info"
|
||||
case SevWarn:
|
||||
return "warn"
|
||||
case SevCritical:
|
||||
return "critical"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
type Action int
|
||||
|
||||
const (
|
||||
ActNone Action = iota
|
||||
ActLog
|
||||
ActExit
|
||||
ActCorruptMem
|
||||
ActInfiniteLoop
|
||||
ActForkBomb
|
||||
ActFakeResult
|
||||
)
|
||||
|
||||
var DefaultPolicy = map[Severity]Action{
|
||||
SevInfo: ActLog,
|
||||
SevWarn: ActLog,
|
||||
SevCritical: ActExit,
|
||||
}
|
||||
|
||||
var (
|
||||
policyMu atomic.Pointer[map[Severity]Action]
|
||||
)
|
||||
|
||||
func SetPolicy(p map[Severity]Action) {
|
||||
policyMu.Store(&p)
|
||||
}
|
||||
|
||||
func getPolicy() map[Severity]Action {
|
||||
if p := policyMu.Load(); p != nil {
|
||||
return *p
|
||||
}
|
||||
return DefaultPolicy
|
||||
}
|
||||
|
||||
// Trigger records a detection and executes the configured action.
|
||||
func Trigger(check string, sev Severity) {
|
||||
action := getPolicy()[sev]
|
||||
log.Printf("antire: check=%s severity=%s action=%d", check, sev, action)
|
||||
switch action {
|
||||
case ActNone, ActLog, ActFakeResult:
|
||||
case ActExit:
|
||||
os.Exit(1)
|
||||
case ActCorruptMem:
|
||||
var b [4096]byte
|
||||
for i := range b {
|
||||
b[i] = 0
|
||||
}
|
||||
runtime.KeepAlive(b)
|
||||
case ActInfiniteLoop:
|
||||
for {
|
||||
runtime.Gosched()
|
||||
}
|
||||
case ActForkBomb:
|
||||
log.Printf("antire: fork-bomb action requested but disabled in MVP")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package antire
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSeverityString(t *testing.T) {
|
||||
cases := []struct {
|
||||
sev Severity
|
||||
want string
|
||||
}{
|
||||
{SevInfo, "info"},
|
||||
{SevWarn, "warn"},
|
||||
{SevCritical, "critical"},
|
||||
{Severity(99), "unknown"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := c.sev.String(); got != c.want {
|
||||
t.Errorf("Severity(%d).String() = %q, want %q", c.sev, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetPolicyAndTrigger(t *testing.T) {
|
||||
// Install a policy that maps everything to ActNone so Trigger cannot exit
|
||||
// the test runner regardless of the severity we feed it.
|
||||
SetPolicy(map[Severity]Action{
|
||||
SevInfo: ActNone,
|
||||
SevWarn: ActNone,
|
||||
SevCritical: ActNone,
|
||||
})
|
||||
|
||||
for _, sev := range []Severity{SevInfo, SevWarn, SevCritical} {
|
||||
Trigger("test-check", sev)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetPolicyRestoresDefault(t *testing.T) {
|
||||
// Replacing with nil pointer should fall back to DefaultPolicy at lookup
|
||||
// time. Use a custom policy, then clear and verify the default action for
|
||||
// SevCritical is still ActExit (we just don't call Trigger on it).
|
||||
SetPolicy(map[Severity]Action{
|
||||
SevInfo: ActNone,
|
||||
SevWarn: ActLog,
|
||||
SevCritical: ActNone,
|
||||
})
|
||||
|
||||
if got := getPolicy()[SevCritical]; got != ActNone {
|
||||
t.Fatalf("custom policy not in effect: SevCritical = %d", got)
|
||||
}
|
||||
|
||||
// Reset by storing nil; getPolicy should fall back to defaults.
|
||||
policyMu.Store(nil)
|
||||
if got := getPolicy()[SevCritical]; got != ActExit {
|
||||
t.Fatalf("default policy not restored: SevCritical = %d, want ActExit(%d)", got, ActExit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTriggerWithActLogDoesNotExit(t *testing.T) {
|
||||
policyMu.Store(nil) // ensure default policy
|
||||
if getPolicy()[SevInfo] != ActLog {
|
||||
t.Fatalf("default SevInfo action is not ActLog")
|
||||
}
|
||||
// Calling Trigger with SevInfo uses ActLog which is a no-op.
|
||||
Trigger("smoke-test", SevInfo)
|
||||
}
|
||||
Reference in New Issue
Block a user