mirror of
https://github.com/lukaszraczylo/go-obfuscation.git
synced 2026-10-14 05:48:22 +00:00
.
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
// Constant blinding transformation.
|
||||
//
|
||||
// Replaces integer literal constants with runtime XOR expressions that
|
||||
// evaluate to the original value, defeating pattern-matching in disassemblers
|
||||
// and static analysis tools. Each constant C is replaced with (K ^ (K ^ C))
|
||||
// where K is a per-constant random key.
|
||||
package transform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/lukaszraczylo/go-obfuscation/pkg/stringcrypt"
|
||||
)
|
||||
|
||||
var (
|
||||
cbHexRe = regexp.MustCompile(`0x[0-9a-fA-F]+`)
|
||||
cbStringRe = regexp.MustCompile(`"[^"\\]*(?:\\.[^"\\]*)*"`)
|
||||
cbBacktickRe = regexp.MustCompile("`[^`]*`")
|
||||
|
||||
cbSkipContexts = []string{
|
||||
"case ", "case\t",
|
||||
"[",
|
||||
"len(", "cap(",
|
||||
"make(",
|
||||
"iota",
|
||||
}
|
||||
)
|
||||
|
||||
func TransformConstants(src string) string {
|
||||
rng := rand.New(rand.NewSource(rand.Int63()))
|
||||
blindHelperName := stringcrypt.RandomIdent("_blind", 4)
|
||||
|
||||
lines := strings.Split(src, "\n")
|
||||
var result []string
|
||||
inFuncBody := false
|
||||
|
||||
for _, line := range lines {
|
||||
trimmed := strings.TrimSpace(line)
|
||||
|
||||
if strings.HasPrefix(trimmed, "func ") && strings.HasSuffix(trimmed, "{") {
|
||||
inFuncBody = true
|
||||
}
|
||||
if inFuncBody && trimmed == "}" {
|
||||
inFuncBody = false
|
||||
}
|
||||
|
||||
if !inFuncBody {
|
||||
result = append(result, line)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(trimmed, "//") || trimmed == "" || trimmed == "{" || trimmed == "}" {
|
||||
result = append(result, line)
|
||||
continue
|
||||
}
|
||||
|
||||
if shouldCBLineSkip(trimmed) {
|
||||
result = append(result, line)
|
||||
continue
|
||||
}
|
||||
|
||||
blinded := cbBlindLine(line, rng, blindHelperName)
|
||||
result = append(result, blinded)
|
||||
}
|
||||
|
||||
resultStr := strings.Join(result, "\n")
|
||||
resultStr = injectBlindHelper(resultStr, blindHelperName)
|
||||
return resultStr
|
||||
}
|
||||
|
||||
func shouldCBLineSkip(trimmed string) bool {
|
||||
if strings.HasPrefix(trimmed, "case ") || strings.HasPrefix(trimmed, "case\t") {
|
||||
return true
|
||||
}
|
||||
if strings.Contains(trimmed, "[") && strings.Contains(trimmed, "]") {
|
||||
return true
|
||||
}
|
||||
for _, ctx := range cbSkipContexts {
|
||||
if strings.Contains(trimmed, ctx) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func cbBlindLine(line string, rng *rand.Rand, helperName string) string {
|
||||
line = cbHexRe.ReplaceAllStringFunc(line, func(match string) string {
|
||||
return cbBlindHex(match, rng, helperName)
|
||||
})
|
||||
|
||||
line = blindDecimalsInLine(line, rng, helperName)
|
||||
return line
|
||||
}
|
||||
|
||||
func blindDecimalsInLine(line string, rng *rand.Rand, helperName string) string {
|
||||
var buf strings.Builder
|
||||
i := 0
|
||||
for i < len(line) {
|
||||
if unicode.IsDigit(rune(line[i])) {
|
||||
start := i
|
||||
for i < len(line) && unicode.IsDigit(rune(line[i])) {
|
||||
i++
|
||||
}
|
||||
numStr := line[start:i]
|
||||
|
||||
if start > 0 && (unicode.IsLetter(rune(line[start-1])) || line[start-1] == '_') {
|
||||
buf.WriteString(numStr)
|
||||
continue
|
||||
}
|
||||
if i < len(line) && (unicode.IsLetter(rune(line[i])) || line[i] == '_' || line[i] == 'x' || line[i] == 'X') {
|
||||
buf.WriteString(numStr)
|
||||
continue
|
||||
}
|
||||
|
||||
replaced := cbBlindDecimal(numStr, rng, helperName)
|
||||
buf.WriteString(replaced)
|
||||
} else {
|
||||
buf.WriteByte(line[i])
|
||||
i++
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func cbBlindHex(match string, rng *rand.Rand, helperName string) string {
|
||||
val, err := strconv.ParseUint(match, 0, 64)
|
||||
if err != nil {
|
||||
return match
|
||||
}
|
||||
if val == 0 || val == 1 {
|
||||
return match
|
||||
}
|
||||
if val > 0x7FFFFFFFFFFFFFFF {
|
||||
return match
|
||||
}
|
||||
|
||||
key := rng.Uint64() | 1
|
||||
blinded := key ^ val
|
||||
return fmt.Sprintf("%s(0x%X, 0x%X)", helperName, key, blinded)
|
||||
}
|
||||
|
||||
func cbBlindDecimal(match string, rng *rand.Rand, helperName string) string {
|
||||
val, err := strconv.ParseUint(match, 10, 64)
|
||||
if err != nil {
|
||||
return match
|
||||
}
|
||||
if val <= 2 || val < 10 {
|
||||
return match
|
||||
}
|
||||
|
||||
key := rng.Uint64() | 1
|
||||
blinded := key ^ val
|
||||
return fmt.Sprintf("%s(0x%X, 0x%X)", helperName, key, blinded)
|
||||
}
|
||||
|
||||
func injectBlindHelper(src string, helperName string) string {
|
||||
helper := fmt.Sprintf(`
|
||||
//go:noinline
|
||||
func %s(a, b uint64) uint64 { return a ^ b }
|
||||
`, helperName)
|
||||
|
||||
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] + helper + src[insertAt:]
|
||||
}
|
||||
return src
|
||||
}
|
||||
Reference in New Issue
Block a user