mirror of
https://github.com/burrowers/garble.git
synced 2025-09-26 20:01:16 +08:00
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>
This commit is contained in:
10
hash.go
10
hash.go
@@ -12,6 +12,7 @@ import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
mathrand "math/rand"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -310,6 +311,15 @@ const (
|
||||
neededSumBytes = 9
|
||||
)
|
||||
|
||||
// randomName generates a random name derived from the given baseName, using the provided random source.
|
||||
func randomName(rand *mathrand.Rand, baseName string) string {
|
||||
salt := make([]byte, buildIDHashLength)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return hashWithCustomSalt(salt, baseName)
|
||||
}
|
||||
|
||||
// hashWithCustomSalt returns a hashed version of name,
|
||||
// including the provided salt as well as opts.Seed into the hash input.
|
||||
//
|
||||
|
@@ -43,11 +43,11 @@ func CallExpr(fun ast.Expr, args ...ast.Expr) *ast.CallExpr {
|
||||
}
|
||||
}
|
||||
|
||||
// LambdaCall "func() resultType {block}()"
|
||||
func LambdaCall(resultType ast.Expr, block *ast.BlockStmt) *ast.CallExpr {
|
||||
// LambdaCall "func(params) resultType {block}(args)"
|
||||
func LambdaCall(params *ast.FieldList, resultType ast.Expr, block *ast.BlockStmt, args []ast.Expr) *ast.CallExpr {
|
||||
funcLit := &ast.FuncLit{
|
||||
Type: &ast.FuncType{
|
||||
Params: &ast.FieldList{},
|
||||
Params: params,
|
||||
Results: &ast.FieldList{
|
||||
List: []*ast.Field{
|
||||
{Type: resultType},
|
||||
@@ -56,7 +56,7 @@ func LambdaCall(resultType ast.Expr, block *ast.BlockStmt) *ast.CallExpr {
|
||||
},
|
||||
Body: block,
|
||||
}
|
||||
return CallExpr(funcLit)
|
||||
return CallExpr(funcLit, args...)
|
||||
}
|
||||
|
||||
// ReturnStmt "return result"
|
||||
@@ -140,6 +140,63 @@ func IndexExprByExpr(xExpr, indexExpr ast.Expr) *ast.IndexExpr {
|
||||
return &ast.IndexExpr{X: xExpr, Index: indexExpr}
|
||||
}
|
||||
|
||||
// UnaryExpr creates a unary expression with the given operator and operand
|
||||
func UnaryExpr(op token.Token, x ast.Expr) *ast.UnaryExpr {
|
||||
return &ast.UnaryExpr{
|
||||
Op: op,
|
||||
X: x,
|
||||
}
|
||||
}
|
||||
|
||||
// StarExpr creates a pointer type expression "*x"
|
||||
func StarExpr(x ast.Expr) *ast.StarExpr {
|
||||
return &ast.StarExpr{X: x}
|
||||
}
|
||||
|
||||
// ArrayType creates an array type expression "[len]eltType"
|
||||
func ArrayType(len ast.Expr, eltType ast.Expr) *ast.ArrayType {
|
||||
return &ast.ArrayType{
|
||||
Len: len,
|
||||
Elt: eltType,
|
||||
}
|
||||
}
|
||||
|
||||
// ByteArrayType creates a byte array type "[len]byte"
|
||||
func ByteArrayType(len int64) *ast.ArrayType {
|
||||
lenLit := IntLit(int(len))
|
||||
return ArrayType(lenLit, ast.NewIdent("byte"))
|
||||
}
|
||||
|
||||
// ByteSliceType creates a byte slice type "[]byte"
|
||||
func ByteSliceType() *ast.ArrayType {
|
||||
return &ast.ArrayType{Elt: ast.NewIdent("byte")}
|
||||
}
|
||||
|
||||
// BinaryExpr creates a binary expression "x op y"
|
||||
func BinaryExpr(x ast.Expr, op token.Token, y ast.Expr) *ast.BinaryExpr {
|
||||
return &ast.BinaryExpr{
|
||||
X: x,
|
||||
Op: op,
|
||||
Y: y,
|
||||
}
|
||||
}
|
||||
|
||||
// UintLit returns an ast.BasicLit of kind INT for uint64 values
|
||||
func UintLit(value uint64) *ast.BasicLit {
|
||||
return &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: fmt.Sprint(value),
|
||||
}
|
||||
}
|
||||
|
||||
// Field creates a field with names and type for function parameters or struct fields
|
||||
func Field(typ ast.Expr, names ...*ast.Ident) *ast.Field {
|
||||
return &ast.Field{
|
||||
Names: names,
|
||||
Type: typ,
|
||||
}
|
||||
}
|
||||
|
||||
func ConstToAst(val constant.Value) ast.Expr {
|
||||
switch val.Kind() {
|
||||
case constant.Bool:
|
||||
|
@@ -72,7 +72,9 @@ func FuzzObfuscate(f *testing.F) {
|
||||
|
||||
// Obfuscate the literals and print the source back.
|
||||
rand := mathrand.New(mathrand.NewSource(randSeed))
|
||||
srcSyntax = literals.Obfuscate(rand, srcSyntax, &info, nil)
|
||||
srcSyntax = literals.Obfuscate(rand, srcSyntax, &info, nil, func(rand *mathrand.Rand, baseName string) string {
|
||||
return fmt.Sprintf("%s%d", baseName, rand.Uint64())
|
||||
})
|
||||
count := tdirCounter.Add(1)
|
||||
f, err := os.Create(filepath.Join(tdir, fmt.Sprintf("src_%d.go", count)))
|
||||
qt.Assert(t, qt.IsNil(err))
|
||||
|
@@ -25,9 +25,19 @@ const MinSize = 8
|
||||
// Beyond that we apply only a subset of obfuscators which are guaranteed to run efficiently.
|
||||
const maxSize = 2 << 10 // KiB
|
||||
|
||||
const (
|
||||
// minStringJunkBytes defines the minimum number of junk bytes to prepend or append during string obfuscation.
|
||||
minStringJunkBytes = 2
|
||||
// maxStringJunkBytes defines the maximum number of junk bytes to prepend or append during string obfuscation.
|
||||
maxStringJunkBytes = 8
|
||||
)
|
||||
|
||||
// NameProviderFunc defines a function type that generates a string based on a random source and a base name.
|
||||
type NameProviderFunc func(rand *mathrand.Rand, baseName string) string
|
||||
|
||||
// Obfuscate replaces literals with obfuscated anonymous functions.
|
||||
func Obfuscate(rand *mathrand.Rand, file *ast.File, info *types.Info, linkStrings map[*types.Var]string) *ast.File {
|
||||
obfRand := newObfRand(rand, file)
|
||||
func Obfuscate(rand *mathrand.Rand, file *ast.File, info *types.Info, linkStrings map[*types.Var]string, nameFunc NameProviderFunc) *ast.File {
|
||||
obfRand := newObfRand(rand, file, nameFunc)
|
||||
pre := func(cursor *astutil.Cursor) bool {
|
||||
switch node := cursor.Node().(type) {
|
||||
case *ast.GenDecl:
|
||||
@@ -110,7 +120,9 @@ func Obfuscate(rand *mathrand.Rand, file *ast.File, info *types.Info, linkString
|
||||
return true
|
||||
}
|
||||
|
||||
return astutil.Apply(file, pre, post).(*ast.File)
|
||||
newFile := astutil.Apply(file, pre, post).(*ast.File)
|
||||
obfRand.proxyDispatcher.AddToFile(newFile)
|
||||
return newFile
|
||||
}
|
||||
|
||||
// handleCompositeLiteral checks if the input node is []byte or [...]byte and
|
||||
@@ -213,39 +225,86 @@ func withPos(node ast.Node, pos token.Pos) ast.Node {
|
||||
|
||||
func obfuscateString(obfRand *obfRand, data string) *ast.CallExpr {
|
||||
obf := getNextObfuscator(obfRand, len(data))
|
||||
block := obf.obfuscate(obfRand.Rand, []byte(data))
|
||||
|
||||
block.List = append(block.List, ah.ReturnStmt(ah.CallExpr(ast.NewIdent("string"), ast.NewIdent("data"))))
|
||||
// Generate junk bytes to to prepend and append to the data.
|
||||
// This is to prevent the obfuscated string from being easily fingerprintable.
|
||||
junkBytes := make([]byte, obfRand.Intn(maxStringJunkBytes-minStringJunkBytes)+minStringJunkBytes)
|
||||
obfRand.Read(junkBytes)
|
||||
splitIdx := obfRand.Intn(len(junkBytes))
|
||||
|
||||
return ah.LambdaCall(ast.NewIdent("string"), block)
|
||||
extKeys := randExtKeys(obfRand.Rand)
|
||||
|
||||
plainData := []byte(data)
|
||||
plainDataWithJunkBytes := append(append(junkBytes[:splitIdx], plainData...), junkBytes[splitIdx:]...)
|
||||
|
||||
block := obf.obfuscate(obfRand.Rand, plainDataWithJunkBytes, extKeys)
|
||||
params, args := extKeysToParams(obfRand, extKeys)
|
||||
|
||||
// Generate unique cast bytes to string function and hide it using proxyDispatcher:
|
||||
//
|
||||
// func(x []byte) string {
|
||||
// return string(x[<splitIdx>:<splitIdx+len(plainData)>])
|
||||
// }
|
||||
funcTyp := &ast.FuncType{
|
||||
Params: &ast.FieldList{List: []*ast.Field{{
|
||||
Type: ah.ByteSliceType(),
|
||||
}}},
|
||||
Results: &ast.FieldList{List: []*ast.Field{{
|
||||
Type: ast.NewIdent("string"),
|
||||
}}},
|
||||
}
|
||||
funcVal := &ast.FuncLit{
|
||||
Type: &ast.FuncType{
|
||||
Params: &ast.FieldList{List: []*ast.Field{{
|
||||
Names: []*ast.Ident{ast.NewIdent("x")},
|
||||
Type: ah.ByteSliceType(),
|
||||
}}},
|
||||
Results: &ast.FieldList{List: []*ast.Field{{
|
||||
Type: ast.NewIdent("string"),
|
||||
}}},
|
||||
},
|
||||
Body: ah.BlockStmt(
|
||||
ah.ReturnStmt(
|
||||
ah.CallExprByName("string",
|
||||
&ast.SliceExpr{
|
||||
X: ast.NewIdent("x"),
|
||||
Low: ah.IntLit(splitIdx),
|
||||
High: ah.IntLit(splitIdx + len(plainData)),
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
}
|
||||
block.List = append(block.List, ah.ReturnStmt(ah.CallExpr(obfRand.proxyDispatcher.HideValue(funcVal, funcTyp), ast.NewIdent("data"))))
|
||||
return ah.LambdaCall(params, ast.NewIdent("string"), block, args)
|
||||
}
|
||||
|
||||
func obfuscateByteSlice(obfRand *obfRand, isPointer bool, data []byte) *ast.CallExpr {
|
||||
obf := getNextObfuscator(obfRand, len(data))
|
||||
block := obf.obfuscate(obfRand.Rand, data)
|
||||
|
||||
extKeys := randExtKeys(obfRand.Rand)
|
||||
block := obf.obfuscate(obfRand.Rand, data, extKeys)
|
||||
params, args := extKeysToParams(obfRand, extKeys)
|
||||
|
||||
if isPointer {
|
||||
block.List = append(block.List, ah.ReturnStmt(&ast.UnaryExpr{
|
||||
Op: token.AND,
|
||||
X: ast.NewIdent("data"),
|
||||
}))
|
||||
return ah.LambdaCall(&ast.StarExpr{
|
||||
X: &ast.ArrayType{Elt: ast.NewIdent("byte")},
|
||||
}, block)
|
||||
block.List = append(block.List, ah.ReturnStmt(
|
||||
ah.UnaryExpr(token.AND, ast.NewIdent("data")),
|
||||
))
|
||||
return ah.LambdaCall(params, ah.StarExpr(ah.ByteSliceType()), block, args)
|
||||
}
|
||||
|
||||
block.List = append(block.List, ah.ReturnStmt(ast.NewIdent("data")))
|
||||
return ah.LambdaCall(&ast.ArrayType{Elt: ast.NewIdent("byte")}, block)
|
||||
return ah.LambdaCall(params, ah.ByteSliceType(), block, args)
|
||||
}
|
||||
|
||||
func obfuscateByteArray(obfRand *obfRand, isPointer bool, data []byte, length int64) *ast.CallExpr {
|
||||
obf := getNextObfuscator(obfRand, len(data))
|
||||
block := obf.obfuscate(obfRand.Rand, data)
|
||||
|
||||
arrayType := &ast.ArrayType{
|
||||
Len: ah.IntLit(int(length)),
|
||||
Elt: ast.NewIdent("byte"),
|
||||
}
|
||||
extKeys := randExtKeys(obfRand.Rand)
|
||||
block := obf.obfuscate(obfRand.Rand, data, extKeys)
|
||||
params, args := extKeysToParams(obfRand, extKeys)
|
||||
|
||||
arrayType := ah.ByteArrayType(length)
|
||||
|
||||
sliceToArray := []ast.Stmt{
|
||||
&ast.DeclStmt{
|
||||
@@ -261,29 +320,28 @@ func obfuscateByteArray(obfRand *obfRand, isPointer bool, data []byte, length in
|
||||
Key: ast.NewIdent("i"),
|
||||
Tok: token.DEFINE,
|
||||
X: ast.NewIdent("data"),
|
||||
Body: &ast.BlockStmt{List: []ast.Stmt{
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ah.IndexExpr("newdata", ast.NewIdent("i"))},
|
||||
Tok: token.ASSIGN,
|
||||
Rhs: []ast.Expr{ah.IndexExpr("data", ast.NewIdent("i"))},
|
||||
},
|
||||
}},
|
||||
Body: ah.BlockStmt(
|
||||
ah.AssignStmt(
|
||||
ah.IndexExprByExpr(ast.NewIdent("newdata"), ast.NewIdent("i")),
|
||||
ah.IndexExprByExpr(ast.NewIdent("data"), ast.NewIdent("i")),
|
||||
),
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
var retexpr ast.Expr = ast.NewIdent("newdata")
|
||||
if isPointer {
|
||||
retexpr = &ast.UnaryExpr{X: retexpr, Op: token.AND}
|
||||
retexpr = ah.UnaryExpr(token.AND, retexpr)
|
||||
}
|
||||
|
||||
sliceToArray = append(sliceToArray, ah.ReturnStmt(retexpr))
|
||||
block.List = append(block.List, sliceToArray...)
|
||||
|
||||
if isPointer {
|
||||
return ah.LambdaCall(&ast.StarExpr{X: arrayType}, block)
|
||||
return ah.LambdaCall(params, ah.StarExpr(arrayType), block, args)
|
||||
}
|
||||
|
||||
return ah.LambdaCall(arrayType, block)
|
||||
return ah.LambdaCall(params, arrayType, block, args)
|
||||
}
|
||||
|
||||
func getNextObfuscator(obfRand *obfRand, size int) obfuscator {
|
||||
|
@@ -7,12 +7,56 @@ import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"math"
|
||||
mathrand "math/rand"
|
||||
"slices"
|
||||
"strconv"
|
||||
|
||||
ah "mvdan.cc/garble/internal/asthelper"
|
||||
)
|
||||
|
||||
// externalKeyProbability probability of using an external key.
|
||||
// Larger value, greater probability of using an external key.
|
||||
// Must be between 0 and 1
|
||||
type externalKeyProbability float32
|
||||
|
||||
const (
|
||||
lowProb externalKeyProbability = 0.4
|
||||
normalProb externalKeyProbability = 0.6
|
||||
highProb externalKeyProbability = 0.8
|
||||
)
|
||||
|
||||
func (r externalKeyProbability) Try(rand *mathrand.Rand) bool {
|
||||
return rand.Float32() < float32(r)
|
||||
}
|
||||
|
||||
// externalKey contains all information about the external key
|
||||
type externalKey struct {
|
||||
name, typ string
|
||||
value uint64
|
||||
bits int
|
||||
refs int
|
||||
}
|
||||
|
||||
func (k *externalKey) Type() *ast.Ident {
|
||||
return ast.NewIdent(k.typ)
|
||||
}
|
||||
|
||||
func (k *externalKey) Name() *ast.Ident {
|
||||
return ast.NewIdent(k.name)
|
||||
}
|
||||
|
||||
func (k *externalKey) AddRef() {
|
||||
k.refs++
|
||||
}
|
||||
|
||||
func (k *externalKey) IsUsed() bool {
|
||||
return k.refs > 0
|
||||
}
|
||||
|
||||
// obfuscator takes a byte slice and converts it to a ast.BlockStmt
|
||||
type obfuscator interface {
|
||||
obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt
|
||||
obfuscate(obfRand *mathrand.Rand, data []byte, extKeys []*externalKey) *ast.BlockStmt
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -63,25 +107,158 @@ func evalOperator(t token.Token, x, y byte) byte {
|
||||
}
|
||||
|
||||
func operatorToReversedBinaryExpr(t token.Token, x, y ast.Expr) *ast.BinaryExpr {
|
||||
expr := &ast.BinaryExpr{X: x, Y: y}
|
||||
|
||||
var op token.Token
|
||||
switch t {
|
||||
case token.XOR:
|
||||
expr.Op = token.XOR
|
||||
op = token.XOR
|
||||
case token.ADD:
|
||||
expr.Op = token.SUB
|
||||
op = token.SUB
|
||||
case token.SUB:
|
||||
expr.Op = token.ADD
|
||||
op = token.ADD
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown operator: %s", t))
|
||||
}
|
||||
return ah.BinaryExpr(x, op, y)
|
||||
}
|
||||
|
||||
return expr
|
||||
const (
|
||||
// minExtKeyCount is minimum number of external keys for one lambda call
|
||||
minExtKeyCount = 2
|
||||
// maxExtKeyCount is maximum number of external keys for one lambda call
|
||||
maxExtKeyCount = 6
|
||||
|
||||
// minByteSliceExtKeyOps minimum number of operations with external keys for one byte slice
|
||||
minByteSliceExtKeyOps = 2
|
||||
// maxByteSliceExtKeyOps maximum number of operations with external keys for one byte slice
|
||||
maxByteSliceExtKeyOps = 12
|
||||
)
|
||||
|
||||
// extKeyRanges contains a list of different ranges of random numbers for external keys
|
||||
// Different types and bitnesses will increase the chance of changing patterns
|
||||
var extKeyRanges = []struct {
|
||||
typ string
|
||||
max uint64
|
||||
bits int
|
||||
}{
|
||||
{"uint8", math.MaxUint8, 8},
|
||||
{"uint16", math.MaxUint16, 16},
|
||||
{"uint32", math.MaxUint32, 32},
|
||||
{"uint64", math.MaxUint64, 64},
|
||||
}
|
||||
|
||||
// randExtKey generates a random external key with a unique name, type, value, and bitnesses
|
||||
func randExtKey(rand *mathrand.Rand, idx int) *externalKey {
|
||||
r := extKeyRanges[rand.Intn(len(extKeyRanges))]
|
||||
return &externalKey{
|
||||
name: "garbleExternalKey" + strconv.Itoa(idx),
|
||||
typ: r.typ,
|
||||
value: rand.Uint64() & r.max,
|
||||
bits: r.bits,
|
||||
}
|
||||
}
|
||||
|
||||
func randExtKeys(rand *mathrand.Rand) []*externalKey {
|
||||
count := minExtKeyCount + rand.Intn(maxExtKeyCount-minExtKeyCount)
|
||||
keys := make([]*externalKey, count)
|
||||
for i := 0; i < count; i++ {
|
||||
keys[i] = randExtKey(rand, i)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// extKeysToParams converts a list of extKeys into a parameter list and argument expressions for function calls.
|
||||
// It ensures unused keys have placeholder names and sometimes use proxyDispatcher.HideValue for key values
|
||||
func extKeysToParams(objRand *obfRand, keys []*externalKey) (params *ast.FieldList, args []ast.Expr) {
|
||||
params = &ast.FieldList{}
|
||||
for _, key := range keys {
|
||||
name := key.Name()
|
||||
if !key.IsUsed() {
|
||||
name.Name = "_"
|
||||
}
|
||||
params.List = append(params.List, ah.Field(key.Type(), name))
|
||||
|
||||
var extKeyExpr ast.Expr = ah.UintLit(key.value)
|
||||
if lowProb.Try(objRand.Rand) {
|
||||
extKeyExpr = objRand.proxyDispatcher.HideValue(extKeyExpr, ast.NewIdent(key.typ))
|
||||
}
|
||||
args = append(args, extKeyExpr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// extKeyToExpr converts an external key into an AST expression like:
|
||||
//
|
||||
// uint8(key >> b)
|
||||
func (key *externalKey) ToExpr(b int) ast.Expr {
|
||||
var x ast.Expr = key.Name()
|
||||
if b > 0 {
|
||||
x = ah.BinaryExpr(x, token.SHR, ah.IntLit(b*8))
|
||||
}
|
||||
if key.typ != "uint8" {
|
||||
x = ah.CallExprByName("byte", x)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// dataToByteSliceWithExtKeys scramble and turn a byte slice into an AST expression like:
|
||||
//
|
||||
// func() []byte {
|
||||
// data := []byte("<data>")
|
||||
// data[<index>] = data[<index>] <random operator> byte(<external key> >> <random shift>) // repeated random times
|
||||
// return data
|
||||
// }()
|
||||
func dataToByteSliceWithExtKeys(rand *mathrand.Rand, data []byte, extKeys []*externalKey) ast.Expr {
|
||||
extKeyOpCount := minByteSliceExtKeyOps + rand.Intn(maxByteSliceExtKeyOps-minByteSliceExtKeyOps)
|
||||
|
||||
var stmts []ast.Stmt
|
||||
for i := 0; i < extKeyOpCount; i++ {
|
||||
key := extKeys[rand.Intn(len(extKeys))]
|
||||
key.AddRef()
|
||||
|
||||
idx, op, b := rand.Intn(len(data)), randOperator(rand), rand.Intn(key.bits/8)
|
||||
data[idx] = evalOperator(op, data[idx], byte(key.value>>(b*8)))
|
||||
stmts = append(stmts, ah.AssignStmt(
|
||||
ah.IndexExpr("data", ah.IntLit(idx)),
|
||||
operatorToReversedBinaryExpr(op,
|
||||
ah.IndexExpr("data", ah.IntLit(idx)),
|
||||
key.ToExpr(b),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
// External keys can be applied several times to the same array element,
|
||||
// and it is important to invert the order of execution to correctly restore the original value
|
||||
slices.Reverse(stmts)
|
||||
|
||||
stmts = append([]ast.Stmt{ah.AssignDefineStmt(ast.NewIdent("data"), ah.DataToByteSlice(data))}, append(stmts, ah.ReturnStmt(ast.NewIdent("data")))...)
|
||||
return ah.LambdaCall(nil, ah.ByteSliceType(), ah.BlockStmt(stmts...), nil)
|
||||
}
|
||||
|
||||
// dataToByteSliceWithExtKeys scramble and turns a byte into an AST expression like:
|
||||
//
|
||||
// byte(<obfuscated value>) <random operator> byte(<external key> >> <random shift>)
|
||||
func byteLitWithExtKey(rand *mathrand.Rand, val byte, extKeys []*externalKey, extKeyProb externalKeyProbability) ast.Expr {
|
||||
if !extKeyProb.Try(rand) {
|
||||
return ah.IntLit(int(val))
|
||||
}
|
||||
|
||||
key := extKeys[rand.Intn(len(extKeys))]
|
||||
key.AddRef()
|
||||
|
||||
op, b := randOperator(rand), rand.Intn(key.bits/8)
|
||||
newVal := evalOperator(op, val, byte(key.value>>(b*8)))
|
||||
|
||||
return operatorToReversedBinaryExpr(op,
|
||||
ah.CallExprByName("byte", ah.IntLit(int(newVal))),
|
||||
key.ToExpr(b),
|
||||
)
|
||||
}
|
||||
|
||||
type obfRand struct {
|
||||
*mathrand.Rand
|
||||
testObfuscator obfuscator
|
||||
|
||||
proxyDispatcher *proxyDispatcher
|
||||
}
|
||||
|
||||
func (r *obfRand) nextObfuscator() obfuscator {
|
||||
@@ -98,7 +275,7 @@ func (r *obfRand) nextLinearTimeObfuscator() obfuscator {
|
||||
return Obfuscators[r.Intn(len(LinearTimeObfuscators))]
|
||||
}
|
||||
|
||||
func newObfRand(rand *mathrand.Rand, file *ast.File) *obfRand {
|
||||
func newObfRand(rand *mathrand.Rand, file *ast.File, nameFunc NameProviderFunc) *obfRand {
|
||||
testObf := testPkgToObfuscatorMap[file.Name.Name]
|
||||
return &obfRand{rand, testObf}
|
||||
return &obfRand{rand, testObf, newProxyDispatcher(rand, nameFunc)}
|
||||
}
|
||||
|
247
internal/literals/proxy.go
Normal file
247
internal/literals/proxy.go
Normal file
@@ -0,0 +1,247 @@
|
||||
package literals
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
mathrand "math/rand"
|
||||
"strconv"
|
||||
|
||||
ah "mvdan.cc/garble/internal/asthelper"
|
||||
)
|
||||
|
||||
const (
|
||||
// minStructCount is the minimum number of proxyStructs initialized in the dispatcher.
|
||||
minStructCount = 4
|
||||
// maxStructCount is the maximum number of proxyStructs initialized in the dispatcher.
|
||||
maxStructCount = 8
|
||||
//minChildCount defines the minimum number of child elements that can be assigned to proxy structs.
|
||||
minChildCount = 1
|
||||
// maxChildCount defines the maximum number of child elements that can be assigned to proxy structs.
|
||||
maxChildCount = 3
|
||||
// minJunkValueCount defines the minimum number of junk values that can be added to a structure.
|
||||
minJunkValueCount = 1
|
||||
// maxJunkValueCount defines the maximum number of junk values that can be added to a structure.
|
||||
maxJunkValueCount = 3
|
||||
// minJunkArraySize defines the minimum size of a randomized byte array for junk data generation.
|
||||
minJunkArraySize = 1
|
||||
// maxJunkArraySize defines the maximum size of a randomized byte array for junk data generation.
|
||||
maxJunkArraySize = 8
|
||||
)
|
||||
|
||||
// proxyValue represents a named field with a type and value used in a proxy structure.
|
||||
type proxyValue struct {
|
||||
name string
|
||||
typ, val ast.Expr
|
||||
}
|
||||
|
||||
type proxyStruct struct {
|
||||
typeName, name string
|
||||
isPointer bool
|
||||
|
||||
values []*proxyValue
|
||||
|
||||
children []*proxyStruct
|
||||
parent *proxyStruct
|
||||
}
|
||||
|
||||
type proxyDispatcher struct {
|
||||
rand *mathrand.Rand
|
||||
nameFunc NameProviderFunc
|
||||
|
||||
root *proxyStruct
|
||||
flattenStructs []*proxyStruct
|
||||
}
|
||||
|
||||
func (d *proxyDispatcher) initialize() {
|
||||
flattenStructs := make([]*proxyStruct, d.rand.Intn(maxStructCount-minStructCount)+minStructCount)
|
||||
for i := 0; i < len(flattenStructs); i++ {
|
||||
flattenStructs[i] = &proxyStruct{
|
||||
typeName: d.nameFunc(d.rand, "proxyStructName"+strconv.Itoa(i)),
|
||||
name: d.nameFunc(d.rand, "proxyStructFieldName"+strconv.Itoa(i)),
|
||||
isPointer: d.rand.Intn(2) == 0,
|
||||
}
|
||||
}
|
||||
|
||||
root := &proxyStruct{
|
||||
name: d.nameFunc(d.rand, "rootStructName"),
|
||||
typeName: d.nameFunc(d.rand, "rootStructType"),
|
||||
parent: nil,
|
||||
}
|
||||
d.root = root
|
||||
|
||||
unassigned := append([]*proxyStruct(nil), flattenStructs...)
|
||||
queue := []*proxyStruct{root}
|
||||
for len(unassigned) > 0 && len(queue) > 0 {
|
||||
current := queue[0]
|
||||
queue = queue[1:]
|
||||
|
||||
childCount := d.rand.Intn(maxChildCount-minChildCount) + minChildCount
|
||||
if childCount > len(unassigned) {
|
||||
childCount = len(unassigned)
|
||||
}
|
||||
|
||||
for i := 0; i < childCount; i++ {
|
||||
child := unassigned[0]
|
||||
unassigned = unassigned[1:]
|
||||
|
||||
child.parent = current
|
||||
current.children = append(current.children, child)
|
||||
queue = append(queue, child)
|
||||
}
|
||||
}
|
||||
|
||||
d.flattenStructs = append(flattenStructs, root)
|
||||
}
|
||||
|
||||
// buildPath creates an AST expression that represents a field access path
|
||||
// from the root of a nested struct hierarchy to a specific field.
|
||||
// Example: root.child.grandchild.fieldName
|
||||
func buildPath(strct *proxyStruct, valueName string) ast.Expr {
|
||||
var stack []*proxyStruct
|
||||
for s := strct; s != nil; s = s.parent {
|
||||
stack = append(stack, s)
|
||||
}
|
||||
|
||||
var expr ast.Expr = ast.NewIdent(stack[len(stack)-1].name)
|
||||
for i := len(stack) - 2; i >= 0; i-- {
|
||||
expr = &ast.SelectorExpr{
|
||||
X: expr,
|
||||
Sel: ast.NewIdent(stack[i].name),
|
||||
}
|
||||
}
|
||||
|
||||
return &ast.SelectorExpr{
|
||||
X: expr,
|
||||
Sel: ast.NewIdent(valueName),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *proxyDispatcher) HideValue(val, typ ast.Expr) ast.Expr {
|
||||
if d.root == nil {
|
||||
d.initialize()
|
||||
}
|
||||
|
||||
strct := d.flattenStructs[d.rand.Intn(len(d.flattenStructs))]
|
||||
valueName := d.nameFunc(d.rand, strct.name+"_"+strct.typeName+"_"+strconv.Itoa(len(strct.values)))
|
||||
strct.values = append(strct.values, &proxyValue{
|
||||
name: valueName,
|
||||
typ: typ,
|
||||
val: val,
|
||||
})
|
||||
return buildPath(strct, valueName)
|
||||
}
|
||||
|
||||
func (d *proxyDispatcher) generateStructLiteral(s *proxyStruct) ast.Expr {
|
||||
var fields []ast.Expr
|
||||
for _, child := range s.children {
|
||||
expr := d.generateStructLiteral(child)
|
||||
if child.isPointer {
|
||||
expr = &ast.UnaryExpr{
|
||||
Op: token.AND,
|
||||
X: expr,
|
||||
}
|
||||
}
|
||||
fields = append(fields, &ast.KeyValueExpr{
|
||||
Key: ast.NewIdent(child.name),
|
||||
Value: expr,
|
||||
})
|
||||
}
|
||||
|
||||
for _, val := range s.values {
|
||||
fields = append(fields, &ast.KeyValueExpr{
|
||||
Key: ast.NewIdent(val.name),
|
||||
Value: val.val,
|
||||
})
|
||||
}
|
||||
|
||||
d.rand.Shuffle(len(fields), func(i, j int) {
|
||||
fields[i], fields[j] = fields[j], fields[i]
|
||||
})
|
||||
|
||||
return &ast.CompositeLit{
|
||||
Type: ast.NewIdent(s.typeName),
|
||||
Elts: fields,
|
||||
}
|
||||
}
|
||||
|
||||
// junkValue generates and returns a proxyValue containing a randomized byte array of variable size
|
||||
func (d *proxyDispatcher) junkValue() *proxyValue {
|
||||
size := d.rand.Intn(maxJunkArraySize-minJunkArraySize+1) + minJunkArraySize
|
||||
data := make([]byte, size)
|
||||
d.rand.Read(data)
|
||||
|
||||
dummyDataExpr := &ast.CompositeLit{
|
||||
Type: &ast.ArrayType{
|
||||
Len: ah.IntLit(size),
|
||||
Elt: ast.NewIdent("byte"),
|
||||
},
|
||||
Elts: ah.DataToArray(data).Elts,
|
||||
}
|
||||
return &proxyValue{
|
||||
name: d.nameFunc(d.rand, "junkValue"),
|
||||
typ: &ast.ArrayType{
|
||||
Len: ah.IntLit(size),
|
||||
Elt: ast.NewIdent("byte"),
|
||||
},
|
||||
val: dummyDataExpr,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *proxyDispatcher) AddToFile(file *ast.File) {
|
||||
if d.root == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, strct := range d.flattenStructs {
|
||||
dummyCount := d.rand.Intn(maxJunkValueCount-minJunkValueCount+1) + minJunkValueCount
|
||||
for i := 0; i < dummyCount; i++ {
|
||||
strct.values = append(strct.values, d.junkValue())
|
||||
}
|
||||
|
||||
structType := &ast.StructType{Fields: &ast.FieldList{}}
|
||||
for _, child := range strct.children {
|
||||
var typ ast.Expr = ast.NewIdent(child.typeName)
|
||||
if child.isPointer {
|
||||
typ = &ast.StarExpr{X: typ}
|
||||
}
|
||||
structType.Fields.List = append(structType.Fields.List, &ast.Field{
|
||||
Names: []*ast.Ident{ast.NewIdent(child.name)},
|
||||
Type: typ,
|
||||
})
|
||||
}
|
||||
|
||||
for _, value := range strct.values {
|
||||
structType.Fields.List = append(structType.Fields.List, &ast.Field{
|
||||
Names: []*ast.Ident{ast.NewIdent(value.name)},
|
||||
Type: value.typ,
|
||||
})
|
||||
}
|
||||
|
||||
d.rand.Shuffle(len(structType.Fields.List), func(i, j int) {
|
||||
structType.Fields.List[i], structType.Fields.List[j] = structType.Fields.List[j], structType.Fields.List[i]
|
||||
})
|
||||
|
||||
file.Decls = append(file.Decls, &ast.GenDecl{
|
||||
Tok: token.TYPE,
|
||||
Specs: []ast.Spec{&ast.TypeSpec{
|
||||
Name: ast.NewIdent(strct.typeName),
|
||||
Type: structType,
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
file.Decls = append(file.Decls, &ast.GenDecl{
|
||||
Tok: token.VAR,
|
||||
Specs: []ast.Spec{&ast.ValueSpec{
|
||||
Names: []*ast.Ident{ast.NewIdent(d.root.name)},
|
||||
Values: []ast.Expr{d.generateStructLiteral(d.root)},
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
func newProxyDispatcher(rand *mathrand.Rand, nameFunc NameProviderFunc) *proxyDispatcher {
|
||||
return &proxyDispatcher{
|
||||
rand: rand,
|
||||
nameFunc: nameFunc,
|
||||
}
|
||||
}
|
@@ -16,30 +16,29 @@ type seed struct{}
|
||||
// check that the obfuscator interface is implemented
|
||||
var _ obfuscator = seed{}
|
||||
|
||||
func (seed) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt {
|
||||
func (seed) obfuscate(obfRand *mathrand.Rand, data []byte, extKeys []*externalKey) *ast.BlockStmt {
|
||||
seed := byte(obfRand.Uint32())
|
||||
originalSeed := seed
|
||||
|
||||
op := randOperator(obfRand)
|
||||
|
||||
var callExpr *ast.CallExpr
|
||||
for i, b := range data {
|
||||
encB := evalOperator(op, b, seed)
|
||||
seed += encB
|
||||
|
||||
if i == 0 {
|
||||
callExpr = ah.CallExpr(ast.NewIdent("fnc"), ah.IntLit(int(encB)))
|
||||
callExpr = ah.CallExpr(ast.NewIdent("fnc"), byteLitWithExtKey(obfRand, encB, extKeys, highProb))
|
||||
continue
|
||||
}
|
||||
|
||||
callExpr = ah.CallExpr(callExpr, ah.IntLit(int(encB)))
|
||||
callExpr = ah.CallExpr(callExpr, byteLitWithExtKey(obfRand, encB, extKeys, lowProb))
|
||||
}
|
||||
|
||||
return ah.BlockStmt(
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ast.NewIdent("seed")},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{ah.CallExpr(ast.NewIdent("byte"), ah.IntLit(int(originalSeed)))},
|
||||
Rhs: []ast.Expr{ah.CallExprByName("byte", byteLitWithExtKey(obfRand, originalSeed, extKeys, highProb))},
|
||||
},
|
||||
&ast.DeclStmt{
|
||||
Decl: &ast.GenDecl{
|
||||
|
@@ -16,9 +16,9 @@ type shuffle struct{}
|
||||
// check that the obfuscator interface is implemented
|
||||
var _ obfuscator = shuffle{}
|
||||
|
||||
func (shuffle) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt {
|
||||
func (shuffle) obfuscate(rand *mathrand.Rand, data []byte, extKeys []*externalKey) *ast.BlockStmt {
|
||||
key := make([]byte, len(data))
|
||||
obfRand.Read(key)
|
||||
rand.Read(key)
|
||||
|
||||
const (
|
||||
minIdxKeySize = 2
|
||||
@@ -26,7 +26,7 @@ func (shuffle) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt {
|
||||
)
|
||||
|
||||
idxKeySize := minIdxKeySize
|
||||
if tmp := obfRand.Intn(len(data)); tmp > idxKeySize {
|
||||
if tmp := rand.Intn(len(data)); tmp > idxKeySize {
|
||||
idxKeySize = tmp
|
||||
}
|
||||
if idxKeySize > maxIdxKeySize {
|
||||
@@ -34,19 +34,19 @@ func (shuffle) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt {
|
||||
}
|
||||
|
||||
idxKey := make([]byte, idxKeySize)
|
||||
obfRand.Read(idxKey)
|
||||
rand.Read(idxKey)
|
||||
|
||||
fullData := make([]byte, len(data)+len(key))
|
||||
operators := make([]token.Token, len(fullData))
|
||||
for i := range operators {
|
||||
operators[i] = randOperator(obfRand)
|
||||
operators[i] = randOperator(rand)
|
||||
}
|
||||
|
||||
for i, b := range key {
|
||||
fullData[i], fullData[i+len(data)] = evalOperator(operators[i], data[i], b), b
|
||||
}
|
||||
|
||||
shuffledIdxs := obfRand.Perm(len(fullData))
|
||||
shuffledIdxs := rand.Perm(len(fullData))
|
||||
|
||||
shuffledFullData := make([]byte, len(fullData))
|
||||
for i, b := range fullData {
|
||||
@@ -55,7 +55,7 @@ func (shuffle) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt {
|
||||
|
||||
args := []ast.Expr{ast.NewIdent("data")}
|
||||
for i := range data {
|
||||
keyIdx := obfRand.Intn(idxKeySize)
|
||||
keyIdx := rand.Intn(idxKeySize)
|
||||
k := int(idxKey[keyIdx])
|
||||
|
||||
args = append(args, operatorToReversedBinaryExpr(
|
||||
@@ -69,12 +69,12 @@ func (shuffle) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt {
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ast.NewIdent("fullData")},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{ah.DataToByteSlice(shuffledFullData)},
|
||||
Rhs: []ast.Expr{dataToByteSliceWithExtKeys(rand, shuffledFullData, extKeys)},
|
||||
},
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ast.NewIdent("idxKey")},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{ah.DataToByteSlice(idxKey)},
|
||||
Rhs: []ast.Expr{dataToByteSliceWithExtKeys(rand, idxKey, extKeys)},
|
||||
},
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ast.NewIdent("data")},
|
||||
|
@@ -16,11 +16,11 @@ type simple struct{}
|
||||
// check that the obfuscator interface is implemented
|
||||
var _ obfuscator = simple{}
|
||||
|
||||
func (simple) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt {
|
||||
func (simple) obfuscate(rand *mathrand.Rand, data []byte, extKeys []*externalKey) *ast.BlockStmt {
|
||||
key := make([]byte, len(data))
|
||||
obfRand.Read(key)
|
||||
rand.Read(key)
|
||||
|
||||
op := randOperator(obfRand)
|
||||
op := randOperator(rand)
|
||||
for i, b := range key {
|
||||
data[i] = evalOperator(op, data[i], b)
|
||||
}
|
||||
@@ -29,12 +29,12 @@ func (simple) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt {
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ast.NewIdent("key")},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{ah.DataToByteSlice(key)},
|
||||
Rhs: []ast.Expr{dataToByteSliceWithExtKeys(rand, key, extKeys)},
|
||||
},
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ast.NewIdent("data")},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{ah.DataToByteSlice(data)},
|
||||
Rhs: []ast.Expr{dataToByteSliceWithExtKeys(rand, data, extKeys)},
|
||||
},
|
||||
&ast.RangeStmt{
|
||||
Key: ast.NewIdent("i"),
|
||||
|
@@ -66,33 +66,33 @@ func encryptChunks(chunks [][]byte, op token.Token, key byte) {
|
||||
}
|
||||
}
|
||||
|
||||
func (split) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt {
|
||||
func (split) obfuscate(rand *mathrand.Rand, data []byte, extKeys []*externalKey) *ast.BlockStmt {
|
||||
var chunks [][]byte
|
||||
// Short arrays should be divided into single-byte fragments
|
||||
if len(data)/maxChunkSize < minCaseCount {
|
||||
chunks = splitIntoOneByteChunks(data)
|
||||
} else {
|
||||
chunks = splitIntoRandomChunks(obfRand, data)
|
||||
chunks = splitIntoRandomChunks(rand, data)
|
||||
}
|
||||
|
||||
// Generate indexes for cases chunk count + 1 decrypt case + 1 exit case
|
||||
indexes := obfRand.Perm(len(chunks) + 2)
|
||||
indexes := rand.Perm(len(chunks) + 2)
|
||||
|
||||
decryptKeyInitial := byte(obfRand.Uint32())
|
||||
decryptKeyInitial := byte(rand.Uint32())
|
||||
decryptKey := decryptKeyInitial
|
||||
// Calculate decrypt key based on indexes and position. Ignore exit index
|
||||
for i, index := range indexes[:len(indexes)-1] {
|
||||
decryptKey ^= byte(index * i)
|
||||
}
|
||||
|
||||
op := randOperator(obfRand)
|
||||
op := randOperator(rand)
|
||||
encryptChunks(chunks, op, decryptKey)
|
||||
|
||||
decryptIndex := indexes[len(indexes)-2]
|
||||
exitIndex := indexes[len(indexes)-1]
|
||||
switchCases := []ast.Stmt{&ast.CaseClause{
|
||||
List: []ast.Expr{ah.IntLit(decryptIndex)},
|
||||
Body: shuffleStmts(obfRand,
|
||||
Body: shuffleStmts(rand,
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ast.NewIdent("i")},
|
||||
Tok: token.ASSIGN,
|
||||
@@ -131,15 +131,15 @@ func (split) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt {
|
||||
}
|
||||
|
||||
if len(chunk) != 1 {
|
||||
appendCallExpr.Args = append(appendCallExpr.Args, ah.StringLit(string(chunk)))
|
||||
appendCallExpr.Args = append(appendCallExpr.Args, dataToByteSliceWithExtKeys(rand, chunk, extKeys))
|
||||
appendCallExpr.Ellipsis = 1
|
||||
} else {
|
||||
appendCallExpr.Args = append(appendCallExpr.Args, ah.IntLit(int(chunk[0])))
|
||||
appendCallExpr.Args = append(appendCallExpr.Args, byteLitWithExtKey(rand, chunk[0], extKeys, lowProb))
|
||||
}
|
||||
|
||||
switchCases = append(switchCases, &ast.CaseClause{
|
||||
List: []ast.Expr{ah.IntLit(index)},
|
||||
Body: shuffleStmts(obfRand,
|
||||
Body: shuffleStmts(rand,
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ast.NewIdent("i")},
|
||||
Tok: token.ASSIGN,
|
||||
@@ -168,7 +168,7 @@ func (split) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt {
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ast.NewIdent("decryptKey")},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{ah.IntLit(int(decryptKeyInitial))},
|
||||
Rhs: []ast.Expr{ah.CallExprByName("int", byteLitWithExtKey(rand, decryptKeyInitial, extKeys, normalProb))},
|
||||
},
|
||||
&ast.ForStmt{
|
||||
Init: &ast.AssignStmt{
|
||||
@@ -199,7 +199,7 @@ func (split) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt {
|
||||
},
|
||||
&ast.SwitchStmt{
|
||||
Tag: ast.NewIdent("i"),
|
||||
Body: ah.BlockStmt(shuffleStmts(obfRand, switchCases...)...),
|
||||
Body: ah.BlockStmt(shuffleStmts(rand, switchCases...)...),
|
||||
}),
|
||||
},
|
||||
)
|
||||
|
@@ -58,13 +58,13 @@ func generateSwapCount(obfRand *mathrand.Rand, dataLen int) int {
|
||||
return swapCount
|
||||
}
|
||||
|
||||
func (swap) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt {
|
||||
swapCount := generateSwapCount(obfRand, len(data))
|
||||
shiftKey := byte(obfRand.Uint32())
|
||||
func (swap) obfuscate(rand *mathrand.Rand, data []byte, extKeys []*externalKey) *ast.BlockStmt {
|
||||
swapCount := generateSwapCount(rand, len(data))
|
||||
shiftKey := byte(rand.Uint32())
|
||||
|
||||
op := randOperator(obfRand)
|
||||
op := randOperator(rand)
|
||||
|
||||
positions := genRandIntSlice(obfRand, len(data), swapCount)
|
||||
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
|
||||
@@ -76,7 +76,7 @@ func (swap) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt {
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ast.NewIdent("data")},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{ah.DataToByteSlice(data)},
|
||||
Rhs: []ast.Expr{dataToByteSliceWithExtKeys(rand, data, extKeys)},
|
||||
},
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{ast.NewIdent("positions")},
|
||||
@@ -118,7 +118,7 @@ func (swap) obfuscate(obfRand *mathrand.Rand, data []byte) *ast.BlockStmt {
|
||||
}),
|
||||
},
|
||||
Op: token.ADD,
|
||||
Y: ah.IntLit(int(shiftKey)),
|
||||
Y: byteLitWithExtKey(rand, shiftKey, extKeys, highProb),
|
||||
}},
|
||||
},
|
||||
&ast.AssignStmt{
|
||||
|
2
main.go
2
main.go
@@ -1819,7 +1819,7 @@ func (tf *transformer) transformGoFile(file *ast.File) *ast.File {
|
||||
// because obfuscated literals sometimes escape to heap,
|
||||
// and that's not allowed in the runtime itself.
|
||||
if flagLiterals && tf.curPkg.ToObfuscate {
|
||||
file = literals.Obfuscate(tf.obfRand, file, tf.info, tf.linkerVariableStrings)
|
||||
file = literals.Obfuscate(tf.obfRand, file, tf.info, tf.linkerVariableStrings, randomName)
|
||||
|
||||
// some imported constants might not be needed anymore, remove unnecessary imports
|
||||
tf.useAllImports(file)
|
||||
|
3
testdata/script/literals.txtar
vendored
3
testdata/script/literals.txtar
vendored
@@ -53,6 +53,9 @@ grep '^(\s+)?\w+ = .*\bappend\(\w+,(\s+\w+\[\d+\^\s.+\][\^\-+]\w+\[\d+\^\s.+\],?
|
||||
# XorSeed obfuscator. Detect type decFunc func(byte) decFunc
|
||||
grep '^\s+type \w+ func\(byte\) \w+$' debug1/test/main/extra_literals.go
|
||||
|
||||
# Check external keys
|
||||
grep 'garbleExternalKey' debug1/test/main/extra_literals.go
|
||||
|
||||
# Finally, sanity check that we can build all of std with -literals.
|
||||
# Analogous to gogarble.txt.
|
||||
exec garble -literals build std
|
||||
|
Reference in New Issue
Block a user