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.
garble/README.md

151 lines
6.3 KiB
Markdown

# garble
go install mvdan.cc/garble@latest
Obfuscate Go code by wrapping the Go toolchain. Requires Go 1.18 or later.
garble build [build flags] [packages]
The tool also supports `garble test` to run tests with obfuscated code,
and `garble reverse` to de-obfuscate text such as stack traces.
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 de-obfuscate 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
* Replace filenames and position information with short base64 hashes
* Remove all [build](https://go.dev/pkg/runtime/#Version) and [module](https://go.dev/pkg/runtime/debug/#ReadBuildInfo) information
* Strip debugging information and symbol tables via `-ldflags="-w -s"`
* [Obfuscate literals](#literal-obfuscation), if the `-literals` flag is given
* Remove [extra information](#tiny-mode), if the `-tiny` flag is given
The tool obfuscates the packages matching `GOGARBLE`, a comma-separated list of
glob patterns of module path prefixes, as documented in `go help private`.
To obfuscate all the packages in a build, use `GOGARBLE=*`.
When `GOGARBLE` is empty, it assumes the value of `GOPRIVATE`.
When `GOPRIVATE` is also empty, then `GOGARBLE` assumes the value of the current
module path, to obfuscate all packages under the current module.
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](https://go.dev/doc/manage-install#installing-multiple)
and set up `$PATH` with them. For example, for Go 1.17.1:
```sh
$ go install golang.org/dl/go1.17.1@latest
$ go1.17.1 download
$ PATH=$(go1.17.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 expressions, resolving to the same value at run-time.
This feature is opt-in, as it can cause slow-downs depending on the input code.
Literals used in constant expressions cannot be obfuscated, since they are
resolved at compile time. This includes any expressions part of a `const`
declaration, for example.
### Tiny mode
With the `-tiny` flag, even more information is stripped from the Go binary.
Position information is removed entirely, rather than being obfuscated.
Runtime code which prints panics, fatal errors, and trace/debug info is removed.
All in all, this can make binaries 2-5% smaller.
With this flag, no panics or fatal runtime 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.
Note that this flag can make debugging crashes harder, as a panic will simply
exit the entire program without printing a stack trace, and all source code
positions are set to line 1. Similarly, `garble reverse` is generally not useful
in this mode.
### 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 then the obfuscated build.
Garble obfuscates one package at a time, mirroring how Go compiles one package
at a time. This allows Garble to fully support Go's build cache; incremental
`garble build` calls should only re-build and re-obfuscate modified code.
Note that the first call to `garble build` may be comparatively slow,
as it has to obfuscate each package for the first time. This is akin to clearing
`GOCACHE` with `go clean -cache` and running a `go build` from scratch.
### Determinism and seeds
Just like Go, garble builds are deterministic and reproducible in nature.
This has significant benefits, such as caching builds and being able to use
`garble reverse` to de-obfuscate stack traces.
By default, garble will obfuscate each package in a unique way,
which will change if its build input changes: the version of garble, the version
of Go, the package's source code, or any build parameter such as GOOS or -tags.
This is a reasonable default since guessing those inputs is very hard.
However, providing your own obfuscation seed via `-seed` brings some advantages.
For example, builds sharing the same seed will produce the same obfuscation,
even if any of the build parameters or versions vary.
It can also make reverse-engineering harder, as an end user could guess what
version of Go or garble you're using.
Note that extra care should be taken when using custom seeds.
If a seed used to build a binary gets lost, `garble reverse` will not work.
Rotating the seeds can also help against reverse-engineering in the long run,
as otherwise some bits of code may be obfuscated the same way over time.
An alternative approach is `-seed=random`, where each build is entirely different.
### 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. This area is a work in progress; see
[#3](https://github.com/burrowers/garble/issues/3).
* Garble aims to automatically detect which Go types are used with reflection,
as obfuscating those types might break your program.
Note that Garble obfuscates [one package at a time](#speed),
so if your reflection code inspects a type from an imported package,
and your program broke, you may need to add a "hint" in the imported package:
```go
type Message struct {
Command string
Args string
}
// Never obfuscate the Message type.
var _ = reflect.TypeOf(Message{})
```
* Go declarations exported for cgo via `//export` are not obfuscated.
* Go plugins are not currently supported; see [#87](https://github.com/burrowers/garble/issues/87).
### Contributing
We welcome new contributors. If you would like to contribute, see
[CONTRIBUTING.md](CONTRIBUTING.md) as a starting point.