Go语言编译的二进制在Panic之后,会泄露一些开发者的信息,某一些信息或许你是期望它们出现在客户的环境上的,那么这里有一些有用的方式去避免它。

1)原始输出信息

写一小段代码:

A. github.com/scue/test/module/module.go

1
2
3
4
5
6
7
package module

import "log"

func Mod() {
	log.Panicln("Panic in module")
}

B. github.com/scue/test/main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

import (
	"log"

	"github.com/scue/test/module"
)

func main() {
	log.Println("Hello World!")
	module.Mod()
}

C. 尝试运行一下它

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$ go run main.go                                                                                                                                                                     22019/10/03 11:00:22 Hello World!
2019/10/03 11:00:22 Panic in module
panic: Panic in module


goroutine 1 [running]:
log.Panicln(0xc0000a7f78, 0x1, 0x1)
        /usr/local/Cellar/go/1.12/libexec/src/log/log.go:347 +0xac
github.com/scue/test/module.Mod(...)
        /Users/scue/go/src/github.com/scue/test/module/module.go:6
main.main()
        /Users/scue/go/src/github.com/scue/test/main.go:15 +0xa2
exit status 2

嗯…,什么信息都暴露出去了~

2)GOPATH路径混淆

按照文章Golang二进制文件混淆保护,做了一些混淆工作

A. 写成Makefile文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
ACTUAL_GOPATH:=$(GOPATH)
export GOPATH=/tmp/.go
export GOROOT_FINAL:=$(GOPATH)
GOCONFUSE := $(shell set -eu \
	&& [ ! -e ${GOPATH} ] && ln -sv $(ACTUAL_GOPATH) $(GOPATH) \
	&& [[ ! $(PATH) =~ ${GOPATH} ]] && export PATH=$(PATH):${GOPATH}/bin \
)
t1:
	GO_ENABLED=0 go build -ldflags '-s -w -extldflags "-static"' -o bin/main .
	./bin/main

B. 那么make t1会输出什么呢?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$ make t1                                                                                                                                                                            2GO_ENABLED=0 go build -ldflags '-s -w -extldflags "-static"' -o bin/main .
./bin/main
2019/10/03 11:05:40 Hello World!
2019/10/03 11:05:40 Panic in module
panic: Panic in module


goroutine 1 [running]:
log.Panicln(0xc00009ff78, 0x1, 0x1)
        /tmp/.go/src/log/log.go:347 +0xac
github.com/scue/test/module.Mod(...)
        /tmp/.go/src/github.com/scue/test/module/module.go:6
main.main()
        /tmp/.go/src/github.com/scue/test/main.go:15 +0xa2
make: *** [t1] Error 2

此时此刻已经把GOPATH信息给隐藏着了,不过还是有一些小问题,凡人一看github.com/scue/test还是能猜出一些信息来。

3)import 路径混淆

这里需要一个工具——gnused(linux下只直接调用sed,而macOS是gsed,brew install gun-sed以安装)

A. 往Makefile添加一些信息

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# import PATH路径混淆
PATH_REAL=github.com/scue/test
PATH_FAKE=.XkkvX
ifndef PATH_FAKE
$(error PATH_FAKE is not set)
endif
PATHCONFUSE := $(shell set -eu \
	echo  $(GOPATH)/src/$(PATH_FAKE); \
	test -e $(GOPATH)/src/$(PATH_FAKE) && rm -rf $(GOPATH)/src/$(PATH_FAKE); \
	cp -a $(GOPATH)/src/$(PATH_REAL) $(GOPATH)/src/$(PATH_FAKE) \
	&& find $(GOPATH)/src/$(PATH_FAKE) -name '*.go' -exec gsed -i 's:$(PATH_REAL):$(PATH_FAKE):g' {} ';' \
)
SRCDIR=$(GOPATH)/src/$(PATH_FAKE)

B. 完整的makefile如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
SRCDIR=.
BINDIR=bin

# GOPATH混淆
ACTUAL_GOPATH:=$(GOPATH)
export GOPATH=/tmp/.go
export GOROOT_FINAL:=$(GOPATH)
GOCONFUSE := $(shell set -eu \
	&& [ ! -e ${GOPATH} ] && ln -sv $(ACTUAL_GOPATH) $(GOPATH) \
	&& [[ ! $(PATH) =~ ${GOPATH} ]] && export PATH=$(PATH):${GOPATH}/bin \
)

# import PATH路径混淆
PATH_REAL=github.com/scue/test
PATH_FAKE=.XkkvX
ifndef PATH_FAKE
$(error PATH_FAKE is not set)
endif
PATHCONFUSE := $(shell set -eu \
	echo  $(GOPATH)/src/$(PATH_FAKE); \
	test -e $(GOPATH)/src/$(PATH_FAKE) && rm -rf $(GOPATH)/src/$(PATH_FAKE); \
	cp -a $(GOPATH)/src/$(PATH_REAL) $(GOPATH)/src/$(PATH_FAKE) \
	&& find $(GOPATH)/src/$(PATH_FAKE) -name '*.go' -exec gsed -i 's:$(PATH_REAL):$(PATH_FAKE):g' {} ';' \
)
SRCDIR=$(GOPATH)/src/$(PATH_FAKE)

t1:
	GO_ENABLED=0 go build -ldflags '-s -w -extldflags "-static"' -o bin/main $(SRCDIR)
	./bin/main

C. 那么make t1会输出什么信息呢?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$ make t1                                                                                                                                                                            2GO_ENABLED=0 go build -ldflags '-s -w -extldflags "-static"' -o bin/main /tmp/.go/src/.XkkvX
./bin/main
2019/10/03 11:12:25 Hello World!
2019/10/03 11:12:25 Panic in module
panic: Panic in module


goroutine 1 [running]:
log.Panicln(0xc000099f78, 0x1, 0x1)
        /tmp/.go/src/log/log.go:347 +0xac
.XkkvX/module.Mod(...)
        /tmp/.go/src/.XkkvX/module/module.go:6
main.main()
        /tmp/.go/src/.XkkvX/main.go:15 +0xa2
make: *** [t1] Error 2

此时此刻,输出信息就会更进一步的隐藏了仓库的信息。

4)UPX压缩二进制

为了便于二进制的分发,可以使用UPX进行压缩一下,压缩的同时也有一个好处就是,常规的grep命令搜索不到变量的名字,举例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import (
	"log"

	"github.com/scue/test/module"
)

var (
	secretKey = "01234567890"
)

func main() {
	log.Println("Hello World!")
	log.Printf("secret key:%s", secretKey)
	module.Mod()
}

这里定义了一个变量secretKey,然后编译它GO_ENABLED=0 go build -ldflags '-s -w -extldflags "-static"' -o bin/main .,得到bin/main,然后使用grep搜索一下

1
2
$ grep secretKey -rn bin
Binary file bin/main matches

那么,如果使用upx压缩了之后呢?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ upx bin/main
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2018
UPX 3.95        Markus Oberhumer, Laszlo Molnar & John Reiser   Aug 26th 2018

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
   1670744 ->    626704   37.51%   macho/amd64   main                          

Packed 1 file.
$ grep secretKey -rn bin # 没有输出

这样子的操作,可以让它减少了直接被扫描到特定字符串导致程序被破解的可能(安全是相对的,这只是稍微增加了一点难度),同时也可以看到二进制文件从1.6MB变成了626KB,体积只有约1/3的大小。

参考链接

  1. Golang二进制文件混淆保护
  2. cmd/link: delete source file path info in panic‘s stack trace for production releases #13809
  3. github.com/upx/upx