【翻译】如何让Linux ELF文件由4KB缩减至45B!
字数 973 2025-08-23 18:31:34
如何将Linux ELF文件从4KB缩减至45B - 深入解析与优化指南
1. 初始探索:从C语言到汇编
1.1 基础C程序分析
我们从最简单的C程序开始:
/* tiny.c */
int main(void) {
return 42;
}
编译后文件大小:
- 普通编译:3998字节
- 使用
-s去除符号表:2632字节 - 添加
-O3优化:2616字节
1.2 转向汇编语言
使用NASM编写等效汇编代码:
; tiny.asm
BITS 32
GLOBAL main
SECTION .text
main:
mov eax, 42
ret
编译后文件大小:2604字节(仅减少12字节)
1.3 绕过main()接口
直接使用_start入口点:
; tiny.asm
BITS 32
GLOBAL _start
SECTION .text
_start:
mov eax, 42
ret
编译选项:
-nostartfiles:不使用标准系统启动文件- 但会导致段错误,因为
_start不是函数,没有返回地址
2. 正确使用系统调用退出
2.1 使用_exit()函数
; tiny.asm
BITS 32
EXTERN _exit
GLOBAL _start
SECTION .text
_start:
push dword 42
call _exit
编译后文件大小:1340字节
2.2 完全脱离标准库
使用-nostdlib选项,但需要自己实现系统调用:
; tiny.asm
BITS 32
GLOBAL _start
SECTION .text
_start:
mov eax, 1 ; exit系统调用号
mov ebx, 42 ; 返回值
int 0x80 ; 触发系统调用
编译后文件大小:372字节
3. 优化汇编代码
3.1 原始机器码分析
00000000 B801000000 mov eax, 1
00000005 BB2A000000 mov ebx, 42
0000000A CD80 int 0x80
3.2 优化后的代码
xor eax, eax ; 清空eax
inc eax ; eax = 1
mov bl, 42 ; 只设置bl寄存器
int 0x80
使用ld直接链接:
- 文件大小:368字节(由于对齐填充)
4. 深入ELF文件格式
4.1 ELF文件结构
ELF文件由以下部分组成:
- ELF头(52字节)
- 程序头表(可选)
- 节头表(可选)
- 实际程序数据
4.2 手动构造最小ELF
BITS 32
org 0x08048000
ehdr: ; Elf32_Ehdr
db 0x7F, "ELF", 1, 1, 1, 0 ; e_ident
times 8 db 0
dw 2 ; e_type
dw 3 ; e_machine
dd 1 ; e_version
dd _start ; e_entry
dd phdr -
$$
; e_phoff
dd 0 ; e_shoff
dd 0 ; e_flags
dw ehdrsize ; e_ehsize
dw phdrsize ; e_phentsize
dw 1 ; e_phnum
dw 0 ; e_shentsize
dw 0 ; e_shnum
dw 0 ; e_shstrndx
ehdrsize equ $ - ehdr
phdr: ; Elf32_Phdr
dd 1 ; p_type
dd 0 ; p_offset
dd
$$
; p_vaddr
dd
$$
; p_paddr
dd filesize ; p_filesz
dd filesize ; p_memsz
dd 5 ; p_flags
dd 0x1000 ; p_align
phdrsize equ $ - phdr
_start:
mov bl, 42
xor eax, eax
inc eax
int 0x80
filesize equ $ -
$$
编译后文件大小:91字节
5. 极限优化技巧
5.1 重叠ELF头和程序代码
将代码嵌入ELF头的未使用部分:
BITS 32
org 0x08048000
ehdr:
db 0x7F, "ELF" ; e_ident
db 1, 1, 1, 0, 0
_start:
mov bl, 42
xor eax, eax
inc eax
int 0x80
dw 2 ; e_type
; ... 其余ELF头字段
文件大小:84字节
5.2 重叠ELF头和程序头表
BITS 32
org 0x08048000
ehdr:
db 0x7F, "ELF" ; e_ident
db 1, 1, 1, 0, 0
_start:
mov bl, 42
xor eax, eax
inc eax
int 0x80
dw 2 ; e_type
dw 3 ; e_machine
dd 1 ; e_version
dd _start ; e_entry
dd phdr -
$$
; e_phoff
phdr:
dd 1 ; e_shoff ; p_type
; ... 重叠的字段
文件大小:76字节
5.3 最终优化版本
BITS 32
org 0x00010000
db 0x7F, "ELF" ; e_ident
dd 1 ; p_type
dd 0 ; p_offset
dd
$$
; p_vaddr
dw 2 ; e_type ; p_paddr
dw 3 ; e_machine
dd _start ; e_version ; p_filesz
dd _start ; e_entry ; p_memsz
dd 4 ; e_phoff ; p_flags
_start:
mov bl, 42 ; e_shoff ; p_align
xor eax, eax
inc eax ; e_flags
int 0x80
db 0
dw 0x34 ; e_ehsize
dw 0x20 ; e_phentsize
db 1 ; e_phnum ; e_shentsize ; e_shnum ; e_shstrndx
文件大小:45字节(绝对最小值)
6. 关键优化技术总结
- 绕过标准启动代码:使用
-nostartfiles和-nostdlib - 直接系统调用:避免链接任何库
- 寄存器优化:使用部分寄存器(如bl而非ebx)
- ELF结构重叠:
- 代码嵌入ELF头
- 程序头表与ELF头重叠
- 利用未检查字段:
- 使用非标准加载地址
- 利用p_flags的可选位
- 文件截断:依赖Linux的零填充行为
7. 实际应用注意事项
- 这种极端优化主要用于研究目的
- 实际应用中应考虑:
- 可读性和可维护性
- 兼容性(不同Linux版本可能有不同行为)
- 安全性(过度紧凑可能被误认为恶意代码)
通过这种方法,我们成功将Linux ELF可执行文件从最初的4KB缩减到了仅45字节,同时保持其完整功能。