ELF程序介绍
ELF是Linux系统上标准的可执行文件、目标文件、共享库的格式 ,ELF文件由文件头(ELF Header)和多个节(Sections)或段(Segments)组成。
其中ELF文件头描述整个文件的属性,节(Sections)用于链接阶段,包含代码、数据、符号表等元数据,段(Segments)用于执行阶段,将多个节组合成内存映射单元。
编译方式
我们知道了elf格式的程序的组成结构,那么编译成elf格式的方式有哪些呢?
elf程序包括静态编译、动态编译、交叉编译、调试或优化编译等,一般情况下,静态编译和动态编译方式是比较常见的,接下来我们主要介绍这两种方式的区别。
静态编译
基础介绍
静态编译是指在编译阶段,将程序所需的所有库(如C标准库、第三方库等)全部打包进最终生成的可执行文件中。
编译示例代码:gcc main.c -o main_static -static
文件特点
- 文件较大: 生成的 ELF 可执行文件体积较大,因为包含了所有依赖库的代码。
- 独立性强: 程序运行时不再依赖外部的动态库(.so),只需操作系统支持即可运行。
- 不包含
.interp
段: 不需要动态链接器(如libc.so.6
)来加载。 - 不包含
.dynamic
: 没有动态链接相关的信息。

- 启动速度快: 没有动态链接器加载和解析共享库符号的开销。
- 可移植性强:适合在没有安装相关库的环境下运行。
- 更新库时,需重新编译程序才能获得新库的功能或修复。
动态编译
基础介绍
在编译链接阶段,不将库代码复制到可执行文件中,而是记录下程序运行所依赖的共享库(.so
文件)的名字和所需符号,程序启动或运行时,由动态链接器负责在内存中加载这些共享库。
文件特点
- 文件较小:生成的 ELF 可执行文件体积较小,只包含自身代码和库的引用信息。
- 包含
.interp
段: 指定了动态链接器的路径。 - 包含
.dynamic
段: 存储了动态链接信息等,如:- 依赖的共享库列表(
NEEDED
entries)。 - 重定位信息。
- 初始化/终止函数地址(
.init_array
,.fini_array
)。
- 依赖的共享库列表(

优缺点
- 程序运行时依赖于系统中已安装的动态库(.so),如果缺失则无法运行。
优点:
- 文件体积小:节省内存和磁盘空间,多个程序可共享同一份库。
- 内存占用低:多个程序共享同一个库的
.text
(代码)段,显著提高内存利用率。 - 库更新方便:无需重新编译程序即可获得新库的功能或修复。
缺点:
- 启动速度稍慢:程序启动时,动态链接器需要加载和链接共享库,有一定开销。
- 兼容性风险:若目标系统上的共享库版本与编译时链接的版本不兼容时,有可能导致程序崩溃或行为异常情况。
总结对比表
对比项 | 静态编译 | 动态编译 |
---|---|---|
可执行文件体积 | 大 | 小 |
运行依赖 | 无需依赖外部库 | 需依赖外部动态库 |
升级库影响 | 需重新编译 | 无需重新编译 |
ELF结构 | 无.interp 和.dynamic 段 | 有.interp 和.dynamic 段 |
资源占用 | 占用更多磁盘和内存 | 多程序可共享库,资源节省 |
区分编译方式
如果有编译好的elf文件时,如何区分该elf是动态编译还是静态编译呢?
一般情况下,我们只需要借助Linux上的系统命令readelf
工具即可查看,比如readelf -d demo
,查看是否有.dynamic 段。
如果有,就是动态编译,如图所示:

如果没用,就是静态编译,如图所示:

安全防范
安全问题
被逆向风险
虽然编译生成的二进制文件逆向分析难度较高,但由于相应的反编译工具(比如ida、Ghidra)也成熟强大,依然可以反编译为类C伪代码;
被调试风险
攻击者可通过调试工具附加应用进程进行调试,在调试过程中可能暴露敏感信息(如密钥、算法逻辑);
程序被篡改风险
攻击者可以通过修改应用内存改变程序行为,绕过安全检查或实现恶意功能,导致数据泄露;
调试符号暴漏
so库中包含调试符号(如函数名、变量名、函数地址),可能暴露敏感信息,则攻击者可以更容易地理解代码逻辑,对代码进行分析。
防范措施
Native程序又叫本地程序,通常指用C、C++等编译型语言开发,直接编译为特定平台机器码的可执行文件(如.exe、.so等),可以直接在操作系统上运行。
ELF格式属于Native程序的一种,Virbox Protector
工具在对ELF程序的保护上有着成熟的方案,可以实现对elf程序的函数级和整体保护,具体方案参考官网文档Native程序保护最佳实践。