From 3a9c9aa3d41efa7acc2f76568ea17d3d0a666874 Mon Sep 17 00:00:00 2001 From: pagran Date: Wed, 6 Dec 2023 21:25:24 +0100 Subject: [PATCH] fix shuffle obfuscation compiler optimization In some cases, compiler could optimize the shuffle obfuscator, causing exposing the obfuscated literal. As a fix, added xor encryption of array indexes. --- internal/literals/shuffle.go | 28 ++++++++++++++++++++++++++-- testdata/script/literals.txtar | 4 ++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/internal/literals/shuffle.go b/internal/literals/shuffle.go index 0590e80..fbe41bd 100644 --- a/internal/literals/shuffle.go +++ b/internal/literals/shuffle.go @@ -20,6 +20,22 @@ func (shuffle) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt { key := make([]byte, len(data)) obfRand.Read(key) + const ( + minIdxKeySize = 2 + maxIdxKeySize = 16 + ) + + idxKeySize := minIdxKeySize + if tmp := obfRand.Intn(len(data)); tmp > idxKeySize { + idxKeySize = tmp + } + if idxKeySize > maxIdxKeySize { + idxKeySize = maxIdxKeySize + } + + idxKey := make([]byte, idxKeySize) + obfRand.Read(idxKey) + fullData := make([]byte, len(data)+len(key)) operators := make([]token.Token, len(fullData)) for i := range operators { @@ -39,10 +55,13 @@ func (shuffle) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt { args := []ast.Expr{ast.NewIdent("data")} for i := range data { + keyIdx := obfRand.Intn(idxKeySize) + k := int(idxKey[keyIdx]) + args = append(args, operatorToReversedBinaryExpr( operators[i], - ah.IndexExpr("fullData", ah.IntLit(shuffledIdxs[i])), - ah.IndexExpr("fullData", ah.IntLit(shuffledIdxs[len(data)+i])), + ah.IndexExpr("fullData", &ast.BinaryExpr{X: ah.IntLit(shuffledIdxs[i] ^ k), Op: token.XOR, Y: ah.CallExprByName("int", ah.IndexExpr("idxKey", ah.IntLit(keyIdx)))}), + ah.IndexExpr("fullData", &ast.BinaryExpr{X: ah.IntLit(shuffledIdxs[len(data)+i] ^ k), Op: token.XOR, Y: ah.CallExprByName("int", ah.IndexExpr("idxKey", ah.IntLit(keyIdx)))}), )) } @@ -52,6 +71,11 @@ func (shuffle) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt { Tok: token.DEFINE, Rhs: []ast.Expr{ah.DataToByteSlice(shuffledFullData)}, }, + &ast.AssignStmt{ + Lhs: []ast.Expr{ast.NewIdent("idxKey")}, + Tok: token.DEFINE, + Rhs: []ast.Expr{ah.DataToByteSlice(idxKey)}, + }, &ast.AssignStmt{ Lhs: []ast.Expr{ast.NewIdent("data")}, Tok: token.DEFINE, diff --git a/testdata/script/literals.txtar b/testdata/script/literals.txtar index e84becd..1a14790 100644 --- a/testdata/script/literals.txtar +++ b/testdata/script/literals.txtar @@ -46,9 +46,9 @@ grep '^\s+\w+ := \[\.{3}\](byte|uint16|uint32|uint64)\{[0-9\s,]+\}$' debug1/test # Split obfuscator. Detect decryptKey ^= i * counter grep '^\s+\w+ \^= \w+ \* \w+$' debug1/test/main/extra_literals.go -# XorShuffle obfuscator. Detect data = append(data, x (^|-|+) y...). +# XorShuffle obfuscator. Detect data = append(data, x[? ^ idxKey[?]] (^|-|+) y[? ^ idxKey[?]]...). # Note that the line obfuscator adds an inline comment before the call. -grep '^\s+\w+ = .*\bappend\(\w+,(\s+\w+\[\d+\][\^\-+]\w+\[\d+\],?)+\)$' debug1/test/main/extra_literals.go +grep '^(\s+)?\w+ = .*\bappend\(\w+,(\s+\w+\[\d+\^\s.+\][\^\-+]\w+\[\d+\^\s.+\],?)+\)$' debug1/test/main/extra_literals.go # XorSeed obfuscator. Detect type decFunc func(byte) decFunc grep '^\s+type \w+ func\(byte\) \w+$' debug1/test/main/extra_literals.go