Files
garble/internal/literals/swap.go
pagran d47e0761eb Prevent automated plaintext extraction of literals with current tools (#930)
Some programs which could automatically reverse string literals obfuscated with `-literals` exist.

They currently work by emulating the string literal decryption functions we insert.

We prevent this naive emulation from succeeding by making the decryption functions dependent on global state.

This can still be broken with enough effort, we are curious which approach reverse-engineers come up with next, we certainly still have some ideas to make this harder.

Fixes #926
---------

Co-authored-by: Paul Scheduikat <lu4p@pm.me>
2025-06-03 02:37:51 +02:00

157 lines
4.1 KiB
Go

// Copyright (c) 2020, The Garble Authors.
// See LICENSE for licensing information.
package literals
import (
"go/ast"
"go/token"
"math"
mathrand "math/rand"
ah "mvdan.cc/garble/internal/asthelper"
)
type swap struct{}
// check that the obfuscator interface is implemented
var _ obfuscator = swap{}
func getIndexType(dataLen int64) string {
switch {
case dataLen <= math.MaxUint8:
return "byte"
case dataLen <= math.MaxUint16:
return "uint16"
case dataLen <= math.MaxUint32:
return "uint32"
default:
return "uint64"
}
}
func positionsToSlice(data []int) *ast.CompositeLit {
arr := &ast.CompositeLit{
Type: &ast.ArrayType{
Len: &ast.Ellipsis{}, // Performance optimization
Elt: ast.NewIdent(getIndexType(int64(len(data)))),
},
Elts: []ast.Expr{},
}
for _, data := range data {
arr.Elts = append(arr.Elts, ah.IntLit(data))
}
return arr
}
// Generates a random even swap count based on the length of data
func generateSwapCount(obfRand *mathrand.Rand, dataLen int) int {
swapCount := dataLen
maxExtraPositions := dataLen / 2 // Limit the number of extra positions to half the data length
if maxExtraPositions > 1 {
swapCount += obfRand.Intn(maxExtraPositions)
}
if swapCount%2 != 0 { // Swap count must be even
swapCount++
}
return swapCount
}
func (swap) obfuscate(rand *mathrand.Rand, data []byte, extKeys []*externalKey) *ast.BlockStmt {
swapCount := generateSwapCount(rand, len(data))
shiftKey := byte(rand.Uint32())
op := randOperator(rand)
positions := genRandIntSlice(rand, 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 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(
&ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent("data")},
Tok: token.DEFINE,
Rhs: []ast.Expr{dataToByteSliceWithExtKeys(rand, data, extKeys)},
},
&ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent("positions")},
Tok: token.DEFINE,
Rhs: []ast.Expr{positionsToSlice(positions)},
},
&ast.ForStmt{
Init: &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent("i")},
Tok: token.DEFINE,
Rhs: []ast.Expr{ah.IntLit(0)},
},
Cond: &ast.BinaryExpr{
X: ast.NewIdent("i"),
Op: token.LSS,
Y: ah.IntLit(len(positions)),
},
Post: &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent("i")},
Tok: token.ADD_ASSIGN,
Rhs: []ast.Expr{ah.IntLit(2)},
},
Body: ah.BlockStmt(
&ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent("localKey")},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.BinaryExpr{
X: &ast.BinaryExpr{
X: ah.CallExpr(ast.NewIdent("byte"), ast.NewIdent("i")),
Op: token.ADD,
Y: ah.CallExpr(ast.NewIdent("byte"), &ast.BinaryExpr{
X: ah.IndexExpr("positions", ast.NewIdent("i")),
Op: token.XOR,
Y: ah.IndexExpr("positions", &ast.BinaryExpr{
X: ast.NewIdent("i"),
Op: token.ADD,
Y: ah.IntLit(1),
}),
}),
},
Op: token.ADD,
Y: byteLitWithExtKey(rand, shiftKey, extKeys, highProb),
}},
},
&ast.AssignStmt{
Lhs: []ast.Expr{
ah.IndexExpr("data", ah.IndexExpr("positions", ast.NewIdent("i"))),
ah.IndexExpr("data", ah.IndexExpr("positions", &ast.BinaryExpr{
X: ast.NewIdent("i"),
Op: token.ADD,
Y: ah.IntLit(1),
})),
},
Tok: token.ASSIGN,
Rhs: []ast.Expr{
operatorToReversedBinaryExpr(
op,
ah.IndexExpr("data",
ah.IndexExpr("positions", &ast.BinaryExpr{
X: ast.NewIdent("i"),
Op: token.ADD,
Y: ah.IntLit(1),
}),
),
ast.NewIdent("localKey"),
),
operatorToReversedBinaryExpr(
op,
ah.IndexExpr("data", ah.IndexExpr("positions", ast.NewIdent("i"))),
ast.NewIdent("localKey"),
),
},
},
),
},
)
}