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 2 ↵
2019/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 2 ↵
GO_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 2 ↵
GO_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的大小。
参考链接
- Golang二进制文件混淆保护
- cmd/link: delete source file path info in panic‘s stack trace for production releases #13809
- github.com/upx/upx