在 golang中怎么对二进制文件进行压缩
创始人
2024-12-26 14:22:34
0次

环境:

$ go version
go version go1.11.2 linux/amd64

$ gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609

一. Go VS C 二进制

hello.go

package main
import "fmt"
func main() {
 fmt.Println("hello world")
}

hello.c

#include 
int main() {
 printf("hello world\n");
 return 0;
}
$ go build -o hello hello.go
$ go build -ldflags "-s -w" -o hello2 hello.go
$ gcc hello.c
$ ls -l
-rwxrwxr-x 1 zengxl zengxl 1902849 11月 27 15:40 hello
-rwxrwxr-x 1 zengxl zengxl 1353824 11月 27 15:43 hello2
-rwxrwxr-x 1 zengxl zengxl 8600 11月 27 15:44 a.out

golang 连接的参数:

$ go tool link -h

usage: link [options] main.o
-s disable symbol table  # 去掉符号表
-w disable DWARF generation # 去掉调试信息

ELF

先来看下 C 的:

$ readelf -h a.out 
ELF 头:
 Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
 类别:        ELF64
 数据:        2 补码,小端序 (little endian)
 版本:        1 (current)
 OS/ABI:       UNIX - System V
 ABI 版本:       0
 类型:        EXEC (可执行文件)
 系统架构:       Advanced Micro Devices X86-64
 版本:        0x1
 入口点地址:    0x400430
 程序头起点:   64 (bytes into file)
 Start of section headers:   6616 (bytes into file)
 标志:    0x0
 本头的大小:  64 (字节)
 程序头大小:  56 (字节)
 Number of program headers:   9
 节头大小:   64 (字节)
 节头数量:   31
 字符串表索引节头: 28
$ readelf -d a.out 

Dynamic section at offset 0xe28 contains 24 entries:
 标记  类型       名称/值
 0x0000000000000001 (NEEDED)    共享库:[libc.so.6]
 0x000000000000000c (INIT)    0x4003c8
 0x000000000000000d (FINI)    0x4005b4
 0x0000000000000019 (INIT_ARRAY)   0x600e10
 0x000000000000001b (INIT_ARRAYSZ)  8 (bytes)
 0x000000000000001a (FINI_ARRAY)   0x600e18
 0x000000000000001c (FINI_ARRAYSZ)  8 (bytes)
 0x000000006ffffef5 (GNU_HASH)   0x400298
 0x0000000000000005 (STRTAB)    0x400318
 0x0000000000000006 (SYMTAB)    0x4002b8
 0x000000000000000a (STRSZ)    61 (bytes)
 0x000000000000000b (SYMENT)    24 (bytes)
 0x0000000000000015 (DEBUG)    0x0
 0x0000000000000003 (PLTGOT)    0x601000
 0x0000000000000002 (PLTRELSZ)   48 (bytes)
 0x0000000000000014 (PLTREL)    RELA
 0x0000000000000017 (JMPREL)    0x400398
 0x0000000000000007 (RELA)    0x400380
 0x0000000000000008 (RELASZ)    24 (bytes)
 0x0000000000000009 (RELAENT)   24 (bytes)
 0x000000006ffffffe (VERNEED)   0x400360
 0x000000006fffffff (VERNEEDNUM)   1
 0x000000006ffffff0 (VERSYM)    0x400356
 0x0000000000000000 (NULL)    0x0

再来看下 go 的:

$ readelf -h hello
ELF 头:
 Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
 类别:        ELF64
 数据:        2 补码,小端序 (little endian)
 版本:        1 (current)
 OS/ABI:       UNIX - System V
 ABI 版本:       0
 类型:        EXEC (可执行文件)
 系统架构:       Advanced Micro Devices X86-64
 版本:        0x1
 入口点地址:    0x451fa0
 程序头起点:   64 (bytes into file)
 Start of section headers:   456 (bytes into file)
 标志:    0x0
 本头的大小:  64 (字节)
 程序头大小:  56 (字节)
 Number of program headers:   7
 节头大小:   64 (字节)
 节头数量:   13
 字符串表索引节头: 3

$ readelf -d hello

There is no dynamic section in this file.

The linker in the gc toolchain creates statically-linked binaries by default. All Go binaries therefore include the Go runtime, along with the run-time type information necessary to support dynamic type checks, reflection, and even panic-time stack traces.

A simple C “hello, world” program compiled and linked statically using gcc on Linux is around 750 kB, including an implementation of printf. An equivalent Go program using fmt.Printf weighs a couple of megabytes, but that includes more powerful run-time support and type and debugging information.

所以,为什么 go 二进制比 C 大很多就比较明显了。

golang 静态编译,不依赖动态库。

二. 如何减小 go 二进制文件大小

2.1. -ldflags

上面已经提到了过了。

$ go build -ldflags "-s -w" xxx.go

2.2. UPX

https://github.com/upx/upx

Commands:
 -1  compress faster     -9 compress better
 -d  decompress      -l list compressed file
 -t  test compressed file    -V display version number
 -h  give more help     -L display software license
Options:
 -q  be quiet       -v be verbose
 -oFILE write output to 'FILE'
 -f  force compression of suspicious files
 -k  keep backup files
file.. executables to (de)compress

Compression tuning options:
 --brute    try all available compression methods & filters [slow]
 --ultra-brute  try even more compression variants [very slow]

$ upx --brute binaryfile

IDA 逆向分析简单看下:

https://www.hex-rays.com/products/ida/support/download.shtml

下面是支持 Go 的 IDA helper

https://github.com/sibears/IDAGolangHelper

原始的 go 二进制文件:

可以看到 go 的一些函数名。

在 golang中怎么对二进制文件进行压缩

去掉符号表和调试信息的 go 二进制文件:

已经看不到函数名信息,只有类似 sub_47BF70 这样。

在 golang中怎么对二进制文件进行压缩

经过 upx 压缩的 go 二进制文件:

信息已经比较少了,入口点也发生了变化。

在 golang中怎么对二进制文件进行压缩

2.3. 压缩结果对比

$ go build -o hello hello.go
$ go build -ldflags "-s -w" -o hello-strip hello.go
$ upx --brute hello
$ ll -h
-rwxr-xr-x 1 aland aland 1.9M Dec 6 13:06 hello
-rwxr-xr-x 1 aland aland 809K Dec 6 13:07 hello-upx
-rwxr-xr-x 1 aland aland 1.3M Dec 6 13:06 hello-strip

相关内容

热门资讯

空调连续工作多久需要停机休息 空调连续工作的时间取决于多个因素,包括空调的型号、功率、使用环境和维护情况等。一般来说,空调可以连续...
空调哪个符号是除湿的 空调除湿功能通常用一个水滴形状的符号表示。不同品牌和型号的空调遥控器可能会有所差异,但一般来说,除湿...
格力空调多长时间加一次氟利昂 1、一般情况下,空调对氟利昂的使用量是非常小的,所以在空调使用了四年左右的时候再考虑加也没问题。2、...
空调用多长时间需要加氟一次 一般情况下是五年。当出现以下这几种情况时,空调才需要加氟。1、空调的室内机和室外机各有两个接口,当接...
格调生活·C标思享荟:卡萨帝在... 9月12日的郑州龙湖金融岛,夜风轻拂,灯光璀璨。“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑...
品牌价值破千亿、高端份额持续领... 9月12日,“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑州站”在郑州龙湖金融岛举办。2026...
空调主板坏了修800还是换新的 空调主板坏了是修还是换新,需要考虑多个因素,以下是一些建议:1. 维修成本:如果维修费用较低,比如 ...
空调开多久需要休息多长时间 问题:空调开多久需要休息多长时间回答:1、在使用空调的过程中,一般空调开7~8个小时左右需要休息2~...
中央空调多长时间清洗一次 1、一般空调开机前清洗一次即可,空调开机中间时段再清洗一次,空调关机时清洗一次,这样比较合理。2、空...
中央空调氟利昂多久加一次 看空调制热和制冷程度,很多人的观点认为氟利昂必须一年放一次,其实这个观点是错误的,理解上面有点偏差,...