Files
go-obfuscation/internal/transform/fakesigs.go
T
2026-05-31 19:48:56 +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 & 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
}