garble icon indicating copy to clipboard operation
garble copied to clipboard

Condition obfuscation [idea]

Open pagran opened this issue 4 years ago • 11 comments

I propose to actively use lambdas and channels since they generate a lot of boilerplate code in the output binary. For example, you can obfuscate any if stmt:

Original code:

if os.Getpid()%2 == 0 || os.Getpid()%3 == 0 {
	println("lucky pid", os.Getpid())
} else {
	println("unlucky pid", os.Getpid())
}

Step 1:

if (func() bool {
	return os.Getpid()%2 == 0 || os.Getpid()%3 == 0
})() {
	println("lucky pid", os.Getpid())
} else {
	println("unlucky pid", os.Getpid())
}

Step 2:

if (func() bool {
	c := make(chan bool, 2)
	go func() {
		c <- os.Getpid()%2 == 0
		c <- os.Getpid()%3 == 0
	}()
	return <-c || <-c
})() {
	println("lucky pid", os.Getpid())
} else {
	println("unlucky pid", os.Getpid())
}

Just try load output binaru into ida pro ;)

pagran avatar Jul 13 '20 09:07 pagran

How would you implement this in a way that doesn't blow up the binary size, compile time, and run-time speed of the program? We definitely can't do this for all statements. Would you just do a small portion of statements at random? Or would you depend on some sort of annotation like "please make this statement harder to deobfuscate"?

mvdan avatar Jul 13 '20 09:07 mvdan

I like the idea of ​​explicitly indicating which expressions to obfuscate. For example, we can obfuscate the logic of checking a license without regard to speed and size, but do not touch the recursive calculation of the number of fibonacci

he-he, goroutines quite expensive

BenchmarkOriginal
BenchmarkOriginal-24          	13812631	        77.4 ns/op
BenchmarkStep1
BenchmarkStep1-24             	14489564	        80.0 ns/op
BenchmarkStep2
BenchmarkStep2-24             	  526202	      2803 ns/op
BenchmarkStep2Optimized
BenchmarkStep2Optimized-24    	 2433956	       504 ns/op

https://gist.github.com/pagran/29cfbd713119e7395b12ed134145be91

pagran avatar Jul 13 '20 09:07 pagran

I'd be fine with some form of annotation. Here are two ideas:

  1. special comments, like:
//go:garble obfuscate-statement
someHiddenValue = ...
  1. special no-op APIs, like:
garble.ObfuscateBlock(func() {
    someHiddenValue = ...
}())

With option 2, we would replace the func call with the logic to hide the code inside the anonymous func.

mvdan avatar Jul 13 '20 09:07 mvdan

In theory, no-op APIs should be more flexible. They allow you to wrap multi-line logic.

garble.ObfuscateBlock(func() {
// open license file
// check rsa signature
//  something else
}) // I think () are not needed, let api guarantee a lambda call

pagran avatar Jul 13 '20 09:07 pagran

I can continue the idea:

  1. Add protection levels
  2. Add special methods for string (and other default types)
  3. Add obfuscated resources (not sure)

Something like:

garble.ObfuscateBlock{Low,...,Ultra}(func() {
// logic
})

gargble.ObfuscateString{Low,Hight,Ultra}("Some string") // return string type

gargble.LoadFile("assets/logo.png")

On ultra we can generate megabytes trash code :)

pagran avatar Jul 13 '20 19:07 pagran

I wonder if options like -literals could be replaced by such annotations too. It certainly feels more powerful, given that you can narrow down your needs. It seems less useful if one wants to obfuscate all strings, but I wonder if that's something we should encourage to begin with.

An option in the code is the route we took with hiding panics with @capnspacehook, though that is just a single global setting.

mvdan avatar Jul 13 '20 19:07 mvdan

I think the function "obfuscate all lines" may be convenient for typical functions where it makes no sense to manually manage obfuscation (error handling, logs, etc.), but it should be as lightweight as possible

pagran avatar Jul 13 '20 19:07 pagran

The only problem I see with no-op APIs, is that it won't allow wrapping top-level declarations. For example, if I wanted to obfuscate several function declarations in a file, wrapping it in another function call is illegal syntax. I know that garble would replace the calls etc and the code could compile cleanly, but this would disallow any other tools from operating on the source code, such as gofmt for example.

Example code:


package main

import (
	"fmt"
)

garble.ObfuscateBlock{
	func foo() { fmt.Println("foo") }

	func bar() { fmt.Println("bar") }

	func baz() { fmt.Println("baz") }
}

func main() {
	fmt.Println("Hello, playground")
}

capnspacehook avatar Jul 14 '20 15:07 capnspacehook

Well, you could do one of:

var secretVar

func init() { garble.ObfuscateBlock(func() { secretVar = ... }) }

func foo() { garble.ObfuscateBlock(func() { body ... }) }

Not ideal, but still better than using comments everywhere, I think.

mvdan avatar Jul 14 '20 15:07 mvdan

Why do you prefer that syntax over using comments? To me, comments could be more flexible. We could allow block comments to annotate blocks of code without changing the source code much:


//garble:blockobfuscate
var secretVar ...

func init() { secretVar = ... }

func foo() { ... }
//garble:end

capnspacehook avatar Jul 14 '20 17:07 capnspacehook

@capnspacehook because that feels like inventing our own syntax on top of Go's syntax, almost like a meta-syntax that the user has to learn and remember. I would like to avoid that. Comment directives that just affect the following node/statement are OK-ish, but anything more than that feels too icky.

Go already has ways to group syntax: top-level declarations can use (, and statements can use {. So your example above could be like:

//garble:whatever
var (
    ...
    ...
)

//garble:whatever
func init() { secretVar = ... }

The alternative is pure Go syntax like garble.ObfuscateBlock, but then we force the user to move global variable assignments to init statements, which isn't super nice. Perhaps we could instead have them do:

var secretVar = garble.ObfuscateValue(expression...)

I'm thinking outloud here. I don't have a strong opinion overall, but I would like to stick to "plain Go" syntax as much as possible, i.e. ideally without magic comments.

mvdan avatar Jul 20 '20 11:07 mvdan