Commit Graph

96 Commits

Author SHA1 Message Date
pagran
ea4a01df87 More correct comments transformation (#152)
More correct comments transformation was implemented.

Added processing of //go:linkname localname [importpath.name] directive, now localname is not renamed. This is safe and does not cause a name disclosure because the functions marked //linkname do not have a name in the resulting binary.

Added cgo directives support

Fixed filename leak protection for cgo

Part of #149
2020-10-05 16:12:25 +03:00
pagran
991fbb042b avoid potential short name collisions
Fix for bug when a conflict occurred between generated short names
and local variables/functions/types/structs.

The already existing names are collected and if the generated short name
already exists, the package counter is increased until a free name is found.

Part of #149.
2020-09-30 16:44:07 +01:00
pagran
434df0476d fixed comments cleaning
Added cleanup of the Comment field.
In some cases, the appearance of a comment in a random place
may break the compilation (e.g. cgo and runtime package).

This is safe because the Comment field cannot contain any directives.

Part of #149.
2020-09-29 20:09:39 +01:00
Daniel Martí
c3bee46a26 testdata: use the debugdir flag less often
In tiny.txt, we already check line numbers via stderr, so there's no
need to do that via -debugdir.

In syntax.txt, we only really care about what names remain in the
binary, not the names which remain in the source but don't affect the
binary.

These changes are important because -debugdir adds a non-trivial amount
of work, which will impede build caching once that feature lands. We
will likely make -debugdir support build caching eventually, but for
now, this preliminary change will make 'go test' much faster with build
caching.

And of course, the tests get simpler, which is nice.
2020-09-22 14:56:21 +01:00
pagran
00c1d5b11d add test for Go version checking (#140)
Add tests for Go version checking

Fix panic if go version has invalid format

Fixes: #121
Co-authored-by: Daniel Martí <mvdan@mvdan.cc>
2020-09-22 16:24:08 +03:00
pagran
90fa325da7 Rewrite renaming logic for private names and reduce length of public names (#135)
1. Now private names are obfuscated based on the counter in scope of the package.
2. The length of public names is reduced to 4 bytes.
2020-09-12 17:36:16 +03:00
Andrew LeFevre
0d182a3dbd remove unnecessary data from runtime if -tiny is passed
Fixes #127. Saves an additional ~1-2% binary size in my testing.
2020-09-08 22:06:37 +01:00
Andrew LeFevre
d679944408 Strip all filename and position info when -tiny is passed (#128)
Co-authored-by: pagran <pagran@protonmail.com>
Co-authored-by: lu4p <lu4p@pm.me>
2020-09-07 17:24:40 +02:00
lu4p
d8d784639f Validate the user provided seed. (#126)
Also allow base64 seeds without padding.

Fixes #123.
2020-09-06 15:23:26 +02:00
Daniel Martí
f764467e9b all: update the docs a bit
Rework the features section in the README, leaving optional features at
the end of the list. Simplify the caveats list, too; the build cache and
exported field/method bits only need one point each. Overall, the
section was far too wordy for little reason.

Also redo the help text a bit. There's now a line to briefly introduce
the tool, as well as a link to the README with all the details. Finally,
the flags have shorter and more consistent help strings.

While at it, remove two unused global vars as spotted by staticcheck.
2020-09-06 13:32:00 +01:00
Andrew LeFevre
c8d61c772f Garble imports and package paths in GOPRIVATE (#116)
Finally, finally this is done. This allows import paths to be obfuscated by modifying
object/archive files and garbling import paths contained within. The bulk of the
code that makes parsing and writing Go object/archive files possible lives at
https://github.com/Binject/debug/tree/master/goobj2, which I wrote as well.

I have tested by garbling and checking for import paths via strings and grep
(in order of difficulty) https://github.com/lu4p/binclude, garble itself, and
https://github.com/dominikh/go-tools/tree/master/cmd/staticcheck.

This only supports object/archive files produced from the Go 1.15 compiler.
The object file format changed at 1.15, and 1.14 and earlier is not supported.

Fixes #13.
2020-09-05 22:29:10 +01:00
Andrew LeFevre
30df5e9bbd Disable plugin test (#120)
* disable plugin test for now, add note to README

* add link to issue in README

* fix README link to issue
2020-09-02 14:00:13 -04:00
pagran
bd46c29380 add blacklist for runtime std packages 2020-08-25 17:22:53 +02:00
lu4p
388ff7d1a4 remove buggy number literal obfuscation
Also remove boolean literal obfuscation.
2020-08-23 19:33:54 +02:00
Daniel Martí
d3af58b558 complain when GOPRIVATE matches no packages
This is important, because it would mean that we would obfuscate
nothing. At best, it would be confusing; at worst, it could mislead
the user into thinking the binary is obfuscated.

Fixes #20.
Updates #108.
2020-08-20 11:51:55 +02:00
Daniel Martí
81b4c49702 move the "missing -trimpath" test to the slow group (#106)
This shouldn't break often, so it doesn't need to be covered by 'go test
-short'. Moreover, it's still a relatively expensive step, since we end
up reaching package compilation.
2020-08-20 11:51:55 +02:00
Daniel Martí
511779d8ff testdata: set GOPRIVATE in all but two tests (#104)
basic.txt just builds main.go without a module. Similarly, we leave
imports.txt without a GOPRIVATE, to test the 'go list -m' fallback.

For all other tests, explicitly set GOPRIVATE, to avoid two exec calls -
both 'go env GOPRIVATE' as well as 'go list -m'. Each of those calls
takes in the order of 10ms, so saving ~26 exec calls should easily add
to 200-300ms saved from 'go test -short'.
2020-08-20 11:51:55 +02:00
lu4p
ea51e78283 Check that all files use LF line endings in CI 2020-08-20 11:51:55 +02:00
Daniel Martí
75e904f6d4 various minor cleanups and fixes (#99)
Error strings should never be capitalized.

A binsubstr line in one of the tests was duplicate and thus useless.

Remove duplicate or trailing spaces in test scripts.

Finally, add a TODO for an optimization I just spotted.
2020-08-14 22:46:26 +02:00
pagran
2735555ab2 Update filename and add line number obfuscation (#94)
Fixes  #2.

Line numbers are now obfuscated, via `//line` comments.
Filenames are now obfuscated via `//line` comments, instead of changing the actual filename.
New flag `-tiny` to reduce the binary size, at the cost of reversibility.
2020-08-14 20:47:15 +02:00
lu4p
7df14ad860 Fix reflect detection if -literals is passed.
Fixes #93.

The second typecheck lead to the creation of different type objects,
which didn't match the types in the blacklist anymore.

It turns out we don't need the second typecheck,
therfore it is now removed.
2020-08-14 20:16:01 +02:00
Daniel Martí
cdac2cd3d6 testdata: avoid fmt in the implement test script
Like other tests, importing fmt results in quite a lot of extra work,
due to the lack of build caching.

In this particular test, we wanted fmt.Println so that T.String would be
called in an indirect way, without defining or referencing Stringer
interface in the main package.

We can do that by rolling our own "tinyfmt" package in a dozen or so
lines of code.

Below is how 'go test -short -vet=off -run Script/implement' is
affected, measured via benchcmd and benchstat:

	name                   old time/op         new time/op         delta
	GoTestScriptImplement          3.67s ± 9%          2.65s ±11%  -27.68%  (p=0.008 n=5+5)

	name                   old user-time/op    new user-time/op    delta
	GoTestScriptImplement          8.18s ± 4%          4.55s ± 9%  -44.35%  (p=0.008 n=5+5)

	name                   old sys-time/op     new sys-time/op     delta
	GoTestScriptImplement          1.27s ±12%          0.71s ±13%  -44.07%  (p=0.008 n=5+5)

	name                   old peak-RSS-bytes  new peak-RSS-bytes  delta
	GoTestScriptImplement          145MB ± 1%          145MB ± 2%     ~     (p=1.000 n=5+5)

All in all, we shave about one full second. It doesn't seem to affect
the total 'go test -short' noticeably, but every little bit counts.
2020-08-14 19:26:55 +02:00
pagran
28adbaa73b Randomize operator (xor, add, subtract) on all obfuscators (#90)
Co-authored-by: lu4p <lu4p@pm.me>
2020-08-12 03:23:43 +02:00
pagran
2eba744530 Add XorSeed obfuscator (#86)
Co-authored-by: lu4p <lu4p@pm.me>
2020-08-11 00:46:08 +02:00
Daniel Martí
aa9767b0d2 add a regression test for #82
The test case we had didn't have a realistic-looking module path with a
dot, so we hadn't noticed the bug with IndexByte.

Fix that. We verified that the new test fails if we undo the fix.
2020-08-10 23:07:30 +02:00
Daniel Martí
d0e01478f0 keep build flags when calling 'go list'
Otherwise any build flags like -tags won't be used, and we might easily
end up with errors or incorrect packages.

The common case with -tags is covered by one of the integration test
scripts. On top of that, we add a table-driven unit test to cover all
edge cases, since there are many we can do quickly in a unit test.

Fixes #82.
2020-08-10 23:04:55 +02:00
Daniel Martí
7fe0bf4787 simplify the main code flow somewhat
We don't really care about tools other than "compile" and "link". Stop
trying to keep a complete list.

Use "if err := f(); err != nil {" where it makes sense.

Simplify some declarations, and use a better variable name than "fW".
2020-08-10 12:49:01 +02:00
pagran
9c25f4c2b2 Add xorShuffle obfuscator (#85)
* Refactoring

* Rename Xor2 to XorShuffle
2020-08-09 12:05:47 +02:00
pagran
c51e08ef37 Add split obfuscator (#81) 2020-08-03 22:08:33 +02:00
Daniel Martí
65461aabce reuse a single 'go list -json -export -deps' call
Instead of doing a 'go list' call every time we need to fetch a
dependency's export file, we now do a single 'go list' call before the
build begins. With the '-deps' flag, it gives us all the dependency
packages recursively.

We store that data in the gob format in a temporary file, and share it
with the future garble sub-processes via an env var.

This required lazy parsing of flags for the 'build' and 'test' commands,
since now we need to run 'go list' with the same package pattern
arguments.

Fixes #63.
2020-07-31 21:10:43 +01:00
pagran
c2079ac0a1 Add test for literal obfuscators (#80)
* Combine literals-all-obfuscators.txt nad literals.txt
Rewrite literals.txt logic

* Remove unused \s

* Refactoring and add float ast helpers
2020-07-31 20:23:51 +02:00
lu4p
21c67b91b1 Only obfuscate required identifiers (#79)
The following identifiers are now skipped,
because they never show up in the binary:

- constant identifiers
- identifiers of local variables
(includes function params and named returns)
- identifiers of local types
2020-07-31 18:42:30 +02:00
lu4p
50d24cdf51 Add float, int, and boolean literal obfuscation.
Add ast helper functions to reduce ast footprint.

Add binsubfloat and binsubint functions for testing.

Fixes #55.
2020-07-19 17:17:34 +01:00
lu4p
705f9d3a28 Fix byte array and untyped constant obfuscation.
Byte arrays were previously,
obfuscated as byte slices.

Untyped constants are now skipped,
because they cannot be replaced with typed variables.
2020-07-17 14:39:05 +01:00
lu4p
d48bdbadae Use XOR instead of AES for literal obfuscation.
Implement a literal obfuscator interface,
to allow the easy addition of new encodings.

Add literal obfuscation for byte literals.

Choose a random obfuscator on literal obfuscation,
useful when multiple obfuscators are implemented.

Fixes #62
2020-07-16 20:19:22 +01:00
lu4p
b3616f19c4 fix implementedOutsideGo, fixes #56 (#59)
Injected functions were mistaken for functions implemented outside go.

Asm functions:
obj.Scope().Pos() == 0
obj.Scope().End() == 0

Injected functions:
obj.Scope().Pos() == 0
obj.Scope().End() == 1

We now check for the End instead of the Pos.
2020-07-13 01:31:26 +02:00
Andrew LeFevre
7ede37cc0b add runtime API to suppress printing fatal errors
Fixes #50.
2020-07-09 16:42:13 +01:00
lu4p
f1bf6f91ee skip literals used in constant expressions
Fixes #39.
2020-07-06 08:49:21 +01:00
Daniel Martí
3e4f3821ea don't leak build version information via a const either
This requires a bit of extra magic to replace one constant in
runtime/internal/sys, but that was simple enough given that we can reuse
a lot of the code to parse the files and write them to a temporary dir.

We can also drop the -X flags, as runtime.buildVersion is based on the
constant that we replace here.

Fixes #44, again.
2020-06-28 22:01:12 +01:00
Daniel Martí
649cc2f6ba strip Go version information from the binary too
Fixes #44.
2020-06-20 16:24:07 +01:00
Daniel Martí
c7d1fc7c60 strip buildid information from linked binaries
Otherwise, one can use 'go tool buildid' to obtain the main package's
build ID, which can make de-obfuscating the main package much simpler.

Fixes #43.
2020-06-19 18:36:26 +01:00
lu4p
234174b418 don't obfuscate some literals which might break typechecking 2020-06-17 20:51:06 +01:00
lu4p
4c64b13506 make -seed=random use the same random seed for all packages
Otherwise, a different random seed per package will break imported names.
2020-06-14 17:47:26 +01:00
lu4p
0cf8d4e7a6 add seed flag to control how builds are reproducible
Fixes #26.
2020-06-13 20:50:10 +01:00
Daniel Martí
5604a2aa9e avoid importing fmt in strings test
Reduces its 'go test -short' time from ~3s to ~2.4s on my laptop, since
we have to compile fewer dependencies.
2020-06-13 00:45:42 +01:00
Daniel Martí
a09b197fe2 remove the code to handle a nil file.Imports
I could not reproduce the supposed panic, even after I was able to reach
a nil x.Imports in that line in question with the modified test.
2020-06-13 00:26:04 +01:00
lu4p
dd1fc4ed87 don't replace all consts with vars
In some cases, such as iotas or when constants are later required to be constants,
we could break compilation. Be more conservative.

Fixes #32.
2020-06-12 23:44:57 +01:00
Nicholas Jones
ecbcc61a62 handle embedded struct fields with universe scope
Whilst it may not be particularly common, it is legal to embed fields
where the type has universe scope (e.g. int, error, etc). This can
cause a panic in 2 difference places:

- When embedding `error`, a named type is resolved but the package is
nil. The call to `pkg.Name()` results in a panic
- When embedding a basic type such as `int`, no named type is resolved
at all. The call to `namedType(obj.Type()).Obj()` results in a panic

I'm assuming it is OK to return early when a named type cannot be
resolved.. we could let it continue but I think `pkg` should be set to
nil to be correct, so it'd end up returning straight away anyway.
2020-06-10 21:19:18 +01:00
Daniel Martí
04e8beed32 testdata: add sections to scripts/test.txt 2020-06-09 17:47:16 +01:00
lu4p
65ceb9b7ca allow easy inpection of garbled code
Fixes #17.
2020-06-07 22:49:34 +01:00