From 28adbaa73bb7315e54de99da0d2d78356d2fcc69 Mon Sep 17 00:00:00 2001 From: pagran <67878280+pagran@users.noreply.github.com> Date: Wed, 12 Aug 2020 04:23:43 +0300 Subject: [PATCH] Randomize operator (xor, add, subtract) on all obfuscators (#90) Co-authored-by: lu4p --- internal/literals/obfuscators.go | 42 +++++++++++++++++++++++++++++++ internal/literals/split.go | 43 +++++++++++++++++++------------- internal/literals/swap.go | 38 +++++++++++++++------------- internal/literals/xor.go | 9 +++---- internal/literals/xor_seed.go | 15 ++++------- internal/literals/xor_shuffle.go | 17 ++++++++----- testdata/scripts/literals.txt | 8 +++--- 7 files changed, 111 insertions(+), 61 deletions(-) diff --git a/internal/literals/obfuscators.go b/internal/literals/obfuscators.go index 5dc03f0..ad73809 100644 --- a/internal/literals/obfuscators.go +++ b/internal/literals/obfuscators.go @@ -4,6 +4,7 @@ import ( cryptrand "crypto/rand" "fmt" "go/ast" + "go/token" mathrand "math/rand" "os" "strings" @@ -44,6 +45,12 @@ func genRandBytes(buffer []byte) { } } +func genRandByte() byte { + bytes := make([]byte, 1) + genRandBytes(bytes) + return bytes[0] +} + func genRandIntSlice(max, count int) []int { indexes := make([]int, count) for i := 0; i < count; i++ { @@ -51,3 +58,38 @@ func genRandIntSlice(max, count int) []int { } return indexes } + +func randOperator() token.Token { + operatorTokens := [...]token.Token{token.XOR, token.ADD, token.SUB} + return operatorTokens[mathrand.Intn(len(operatorTokens))] +} + +func evalOperator(t token.Token, x, y byte) byte { + switch t { + case token.XOR: + return x ^ y + case token.ADD: + return x + y + case token.SUB: + return x - y + default: + panic(fmt.Sprintf("unknown operator: %s", t)) + } +} + +func operatorToReversedBinaryExpr(t token.Token, x, y ast.Expr) *ast.BinaryExpr { + expr := &ast.BinaryExpr{X: x, Y: y} + + switch t { + case token.XOR: + expr.Op = token.XOR + case token.ADD: + expr.Op = token.SUB + case token.SUB: + expr.Op = token.ADD + default: + panic(fmt.Sprintf("unknown operator: %s", t)) + } + + return expr +} diff --git a/internal/literals/split.go b/internal/literals/split.go index e6e3a19..1ff99d2 100644 --- a/internal/literals/split.go +++ b/internal/literals/split.go @@ -55,14 +55,13 @@ func shuffleStmts(stmts ...ast.Stmt) []ast.Stmt { return stmts } -// Xor encrypt chunks based on key and position -func encryptChunks(chunks [][]byte, key int) { - idx := 0 - for chunkIdx := range chunks { - chunk := chunks[chunkIdx] - for i := range chunk { - chunk[i] ^= byte(key ^ idx) - idx++ +// Encrypt chunks based on key and position +func encryptChunks(chunks [][]byte, op token.Token, key byte) { + byteOffset := 0 + for _, chunk := range chunks { + for i, b := range chunk { + chunk[i] = evalOperator(op, b, key^byte(byteOffset)) + byteOffset++ } } } @@ -79,13 +78,15 @@ func (x split) obfuscate(data []byte) *ast.BlockStmt { // Generate indexes for cases chunk count + 1 decrypt case + 1 exit case indexes := mathrand.Perm(len(chunks) + 2) - decryptKeyInitial := mathrand.Int() + decryptKeyInitial := genRandByte() decryptKey := decryptKeyInitial // Calculate decrypt key based on indexes and position. Ignore exit index for i, index := range indexes[:len(indexes)-1] { - decryptKey ^= index * i + decryptKey ^= byte(index * i) } - encryptChunks(chunks, decryptKey) + + op := randOperator() + encryptChunks(chunks, op, decryptKey) decryptIndex := indexes[len(indexes)-2] exitIndex := indexes[len(indexes)-1] @@ -103,12 +104,18 @@ func (x split) obfuscate(data []byte) *ast.BlockStmt { X: ah.Ident("data"), Body: ah.BlockStmt(&ast.AssignStmt{ Lhs: []ast.Expr{ah.IndexExpr("data", ah.Ident("y"))}, - Tok: token.XOR_ASSIGN, - Rhs: []ast.Expr{ah.CallExpr(ah.Ident("byte"), &ast.BinaryExpr{ - X: ah.Ident("decryptKey"), - Op: token.XOR, - Y: ah.Ident("y"), - })}, + Tok: token.ASSIGN, + Rhs: []ast.Expr{ + operatorToReversedBinaryExpr( + op, + ah.IndexExpr("data", ah.Ident("y")), + ah.CallExpr(ah.Ident("byte"), &ast.BinaryExpr{ + X: ah.Ident("decryptKey"), + Op: token.XOR, + Y: ah.Ident("y"), + }), + ), + }, }), }, ), @@ -165,7 +172,7 @@ func (x split) obfuscate(data []byte) *ast.BlockStmt { &ast.AssignStmt{ Lhs: []ast.Expr{ah.Ident("decryptKey")}, Tok: token.DEFINE, - Rhs: []ast.Expr{ah.IntLit(decryptKeyInitial)}, + Rhs: []ast.Expr{ah.IntLit(int(decryptKeyInitial))}, }, &ast.ForStmt{ Init: &ast.AssignStmt{ diff --git a/internal/literals/swap.go b/internal/literals/swap.go index 44869cd..91b585c 100644 --- a/internal/literals/swap.go +++ b/internal/literals/swap.go @@ -57,14 +57,16 @@ func generateSwapCount(dataLen int) int { func (x swap) obfuscate(data []byte) *ast.BlockStmt { swapCount := generateSwapCount(len(data)) - shiftKey := byte(mathrand.Intn(math.MaxUint8)) + shiftKey := genRandByte() + + op := randOperator() positions := genRandIntSlice(len(data), swapCount) for i := len(positions) - 2; i >= 0; i -= 2 { // Generate local key for xor based on random key and byte position localKey := byte(i) + byte(positions[i]^positions[i+1]) + shiftKey - // Swap bytes from i+1 to i and xor using local key - data[positions[i]], data[positions[i+1]] = data[positions[i+1]]^localKey, data[positions[i]]^localKey + // Swap bytes from i+1 to i and encrypt using operator and local key + data[positions[i]], data[positions[i+1]] = evalOperator(op, data[positions[i+1]], localKey), evalOperator(op, data[positions[i]], localKey) } return ah.BlockStmt( @@ -127,20 +129,22 @@ func (x swap) obfuscate(data []byte) *ast.BlockStmt { }, Tok: token.ASSIGN, Rhs: []ast.Expr{ - &ast.BinaryExpr{ - X: ah.IndexExpr("data", ah.IndexExpr("positions", &ast.BinaryExpr{ - X: ah.Ident("i"), - Op: token.ADD, - Y: ah.IntLit(1), - })), - Op: token.XOR, - Y: ah.Ident("localKey"), - }, - &ast.BinaryExpr{ - X: ah.IndexExpr("data", ah.IndexExpr("positions", ah.Ident("i"))), - Op: token.XOR, - Y: ah.Ident("localKey"), - }, + operatorToReversedBinaryExpr( + op, + ah.IndexExpr("data", + ah.IndexExpr("positions", &ast.BinaryExpr{ + X: ah.Ident("i"), + Op: token.ADD, + Y: ah.IntLit(1), + }), + ), + ah.Ident("localKey"), + ), + operatorToReversedBinaryExpr( + op, + ah.IndexExpr("data", ah.IndexExpr("positions", ah.Ident("i"))), + ah.Ident("localKey"), + ), }, }, ), diff --git a/internal/literals/xor.go b/internal/literals/xor.go index 793dd73..092728f 100644 --- a/internal/literals/xor.go +++ b/internal/literals/xor.go @@ -16,8 +16,9 @@ func (x xor) obfuscate(data []byte) *ast.BlockStmt { key := make([]byte, len(data)) genRandBytes(key) + op := randOperator() for i, b := range key { - data[i] = data[i] ^ b + data[i] = evalOperator(op, data[i], b) } return &ast.BlockStmt{List: []ast.Stmt{ @@ -40,11 +41,7 @@ func (x xor) obfuscate(data []byte) *ast.BlockStmt { &ast.AssignStmt{ Lhs: []ast.Expr{ah.IndexExpr("data", ah.Ident("i"))}, Tok: token.ASSIGN, - Rhs: []ast.Expr{&ast.BinaryExpr{ - X: ah.IndexExpr("data", ah.Ident("i")), - Op: token.XOR, - Y: ah.Ident("b"), - }}, + Rhs: []ast.Expr{operatorToReversedBinaryExpr(op, ah.IndexExpr("data", ah.Ident("i")), ah.Ident("b"))}, }, }}, }, diff --git a/internal/literals/xor_seed.go b/internal/literals/xor_seed.go index 8812ccb..1c97a49 100644 --- a/internal/literals/xor_seed.go +++ b/internal/literals/xor_seed.go @@ -13,15 +13,14 @@ type xorSeed struct{} var _ obfuscator = xorSeed{} func (x xorSeed) obfuscate(data []byte) *ast.BlockStmt { - preSeed := make([]byte, 1) - genRandBytes(preSeed) - - seed := preSeed[0] + seed := genRandByte() originalSeed := seed + op := randOperator() + var callExpr *ast.CallExpr for i, b := range data { - encB := b ^ seed + encB := evalOperator(op, b, seed) seed += encB if i == 0 { @@ -95,11 +94,7 @@ func (x xorSeed) obfuscate(data []byte) *ast.BlockStmt { Lhs: []ast.Expr{ah.Ident("data")}, Tok: token.ASSIGN, Rhs: []ast.Expr{ - ah.CallExpr(ah.Ident("append"), ah.Ident("data"), &ast.BinaryExpr{ - X: ah.Ident("x"), - Op: token.XOR, - Y: ah.Ident("seed"), - }), + ah.CallExpr(ah.Ident("append"), ah.Ident("data"), operatorToReversedBinaryExpr(op, ah.Ident("x"), ah.Ident("seed"))), }, }, &ast.AssignStmt{ diff --git a/internal/literals/xor_shuffle.go b/internal/literals/xor_shuffle.go index 1a8741a..28f7a60 100644 --- a/internal/literals/xor_shuffle.go +++ b/internal/literals/xor_shuffle.go @@ -18,8 +18,13 @@ func (x xorShuffle) obfuscate(data []byte) *ast.BlockStmt { genRandBytes(key) fullData := make([]byte, len(data)+len(key)) + operators := make([]token.Token, len(fullData)) + for i := range operators { + operators[i] = randOperator() + } + for i, b := range key { - fullData[i], fullData[i+len(data)] = data[i]^b, b + fullData[i], fullData[i+len(data)] = evalOperator(operators[i], data[i], b), b } shuffledIdxs := mathrand.Perm(len(fullData)) @@ -31,11 +36,11 @@ func (x xorShuffle) obfuscate(data []byte) *ast.BlockStmt { args := []ast.Expr{ah.Ident("data")} for i := range data { - args = append(args, &ast.BinaryExpr{ - X: ah.IndexExpr("fullData", ah.IntLit(shuffledIdxs[i])), - Op: token.XOR, - Y: ah.IndexExpr("fullData", ah.IntLit(shuffledIdxs[len(data)+i])), - }) + args = append(args, operatorToReversedBinaryExpr( + operators[i], + ah.IndexExpr("fullData", ah.IntLit(shuffledIdxs[i])), + ah.IndexExpr("fullData", ah.IntLit(shuffledIdxs[len(data)+i])), + )) } return ah.BlockStmt( diff --git a/testdata/scripts/literals.txt b/testdata/scripts/literals.txt index 5753122..7ffb884 100644 --- a/testdata/scripts/literals.txt +++ b/testdata/scripts/literals.txt @@ -36,8 +36,8 @@ cmp stderr normal.stderr # Check obfuscators -# Xor obfuscator. Detect a[i] = a[i] ^ b[i] -grep '^\s+\w+\[\w+\] = \w+\[\w+\] \^ \w+$' .obf-src/main/z0.go +# Xor obfuscator. Detect a[i] = a[i] (^|-|+) b[i] +grep '^\s+\w+\[\w+\] = \w+\[\w+\] [\^\-+] \w+$' .obf-src/main/z0.go # Swap obfuscator. Detect [...]byte|uint16|uint32|uint64{...} grep '^\s+\w+ := \[\.{3}\](byte|uint16|uint32|uint64)\{[0-9\s,]+\}$' .obf-src/main/z0.go @@ -45,8 +45,8 @@ grep '^\s+\w+ := \[\.{3}\](byte|uint16|uint32|uint64)\{[0-9\s,]+\}$' .obf-src/ma # Split obfuscator. Detect decryptKey ^= i * counter grep '^\s+\w+ \^= \w+ \* \w+$' .obf-src/main/z0.go -# XorShuffle obfuscator. Detect data = append(data, x ^ y...) -grep '^\s+\w+ = append\(\w+,(\s+\w+\[\d+\]\^\w+\[\d+\],?)+\)$' .obf-src/main/z0.go +# XorShuffle obfuscator. Detect data = append(data, x (^|-|+) y...) +grep '^\s+\w+ = append\(\w+,(\s+\w+\[\d+\][\^\-+]\w+\[\d+\],?)+\)$' .obf-src/main/z0.go # XorSeed obfuscator. Detect type decFunc func(byte) decFunc grep '^\s+type \w+ func\(byte\) \w+$' .obf-src/main/z0.go