From 260cad2a3fef18a51d1c4a90d84eb95ad7d92177 Mon Sep 17 00:00:00 2001 From: pagran Date: Sat, 1 Jul 2023 22:10:30 +0200 Subject: [PATCH] add "max" flag value and limits for control flow obfuscation parameters --- docs/CONTROLFLOW.md | 15 +++++++++++---- internal/ctrlflow/ctrlflow.go | 26 +++++++++++++++++++++----- testdata/script/ctrlflow.txtar | 6 ++++++ 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/docs/CONTROLFLOW.md b/docs/CONTROLFLOW.md index 0e89f6b..4dacf63 100644 --- a/docs/CONTROLFLOW.md +++ b/docs/CONTROLFLOW.md @@ -22,6 +22,13 @@ Control flow obfuscation works in several stages: func main() { println("Hello world!") } + + +// Obfuscate with maximum parameters +//garble:controlflow block_splits=max junk_jumps=max flatten_passes=max +func main() { + println("Hello world!") +} ``` ### Parameter explanation @@ -45,8 +52,8 @@ Input: ```go package main -// Note that the block_splits value is very large, so code blocks are split into the smallest possible blocks. -//garble:controlflow flatten_passes=0 junk_jumps=0 block_splits=1024 +// Note that the block_splits value is "max", so code blocks are split into the smallest possible blocks. +//garble:controlflow flatten_passes=0 junk_jumps=0 block_splits=max func main() { println("1") println("2") @@ -91,7 +98,7 @@ _s2a_l4: #### Junk jumps -Parameter: `junk_jumps` (default: `0`) +Parameter: `junk_jumps` (default: `0`, maximum: `1024`) > Warning: this param affects resulting binary only when used in combination with [flattening](#control-flow-flattening) @@ -152,7 +159,7 @@ _s2a_l7: #### Control flow flattening -Parameter: `flatten_passes` (default: `1`) +Parameter: `flatten_passes` (default: `1`, maximum: `4`) This parameter completely [flattens the control flow](https://github.com/obfuscator-llvm/obfuscator/wiki/Control-Flow-Flattening) `flatten_passes` times, which makes analysing the logic of the function very difficult diff --git a/internal/ctrlflow/ctrlflow.go b/internal/ctrlflow/ctrlflow.go index d637545..13440a4 100644 --- a/internal/ctrlflow/ctrlflow.go +++ b/internal/ctrlflow/ctrlflow.go @@ -6,7 +6,9 @@ import ( "go/token" "go/types" "log" + "math" mathrand "math/rand" + "os" "strconv" "strings" @@ -24,19 +26,30 @@ const ( defaultBlockSplits = 0 defaultJunkJumps = 0 defaultFlattenPasses = 1 + + maxBlockSplits = math.MaxInt32 + maxJunkJumps = 256 + maxFlattenPasses = 4 ) type directiveParamMap map[string]string -func (m directiveParamMap) GetInt(name string, def int) int { +func (m directiveParamMap) GetInt(name string, def, max int) int { rawVal, ok := m[name] if !ok { return def } + if rawVal == "max" { + return max + } + val, err := strconv.Atoi(rawVal) if err != nil { - panic(fmt.Errorf("invalid flag %s format: %v", name, err)) + panic(fmt.Errorf("invalid flag %q format: %v", name, err)) + } + if val > max { + panic(fmt.Errorf("too big flag %q value: %d (max: %d)", name, val, max)) } return val } @@ -150,9 +163,12 @@ func Obfuscate(fset *token.FileSet, ssaPkg *ssa.Package, files []*ast.File, obfR for idx, ssaFunc := range ssaFuncs { params := ssaParams[idx] - split := params.GetInt("block_splits", defaultBlockSplits) - junkCount := params.GetInt("junk_jumps", defaultJunkJumps) - passes := params.GetInt("flatten_passes", defaultFlattenPasses) + split := params.GetInt("block_splits", defaultBlockSplits, maxBlockSplits) + junkCount := params.GetInt("junk_jumps", defaultJunkJumps, maxJunkJumps) + passes := params.GetInt("flatten_passes", defaultFlattenPasses, maxFlattenPasses) + if passes == 0 { + fmt.Fprintf(os.Stderr, "control flow obfuscation for %q function has no effect on the resulting binary, to fix this flatten_passes must be greater than zero", ssaFunc) + } applyObfuscation := func(ssaFunc *ssa.Function) { for i := 0; i < split; i++ { diff --git a/testdata/script/ctrlflow.txtar b/testdata/script/ctrlflow.txtar index 8ff3273..1ba5f7c 100644 --- a/testdata/script/ctrlflow.txtar +++ b/testdata/script/ctrlflow.txtar @@ -1,5 +1,8 @@ env GARBLE_EXPERIMENTAL_CONTROLFLOW=1 exec garble -literals -debugdir=debug -seed=0002deadbeef build -o=main$exe + +stderr '"test/main.func1" function has no effect on the resulting binary' + exec ./main cmp stderr main.stderr @@ -34,6 +37,9 @@ import ( "hash/crc32" ) +//garble:controlflow flatten_passes=0 junk_jumps=max block_splits=max +func func1() {} + //garble:controlflow flatten_passes=1 junk_jumps=10 block_splits=10 func main() { // Reference to the unexported interface triggers creation of a new interface