目的
编写一个最小的 ELF 程序,来加强对 ELF 文件格式的掌握和理解。(参考:《程序员的自我修养》一书)
源代码
这里采用 GCC 内置汇编代码的编写来避免 libc 中自带的库函数代码。
- 汇编代码采用 AT&T 格式
char *str="H3ll0\n";
void print(){
asm(
"movl $6,%%edx \n\t"
"movl %0,%%ecx \n\t"
"movl $0,%%ebx \n\t"
"movl $4,%%eax \n\t"
"int $0x80 \n\t"
::"r"(str):"edx","ecx","ebx"
);
}
void exit(){
asm(
"movl $42,%ebx \n\t"
"movl $1,%eax \n\t"
"int $0x80 \n\t"
);
}
void nomain(){
print();
exit();
}
这里采用系统调用号来进行系统调用。
参考:http://syscalls.kernelgrok.com/
编译
这里需要编译成 32 位的程序,在 64 位机器下需要使用下面的命令:
gcc -c -m32 -fno-builtin tiny.c
ld -m elf_i386 -static -e nomain -o tiny tiny.o
参考:https://blog.csdn.net/neuq_jtxw007/article/details/78112672
执行、查看大小
1112 个字节。还可以继续削减他的大小。
自定义程序段
先查看他有哪些段,再决定去掉哪些无用的段。
nick@nick-machine:~/testelf$ readelf -S ./tiny
There are 9 section headers, starting at offset 0x2f0:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 08048094 000094 000042 00 AX 0 0 1
[ 2] .rodata PROGBITS 080480d6 0000d6 000007 00 A 0 0 1
[ 3] .eh_frame PROGBITS 080480e0 0000e0 00007c 00 A 0 0 4
[ 4] .data PROGBITS 0804915c 00015c 000004 00 WA 0 0 4
[ 5] .comment PROGBITS 00000000 000160 000035 01 MS 0 0 1
[ 6] .shstrtab STRTAB 00000000 0002ae 000042 00 0 0 1
[ 7] .symtab SYMTAB 00000000 000198 0000e0 10 8 7 4
[ 8] .strtab STRTAB 00000000 000278 000036 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
9 个段,首先 text 、rodata、data
这三个段可能是需要的,我们可以把他合成为一个段(代码和数据混合),这样就可以缩减描述各自属性段的字节大小。eh_frame
和 comment
可以直接去除。
-
eh_frame
是用于调试的段,没有这个段在 gdb 中调试会出错。
自定义 ld 脚本
通过自定义 ld 脚本的方法,来控制 ld 链接器的行为。
脚本如下:
ENTRY(nomain)
SECTIONS{
. = 0x8048000+SIZEOF_HEADERS;
tinytext : { *(.text) *(.data) *(.nodata) }
/DISCARD/ : { *(.comment) *(.eh_frame)}
}
编译
nick@nick-machine:~/testelf$ gcc -m32 -c ./tiny.c -fno-builtin -o tiny.o
nick@nick-machine:~/testelf$ ld -static -m elf_i386 -T tiny.lds ./tiny.o -o tiny
运行之后还是正常的。
查看段信息,这是被合并成了一个段 tinytext
,权限是 WAX。
nick@nick-machine:~/testelf$ readelf -S tiny
There are 6 section headers, starting at offset 0x190:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .rodata PROGBITS 08048074 000074 000007 00 A 0 0 1
[ 2] tinytext PROGBITS 0804807c 00007c 000048 00 WAX 0 0 4
[ 3] .shstrtab STRTAB 00000000 000162 00002c 00 0 0 1
[ 4] .symtab SYMTAB 00000000 0000c4 000080 10 5 4 4
[ 5] .strtab STRTAB 00000000 000144 00001e 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
查看大小
640 个字节,还可以使用 strip 来去除 symbol 信息。
nick@nick-machine:~/testelf$ ls -al ./tiny
-rwxrwxr-x 1 nick nick 640 5月 7 16:40 ./tiny
这里就只有 384 字节。
nick@nick-machine:~/testelf$ strip tiny
nick@nick-machine:~/testelf$ ls -al ./tiny
-rwxrwxr-x 1 nick nick 384 5月 7 16:45 ./tiny
应该还能再削减大小,先填坑。。
总结
用这个大小的程序来进行 ELF 文件格式分析,会简单很多