mirror of
https://github.com/lukaszraczylo/go-obfuscation.git
synced 2026-10-14 05:48:22 +00:00
295 lines
8.7 KiB
Go
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
|
|
}
|