Files
garble/internal/asthelper/asthelper.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

216 lines
4.9 KiB
Go

// Copyright (c) 2020, The Garble Authors.
// See LICENSE for licensing information.
package asthelper
import (
"fmt"
"go/ast"
"go/constant"
"go/token"
"strconv"
)
// StringLit returns an ast.BasicLit of kind STRING
func StringLit(value string) *ast.BasicLit {
return &ast.BasicLit{
Kind: token.STRING,
Value: fmt.Sprintf("%q", value),
}
}
// IntLit returns an ast.BasicLit of kind INT
func IntLit(value int) *ast.BasicLit {
return &ast.BasicLit{
Kind: token.INT,
Value: strconv.Itoa(value),
}
}
// IndexExpr "name[index]"
func IndexExpr(name string, index ast.Expr) *ast.IndexExpr {
return &ast.IndexExpr{
X: ast.NewIdent(name),
Index: index,
}
}
// CallExpr "fun(arg)"
func CallExpr(fun ast.Expr, args ...ast.Expr) *ast.CallExpr {
return &ast.CallExpr{
Fun: fun,
Args: args,
}
}
// 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: params,
Results: &ast.FieldList{
List: []*ast.Field{
{Type: resultType},
},
},
},
Body: block,
}
return CallExpr(funcLit, args...)
}
// ReturnStmt "return result"
func ReturnStmt(results ...ast.Expr) *ast.ReturnStmt {
return &ast.ReturnStmt{
Results: results,
}
}
// BlockStmt a block of multiple statements e.g. a function body
func BlockStmt(stmts ...ast.Stmt) *ast.BlockStmt {
return &ast.BlockStmt{List: stmts}
}
// ExprStmt convert an ast.Expr to an ast.Stmt
func ExprStmt(expr ast.Expr) *ast.ExprStmt {
return &ast.ExprStmt{X: expr}
}
// DataToByteSlice turns a byte slice like []byte{1, 2, 3} into an AST
// expression
func DataToByteSlice(data []byte) *ast.CallExpr {
return &ast.CallExpr{
Fun: &ast.ArrayType{
Elt: &ast.Ident{Name: "byte"},
},
Args: []ast.Expr{StringLit(string(data))},
}
}
// DataToArray turns a byte slice like []byte{1, 2, 3} into an AST
// expression
func DataToArray(data []byte) *ast.CompositeLit {
elts := make([]ast.Expr, len(data))
for i, b := range data {
elts[i] = IntLit(int(b))
}
return &ast.CompositeLit{
Type: &ast.ArrayType{
Len: IntLit(len(data)),
Elt: ast.NewIdent("byte"),
},
Elts: elts,
}
}
// SelectExpr "x.sel"
func SelectExpr(x ast.Expr, sel *ast.Ident) *ast.SelectorExpr {
return &ast.SelectorExpr{
X: x,
Sel: sel,
}
}
// AssignDefineStmt "Lhs := Rhs"
func AssignDefineStmt(Lhs ast.Expr, Rhs ast.Expr) *ast.AssignStmt {
return &ast.AssignStmt{
Lhs: []ast.Expr{Lhs},
Tok: token.DEFINE,
Rhs: []ast.Expr{Rhs},
}
}
// CallExprByName "fun(args...)"
func CallExprByName(fun string, args ...ast.Expr) *ast.CallExpr {
return CallExpr(ast.NewIdent(fun), args...)
}
// AssignStmt "Lhs = Rhs"
func AssignStmt(Lhs ast.Expr, Rhs ast.Expr) *ast.AssignStmt {
return &ast.AssignStmt{
Lhs: []ast.Expr{Lhs},
Tok: token.ASSIGN,
Rhs: []ast.Expr{Rhs},
}
}
// IndexExprByExpr "xExpr[indexExpr]"
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:
return ast.NewIdent(val.ExactString())
case constant.String:
return &ast.BasicLit{Kind: token.STRING, Value: val.ExactString()}
case constant.Int:
return &ast.BasicLit{Kind: token.INT, Value: val.ExactString()}
case constant.Float:
return &ast.BasicLit{Kind: token.FLOAT, Value: val.String()}
case constant.Complex:
return CallExprByName("complex", ConstToAst(constant.Real(val)), ConstToAst(constant.Imag(val)))
default:
panic("unreachable")
}
}