Files
go-obfuscation/internal/transform/fakesigs.go
T
lukaszraczylo 0cbba3a8ce 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.
2026-06-02 00:43:08 +01:00

295 lines
8.7 KiB
Go

// Fake function signature injection.
//
// Injects realistic-looking but dead function definitions into the source
// file. These functions are never called and serve to confuse disassemblers
// by polluting the symbol table with plausible function boundaries, making
// it harder to identify real functions and their purposes.
package transform
import (
"fmt"
"math/rand"
"strings"
"github.com/lukaszraczylo/go-obfuscation/pkg/stringcrypt"
)
type fakeFuncTemplate struct {
namePattern string
params string
returnType string
bodyGen func(rng *rand.Rand, argName string) string
}
var fakeFuncTemplates = []fakeFuncTemplate{
{
namePattern: "_initAuth_obf",
params: "token string",
returnType: "bool",
bodyGen: func(rng *rand.Rand, arg string) string {
v1 := stringcrypt.RandomIdent("_v", 3)
v2 := stringcrypt.RandomIdent("_v", 3)
return fmt.Sprintf("\t_ = %s\n\t%s := len(%s)\n\t%s := %s[0] & 0xFF\n\treturn %s > 100 && %s < 0",
arg, v1, arg, v2, arg, v1, v2)
},
},
{
namePattern: "_parseConfig_obf",
params: "data []byte",
returnType: "int",
bodyGen: func(rng *rand.Rand, arg string) string {
v1 := stringcrypt.RandomIdent("_c", 3)
v2 := stringcrypt.RandomIdent("_c", 3)
return fmt.Sprintf("\t_ = %s\n\t%s := len(%s)\n\t%s := %s * 3\n\t_ = %s\n\treturn %s - %s",
arg, v1, arg, v2, v1, v2, v1, v2)
},
},
{
namePattern: "_validateKey_obf",
params: "key string, size int",
returnType: "bool",
bodyGen: func(rng *rand.Rand, arg string) string {
v1 := stringcrypt.RandomIdent("_k", 3)
v2 := stringcrypt.RandomIdent("_k", 3)
return fmt.Sprintf("\t%s := len(key)\n\t%s := size ^ 0xDEAD\n\t_ = %s\n\treturn %s == %s && %s != %s",
v1, v2, v2, v1, v2, v1, v2)
},
},
{
namePattern: "_hashData_obf",
params: "input []byte",
returnType: "[32]byte",
bodyGen: func(rng *rand.Rand, arg string) string {
v1 := stringcrypt.RandomIdent("_h", 3)
return fmt.Sprintf("\tvar %s [32]byte\n\t_ = %s\n\tfor _i := range %s {\n\t\t%s[_i] = byte(_i)\n\t}\n\treturn %s",
v1, arg, arg, v1, v1)
},
},
{
namePattern: "_encodePayload_obf",
params: "buf []byte, offset int",
returnType: "int",
bodyGen: func(rng *rand.Rand, arg string) string {
v1 := stringcrypt.RandomIdent("_e", 3)
return fmt.Sprintf("\t_ = buf\n\t_ = offset\n\t%s := len(buf) + offset\n\treturn %s ^ %s",
v1, v1, v1)
},
},
{
namePattern: "_checkPerms_obf",
params: "uid int, gid int",
returnType: "bool",
bodyGen: func(rng *rand.Rand, arg string) string {
v1 := stringcrypt.RandomIdent("_p", 3)
return fmt.Sprintf("\t%s := uid ^ gid\n\treturn %s > 0 && %s < 0 && uid == gid && uid != gid",
v1, v1, v1)
},
},
{
namePattern: "_readStream_obf",
params: "path string",
returnType: "[]byte",
bodyGen: func(rng *rand.Rand, arg string) string {
v1 := stringcrypt.RandomIdent("_r", 3)
v2 := stringcrypt.RandomIdent("_r", 3)
return fmt.Sprintf("\t_ = %s\n\t%s := len(%s)\n\t%s := make([]byte, %s)\n\tfor _i := range %s {\n\t\t%s[_i] = 0\n\t}\n\treturn %s",
arg, v1, arg, v2, v1, v2, v2, v2)
},
},
{
namePattern: "_formatLog_obf",
params: "level int, msg string",
returnType: "string",
bodyGen: func(rng *rand.Rand, arg string) string {
v1 := stringcrypt.RandomIdent("_l", 3)
return fmt.Sprintf("\t_ = level\n\t%s := len(msg)\n\tif %s > 1000000 {\n\t\treturn \"\"\n\t}\n\treturn msg[:%s]",
v1, v1, v1)
},
},
}
// InjectFakeSignatures inserts 2-5 fake function definitions per file
// that look realistic but are never called.
func InjectFakeSignatures(src string) string {
rng := rand.New(rand.NewSource(rand.Int63()))
numFake := rng.Intn(4) + 2 // 2-5 fake functions
var fakeFuncs strings.Builder
// Select random templates without replacement
perm := rng.Perm(len(fakeFuncTemplates))
for i := 0; i < numFake && i < len(fakeFuncTemplates); i++ {
tmpl := fakeFuncTemplates[perm[i]]
suffix := fmt.Sprintf("%04X", rng.Intn(0xFFFF))
funcName := tmpl.namePattern + suffix
argName := extractFirstArgName(tmpl.params)
fakeFuncs.WriteString("\n//go:noinline\n")
fakeFuncs.WriteString(fmt.Sprintf("func %s(%s) %s {\n", funcName, tmpl.params, tmpl.returnType))
fakeFuncs.WriteString(tmpl.bodyGen(rng, argName))
fakeFuncs.WriteString("\n}\n")
}
// Inject at package level, after imports
insertAt := findImportBlockEnd(src)
if insertAt == -1 {
pkgIdx := strings.Index(src, "package ")
if pkgIdx != -1 {
lineEnd := strings.Index(src[pkgIdx:], "\n")
if lineEnd != -1 {
insertAt = pkgIdx + lineEnd + 1
}
}
}
if insertAt != -1 && insertAt < len(src) {
src = src[:insertAt] + fakeFuncs.String() + src[insertAt:]
}
return src
}
func extractFirstArgName(params string) string {
params = strings.TrimSpace(params)
if params == "" {
return "_"
}
parts := strings.SplitN(params, ",", 2)
first := strings.TrimSpace(parts[0])
fields := strings.Fields(first)
if len(fields) >= 1 {
return fields[0]
}
return "_"
}
// InjectMoreFakeSignatures generates additional fake functions with
// random realistic names and bodies for deeper obfuscation.
func InjectMoreFakeSignatures(src string, count int) string {
rng := rand.New(rand.NewSource(rand.Int63()))
prefixes := []string{
"_resolveAddr", "_buildRequest", "_handleResp", "_writeLog",
"_parseHeader", "_checkAuth", "_compressData", "_decompressBuf",
"_verifySig", "_loadCache", "_flushBuffer", "_retryConn",
"_closeSocket", "_allocMem", "_releaseLock", "_acquireLock",
"_serializeMsg", "_deserializeMsg", "_encodeURL", "_decodeURL",
}
paramSets := []struct {
params string
returnType string
}{
{"addr string, port int", "error"},
{"data []byte", "int"},
{"key string", "bool"},
{"src, dst string", "string"},
{"n int", "int64"},
{"buf []byte, offset int", "[]byte"},
{"flag bool, name string", "string"},
{"x, y int64", "int64"},
}
var fakeFuncs strings.Builder
for i := 0; i < count; i++ {
prefix := prefixes[rng.Intn(len(prefixes))]
suffix := fmt.Sprintf("%04X", rng.Intn(0xFFFF))
funcName := prefix + "_obf" + suffix
pset := paramSets[rng.Intn(len(paramSets))]
argName := extractFirstArgName(pset.params)
body := generateRandomBody(rng, argName, pset.returnType)
fakeFuncs.WriteString("\n//go:noinline\n")
fakeFuncs.WriteString(fmt.Sprintf("func %s(%s) %s {\n", funcName, pset.params, pset.returnType))
fakeFuncs.WriteString(body)
fakeFuncs.WriteString("\n}\n")
}
insertAt := findImportBlockEnd(src)
if insertAt == -1 {
pkgIdx := strings.Index(src, "package ")
if pkgIdx != -1 {
lineEnd := strings.Index(src[pkgIdx:], "\n")
if lineEnd != -1 {
insertAt = pkgIdx + lineEnd + 1
}
}
}
if insertAt != -1 && insertAt < len(src) {
src = src[:insertAt] + fakeFuncs.String() + src[insertAt:]
}
return src
}
// generateRandomBody creates a plausible-looking function body that computes
// something meaningless but looks legitimate.
func generateRandomBody(rng *rand.Rand, argName, returnType string) string {
var sb strings.Builder
numStmts := rng.Intn(6) + 3 // 3-8 statements
vars := make([]string, numStmts)
for i := range vars {
vars[i] = stringcrypt.RandomIdent("_x", 3)
}
// Always start with consuming the argument
sb.WriteString(fmt.Sprintf("\t_ = %s\n", argName))
for i := 0; i < numStmts; i++ {
switch rng.Intn(5) {
case 0:
// Assignment from arithmetic
val := rng.Intn(0xFFFF) + 1
sb.WriteString(fmt.Sprintf("\t%s := %d ^ 0x%X\n", vars[i], val, rng.Intn(0xFFFF)))
case 1:
// Boolean expression
sb.WriteString(fmt.Sprintf("\t%s := %d > %d && %d < %d\n",
vars[i], rng.Intn(100), rng.Intn(100)+200, rng.Intn(100), rng.Intn(100)+200))
case 2:
// Self-referencing computation
if i > 0 {
sb.WriteString(fmt.Sprintf("\t%s = %s + %d\n", vars[i-1], vars[i-1], rng.Intn(1000)))
} else {
sb.WriteString(fmt.Sprintf("\t%s := %d\n", vars[i], rng.Intn(0xFFFF)))
}
case 3:
// Dead code branch
sb.WriteString(fmt.Sprintf("\tif false { %s = %s }\n", vars[i], vars[max(0, i-1)]))
case 4:
// Bitwise operation
sb.WriteString(fmt.Sprintf("\t%s := int64(%d) << %d\n", vars[i], rng.Intn(256), rng.Intn(8)))
}
}
// Return something based on the return type
switch returnType {
case "bool":
sb.WriteString(fmt.Sprintf("\treturn %s && !%s\n", vars[0], vars[0]))
case "int", "int64":
sb.WriteString(fmt.Sprintf("\treturn %s - %s\n", vars[0], vars[0]))
case "string":
sb.WriteString("\treturn \"\"\n")
case "[]byte":
sb.WriteString("\treturn nil\n")
case "[32]byte":
sb.WriteString(fmt.Sprintf("\tvar %s [32]byte\n\treturn %s\n", vars[numStmts-1], vars[numStmts-1]))
case "error":
sb.WriteString("\treturn nil\n")
default:
sb.WriteString(fmt.Sprintf("\treturn %s\n", vars[0]))
}
return sb.String()
}
func max(a, b int) int {
if a > b {
return a
}
return b
}