Forked from: github.com/burrowers/garble
You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
Go to file
Daniel Martí e2f06cce94 set positions when using cursor.Replace
The regular obfuscation process simply modifies some simple nodes, such
as identifiers and strings. In those cases, we modify the nodes
in-place, meaning that their positions remain the same. This hasn't
caused any problems.

Literal obfuscation is trickier. Since we replace one expression with an
entirely different one, we use cursor.Replace. The new expression is
entirely made up on the spot, so it lacks position information.

This was causing problems. For example, in the added test input:

	> garble -literals build
	[stderr]
	# test/main
	dgcm4t6w.go:3: misplaced compiler directive
	dgcm4t6w.go:4: misplaced compiler directive
	dgcm4t6w.go:3: misplaced compiler directive
	dgcm4t6w.go:6: misplaced compiler directive
	dgcm4t6w.go:7: misplaced compiler directive
	dgcm4t6w.go:3: misplaced compiler directive
	dgcm4t6w.go:9: misplaced compiler directive
	dgcm4t6w.go:3: misplaced compiler directive
	dgcm4t6w.go:3: too many errors

The build errors are because we'd move the compiler directives, which
makes the compiler unhappy as they must be directly followed by a
function declaration.

The root cause there seems to be that, since the replacement nodes lack
position information, go/printer would try to estimate its printing
position by adding to the last known position. Since -literals adds
code, this would result in the printer position increasing rapidly, and
potentially printing directive comments earlier than needed.

For now, making the replacement nodes have the same position as the
original node seems to stop go/printer from making this mistake.

It's possible that this workaround won't be bulletproof forever, but it
works well for now, and I don't see a simpler workaround right now.
It would be possible to use fancier mechanisms like go/ast.CommentMap or
dave/dst, but those are a significant amount of added complexity as well.

Fixes #285.
3 years ago
.github all: drop support for Go 1.15.x (#265) 3 years ago
internal set positions when using cursor.Replace 3 years ago
scripts update the list of runtime-related packages for 1.16 (#246) 3 years ago
testdata set positions when using cursor.Replace 3 years ago
.gitattributes start testing on GitHub Actions 5 years ago
.gitignore skip literals used in constant expressions 4 years ago
AUTHORS set up an AUTHORS file to attribute copyright 4 years ago
CHANGELOG.md CHANGELOG: start drafting for v0.2.0 3 years ago
CONTRIBUTING.md CONTRIBUTING: include some basic terminology (#188) 4 years ago
LICENSE set up an AUTHORS file to attribute copyright 4 years ago
README.md README: refactor and include more useful information 3 years ago
bench_test.go rework the build benchmarks 3 years ago
go.mod all: drop support for Go 1.15.x (#265) 3 years ago
go.sum all: drop support for Go 1.15.x (#265) 3 years ago
hash.go make flags like -literals and GOPRIVATE affect hashing (#288) 3 years ago
main.go obfuscate literals as part of transformGo (#299) 3 years ago
main_test.go handle unknown flags in reverse (#290) 3 years ago
position.go set positions when using cursor.Replace 3 years ago
reverse.go make reverse error if no changes were made 3 years ago
runtime_strip.go all: drop support for Go 1.15.x (#265) 3 years ago
shared.go make flags like -literals and GOPRIVATE affect hashing (#288) 3 years ago

README.md

garble

GO111MODULE=on go get mvdan.cc/garble

Obfuscate Go code by wrapping the Go toolchain. Requires Go 1.16 or later.

garble build [build flags] [packages]

See garble -h for up to date usage information.

Purpose

Produce a binary that works as well as a regular build, but that has as little information about the original source code as possible.

The tool is designed to be:

  • Coupled with cmd/go, to support modules and build caching
  • Deterministic and reproducible, given the same initial source code
  • Reversible given the original source, to deobfuscate panic stack traces

Mechanism

The tool wraps calls to the Go compiler and linker to transform the Go build, in order to:

  • Replace as many useful identifiers as possible with short base64 hashes
  • Replace package paths with short base64 hashes
  • Remove all build and module information
  • Strip filenames and shuffle position information
  • Strip debugging information and symbol tables via -ldflags="-w -s"
  • Obfuscate literals, if the -literals flag is given
  • Remove extra information, if the -tiny flag is given

By default, the tool obfuscates the packages under the current module. If not running in module mode, then only the main package is obfuscated. To specify what packages to obfuscate, set GOPRIVATE, documented at go help private.

Note that commands like garble build will use the go version found in your $PATH. To use different versions of Go, you can install them and set up $PATH with them. For example, for Go 1.16.1:

$ go get golang.org/dl/go1.16.1
$ go1.16.1 download
$ PATH=$(go1.16.1 env GOROOT)/bin:${PATH} garble build

Literal obfuscation

Using the -literals flag causes literal expressions such as strings to be replaced with more complex variants, resolving to the same value at run-time. This feature is opt-in, as it can cause slow-downs depending on the input code.

Literal expressions used as constants cannot be obfuscated, since they are resolved at compile time. This includes any expressions part of a const declaration.

Tiny mode

When the -tiny flag is passed, extra information is stripped from the resulting Go binary. This includes line numbers, filenames, and code in the runtime that prints panics, fatal errors, and trace/debug info. All in all this can make binaries 2-5% smaller in our testing.

Note: if -tiny is passed, no panics, fatal errors will ever be printed, but they can still be handled internally with recover as normal. In addition, the GODEBUG environmental variable will be ignored.

Speed

garble build should take about twice as long as go build, as it needs to complete two builds. The original build, to be able to load and type-check the input code, and finally the obfuscated build.

Go's build cache is fully supported; if a first garble build run is slow, a second run should be significantly faster. This should offset the cost of the double builds, as incremental builds in Go are fast.

Determinism and seeds

Just like Go, garble builds are deterministic and reproducible if the inputs remain the same: the version of Go, the version of Garble, and the input code. This has significant benefits, such as caching builds or being able to use garble reverse to de-obfuscate stack traces.

However, it also means that an input package will be obfuscated in exactly the same way if none of those inputs change. If you want two builds of your program to be entirely different, you can use -seed to provide a new seed for the entire build, which will cause a full rebuild.

If any open source packages are being obfuscated, providing a custom seed can also provide extra protection. It could be possible to guess the versions of Go and garble given how a public package was obfuscated without a seed.

Caveats

Most of these can improve with time and effort. The purpose of this section is to document the current shortcomings of this tool.

  • Exported methods are never obfuscated at the moment, since they could be required by interfaces and reflection. This area is a work in progress.

  • It can be hard for garble to know what types will be used with reflection, including JSON encoding or decoding. If your program breaks because a type's names are obfuscated when they should not be, you can add an explicit hint:

    type Message struct {
    	Command string
    	Args    string
    }
    
    // Never obfuscate the Message type.
    var _ = reflect.TypeOf(Message{})
    
  • Go plugins are not currently supported; see #87.

Contributing

We welcome new contributors. If you would like to contribute, see CONTRIBUTING.md as a starting point.