静态链接学习之 ELF 文件 DIY

时间:2021-04-07 08:12:35

目的

编写一个最小的 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 个字节。还可以继续削减他的大小。

静态链接学习之 ELF 文件 DIY

自定义程序段

先查看他有哪些段,再决定去掉哪些无用的段。

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_framecomment 可以直接去除。

  • 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 文件 DIY

用这个大小的程序来进行 ELF 文件格式分析,会简单很多