一个最小x86 ELF Hello World程序的诞生
by yuanyi
at 2011-10-19 22:57:39
original http://item.feedsky.com/~feedsky/heikezhi/~8608072/577089957/6713895/1/item.html
注:这里的最小是指我能做到的
最终大小: 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 -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 8.04.1 Release: 8.04 Codename: hardy user@computer:~$ uname -a Linux ryanh-desktop 2.6.24-19-generic #1 SMP Wed Jun 18 14:43:41 UTC 2008 i686 GNU/Linux user@computer:~$ gcc --version gcc (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 NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. user@computer:~$ nasm -version NASM version 0.99.06-20071101 compiled on Nov 15 2007
我的尝试从C开始,下面是我写的C程序,chello.c
#include int main() { printf ("Hi World\n"); return0; }
编译:
user@computer:~$ gcc -o chello chello.c user@computer:~$ ./chello Hi World
我最初得到的可执行文件大小为6363字节,你可以使用readelf来查看ELF文件的头信息,命令:
user@computer:~$ readelf -h chello ELF 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 chello chello: 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命令给它做个瘦身:
user@computer:~$ strip -s chello
经过瘦身之后,现在这个二进制的大小变成了2984字节,还是没法接受,是时候做出艰难决定了,于是我放弃了C以及printf,转而使用nasm x86汇编,下面是hello.asm:
SECTION .data msg: db "Hi World",10 len: equ $-msg SECTION .text global main main: mov edx,len mov ecx,msg mov ebx,1 mov eax,4 int 0x80 mov ebx,0 mov eax,1 int 0x80
编译
user@computer:~$ nasm -f elf hello.asm user@computer:~$ gcc -o hello hello.o -nostartfiles -nostdlib -nodefaultlibs user@computer:~$ strip -s hello user@computer:~$ ./hello Hi 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-M N-Z。
typedef struct { unsigned char e_ident[EI_NIDENT]; Elf32_Half e_type; Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; Elf32_Off e_phoff; Elf32_Off e_shoff; Elf32_Word e_flags; Elf32_Half e_ehsize; Elf32_Half e_phentsize; Elf32_Half e_phnum; Elf32_Half e_shentsize; Elf32_Half e_shnum; Elf32_Half e_shtrndx; } Elf32_Ehdr;
结论
最终大小: 142 字节
我确信肯定还有办法让它更小,应该还是可以从头部移掉一些没用的数据,但是我不想花太多时间钻研ELF的头部格式,另一个办法或许是使用a.out格式来代替ELF格式。
如果你有意见,建议或是批评欢迎给我邮件:henszey#gmail.com
-----------
本文来自:"Smallest x86 ELF Hello World",作者:henszey
想和我们一道传播黑客精神?快来加入吧!
无觅猜您也喜欢: | |||
Hello world! |
破解我吧:一次ELF文件的解构之旅 |
debugCSS: 超简单的CSS/(x)html诊断工具 |
轻松为任意程序代码添加语法高亮 |
无觅 |