一个最小x86 ELF Hello World程序的诞生

来源:互联网 发布:mac下载iphone应用 编辑:程序博客网 时间:2024/06/05 16:06


注:这里的最小是指我能做到的

最终大小: 142字节

介绍

这篇文章可以算是我在Ubuntu Linux上尝试创建一个最小的x86 ELF二进制Hello World文件的记录,你也可以把它当作一篇指南,我的尝试先是从c开始,然后转向x86汇编,最后以16进制编辑器搞定,但我的最终成果实际上只能打印"Hi World",这纯粹是为了让最终的数字看着更顺眼一些而已,最终的x86 ELF二进制虽然已经被破坏的不成样子,但最重要的是它仍然可以照常运行。

开始

  • 如果你也想跟我一起来试试,那你要做的第一件事就应该是先配置环境:
  • 安装Ubuntu (或随便别的你喜欢的发行版)
  • 运行: sudo apt-get install g++ gcc nasm
  • 查看系统版本
user@computer:~$ lsb_release -aNo LSB modules are available.Distributor ID: UbuntuDescription:    Ubuntu 8.04.1Release:        8.04Codename:       hardyuser@computer:~$ uname -aLinux ryanh-desktop 2.6.24-19-generic #1 SMP Wed Jun 18 14:43:41 UTC 2008 i686 GNU/Linuxuser@computer:~$ gcc --versiongcc (GCC) 4.2.3 (Ubuntu 4.2.3-2ubuntu7)Copyright (C) 2007 Free Software Foundation, Inc.This is free software; see the source for copying conditions.  There is NOwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.user@computer:~$ nasm -versionNASM version 0.99.06-20071101 compiled on Nov 15 2007

我的尝试从C开始,下面是我写的C程序,chello.c


  1. #include <STDIO.H>
  2. int main()
  3. {
  4. printf ("Hi World\n");
  5. return0;
  6. }

编译:

user@computer:~$ gcc -o chello chello.cuser@computer:~$ ./chelloHi World

我最初得到的可执行文件大小为6363字节,你可以使用readelf来查看ELF文件的头信息,命令:

user@computer:~$ readelf -h chelloELF Header:  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00  Class:                             ELF32  Data:                              2's complement, little endian  Version:                           1 (current)  OS/ABI:                            UNIX - System V  ABI Version:                       0  Type:                              EXEC (Executable file)  Machine:                           Intel 80386  Version:                           0x1  Entry point address:               0x80482f0  Start of program headers:          52 (bytes into file)  Start of section headers:          3220 (bytes into file)  Flags:                             0x0  Size of this header:               52 (bytes)  Size of program headers:           32 (bytes)  Number of program headers:         7  Size of section headers:           40 (bytes)  Number of section headers:         36  Section header string table index: 33

ldd也是个很有用的命令,它可以显示这个c程序链接了哪些动态库:

user@computer:~$ ldd chello    linux-gate.so.1 =>  (0xb7f77000)    libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7e18000)    /lib/ld-linux.so.2 (0xb7f78000)

file命令可以告诉你这个文件的基本信息

user@computer:~$ file chellochello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.8, dynamically linked (uses shared libs), not stripped

我们看到file命令返回了"not stripped",这就是说这个二进制包含了用于调试的符号信息,让我们使用strip命令给它做个瘦身:

  1. user@computer:~$ strip -s chello

经过瘦身之后,现在这个二进制的大小变成了2984字节,还是没法接受,是时候做出艰难决定了,于是我放弃了C以及printf,转而使用nasm x86汇编,下面是hello.asm:

        SECTION .datamsg:db "Hi World",10len:equ $-msgSECTION .text        global mainmain:movedx,lenmovecx,msgmovebx,1moveax,4int0x80movebx,0moveax,1int0x80

编译

user@computer:~$ nasm -f elf hello.asmuser@computer:~$ gcc -o hello hello.o -nostartfiles -nostdlib -nodefaultlibsuser@computer:~$ strip -s hellouser@computer:~$ ./helloHi World

strip之前是770字节,之后是448字节,但是这个二进制仍然包含一些无用的头部和section信息,现在找个你最顺手的16进制编辑器打开这个二进制,我一般用curses hexeditor和ghex2。

删掉0xAD后面的内容后,现在大小变成了173字节

可以看到0x7后面有一块无用空间,所有我们把保存Hi World字符串的数据块从0xA4-0xAC移到了0x7然后把0x86对字符串的引用从0xA4改为新的地址0x7,最后删除0xA2和0xA3.

现在文件大小应该变成了164字节,是时候进入最终环节了,剩下的部分我需要做些解释,基本上,我要做的就是不断尝试改变ELF的头部,但是避免出现segfault fault,我加了许多jmp并完全破坏了原本的可执行文件,尽管如此,它还是可以运行的,这里是一些有用的技巧: 在x86汇编中 0xD9D0,也就是nop操作符,如果你需要填充空白时这个指令很有用,另外0xEB后面跟一个有符号字节来完成相对跳转,你真的应该看看intel x86的汇编指令文档A-MN-Z。

view plaincopy to clipboardprint?
  1. typedef struct {
  2. unsigned char e_ident[EI_NIDENT];
  3. Elf32_Half e_type;
  4. Elf32_Half e_machine;
  5. Elf32_Word e_version;
  6. Elf32_Addr e_entry;
  7. Elf32_Off e_phoff;
  8. Elf32_Off e_shoff;
  9. Elf32_Word e_flags;
  10. Elf32_Half e_ehsize;
  11. Elf32_Half e_phentsize;
  12. Elf32_Half e_phnum;
  13. Elf32_Half e_shentsize;
  14. Elf32_Half e_shnum;
  15. Elf32_Half e_shtrndx;
  16. } Elf32_Ehdr;

结论

最终大小: 142 字节

helloworld.tar.gz

我确信肯定还有办法让它更小,应该还是可以从头部移掉一些没用的数据,但是我不想花太多时间钻研ELF的头部格式,另一个办法或许是使用a.out格式来代替ELF格式。

如果你有意见,建议或是批评欢迎给我邮件:henszey#gmail.com

-----------
本文来自:"Smallest x86 ELF Hello World",作者:henszey


原创粉丝点击