package builder import ( "crypto/sha256" "encoding/binary" "encoding/hex" "fmt" "hash/fnv" "io" "math/rand" "os" "path/filepath" "strings" "time" ) const sigilOverlayMagic = "AFSC\x01" // ApplySigilScramble mutates the built binary so each dispense has a unique on-disk // signature (overlay entropy + optional PE timestamp). Does not change runtime logic. func ApplySigilScramble(path, buildID string) (fingerprint string, err error) { if path == "" { return "", fmt.Errorf("empty path") } data, err := os.ReadFile(path) if err != nil { return "", err } if len(data) == 0 { return "", fmt.Errorf("empty binary") } seed := strings.TrimSpace(buildID) if seed == "" { seed = fmt.Sprintf("%d", time.Now().UnixNano()) } rng := scrambleRNG(seed) if isPEExecutable(path, data) { data = patchPETimestamp(data, rng) } overlay := buildSigilOverlay(seed, rng) data = append(data, overlay...) if err := os.WriteFile(path, data, 0755); err != nil { return "", err } sum := sha256.Sum256(data) fingerprint = hex.EncodeToString(sum[:8]) return fingerprint, nil } func isPEExecutable(path string, data []byte) bool { if !strings.EqualFold(filepath.Ext(path), ".exe") { return false } return len(data) > 64 && data[0] == 'M' && data[1] == 'Z' } // patchPETimestamp adjusts the COFF header timestamp (bytes 8-11 after MZ). func patchPETimestamp(data []byte, rng *rand.Rand) []byte { out := make([]byte, len(data)) copy(out, data) peOff := int(binary.LittleEndian.Uint32(out[0x3c:0x40])) if peOff < 0 || peOff+8 > len(out) { return out } if string(out[peOff:peOff+4]) != "PE\x00\x00" { return out } ts := uint32(time.Now().Unix()) ^ uint32(rng.Intn(1<<20)) binary.LittleEndian.PutUint32(out[peOff+8:peOff+12], ts) return out } func buildSigilOverlay(seed string, rng *rand.Rand) []byte { padLen := 8192 + rng.Intn(57344) buf := make([]byte, len(sigilOverlayMagic)+len(seed)+2+padLen) copy(buf, sigilOverlayMagic) buf[len(sigilOverlayMagic)] = byte(len(seed) & 0xff) copy(buf[len(sigilOverlayMagic)+1:], []byte(seed)) off := len(sigilOverlayMagic) + 1 + len(seed) for i := 0; i < padLen; i++ { buf[off+i] = byte(rng.Intn(256)) } return buf } func scrambleRNG(seed string) *rand.Rand { h := fnv.New64a() _, _ = io.WriteString(h, seed) return rand.New(rand.NewSource(int64(h.Sum64()))) } // StealthScore estimates how many uniqueness layers were applied (0–100). func StealthScore(obfuscated, scrambled, signed bool) int { score := 35 // polymorph is always injected at compile time if obfuscated { score += 30 } if scrambled { score += 20 } if signed { score += 15 } if score > 100 { return 100 } return score } func shouldSigilScramble(req *BuildRequest) bool { if req.SigilScramble { return true } return false }