前言
某日早上, Sunskey 正喝着咖啡,悠闲的听着小曲(不存在的,苦逼写代码)。领导火急火燎的走到了我面前,咱们的加密工具 .so 出现了安全隐患,需要清除符号表,提升安全性。我一脸的懵逼 “什么是符号表“。但是还是毫无犹豫的答应了“好的”。经过简单的查阅相关资料,对符号表有了一个基本认识。
一. 符号和符号表
1.1 什么是符号表
程序编译成可执行文件后,文件中会有一个专门的表用来保存函数名,变量名,段名和代码或者数据的对应关系,这个表就是符号表(编译阶段产生的)。
ELF文件中通常会有两张符号表。一张叫符号表(.symtab) ,另一张叫动态符号表(.dynsym),一般只用对符号表(.symtab) 进行去除的工作。
ELF(Executable and Linkable Format)即可执行可链接文件格式,目前常见的Linux、 Android可执行文件、共享库(.so)、目标文件( .o)以及Core 文件(吐核)均为此格式。
符号表类型 | 说明 |
---|---|
.symtab | 包含大量的信息(包括全局符号global symbols) |
.dynsym | 只保留.symtab中的全局符号 |
1.2 符号表的作用
1.收集符号属性
编译程序扫描说明部分收集有关标识符的属性,并在符号表中建立符号的相应的属性信息。
1 | 例如:int A; float B[5]; |
2.上下文语义的合法性检查
同一个标识符可能出现在不同的地方,需要在上下文中对标识符的属性进行一致性和合法性的检查。
1 | 例如:int A[3,5]; float A[3,5]; |
3.作为目标代码生成阶段地址分配的依据
符号变量需要由它符号类型以及次序确定该变量存储在符号表的哪个区域。
1 | 例如:常见的区域有公共区(extern)、文件静态区(extern static)、函数静态区(函数中static)、还是函数运行时的动态区(auto)等。 |
1.3 符号表的种类
Global symbols(模块内部定义的全局符号) 由主模块定义并能够被其他模块引用的符号。
External symbols(外部定义的全局符号) 由其他模块定义并被主模块引用全局符号。
Local symbols(本模块的局部符号) 仅由主模块定义和引用的本地符号。
二. 怎么去除符号表
2.1 strip 命令
strip 经常用来去除目标文件(.so、.a、可执行程序等)中的一些符号表、调试符号表信息,以减小静态库、动态库和程序的大小。
使用方法:strip 文件名
通过ls -l 命令可知道原文件的大小。
通过file 命令可知,原文件是否经过stripped过,也就是strip 处理过的。
通过nm 命令,可查看原文件的符号表。
2.2 链接阶段使用-s 和 -S参数
-s:用于去除所有符号信息
-S:用于去除调试符号信息
也可以直接在GCC中通过 -Wl,-s 和-Wl,-S来移除符号信息。
三 . 总结
符号表基本只在编译阶段和链接阶段有用,移除符号表能够减少程序的大小也能提高一定的安全性能,但是移除符号表之后没办法调试,程序出了问题后,给定位问题带来了一定的难度。可以根据自己的实际情况,去选择是否需要去除符号表。