Smallest x86 ELF Hello World

其实这篇文章没说多少东西,就是帮助我们熟悉ELF文件格式以及熟悉各种工具的使用!

 

(That I could achieve)

Final size: 142 bytes

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

最终大小: 142字节

Intro

介绍

This page is a combination tutorial/documentary about my attempts at creating the smallest x86 ELF binary that would execute saying Hello World on Ubuntu Linux. My first attempts started with C then progressed to x86 assembly and finally to a hexeditor. I ended up compromising and switching to a "Hi World" app instead in order to fit the string data into the elf magic number. The final result is a completely corrupted x86 ELF Binary that still runs.

这篇文章可以算是我在Ubuntu Linux上尝试创建一个最小的x86 ELF二进制Hello World文件的记录,你也可以把它当作一篇指南,我的尝试先是从c开始,然后转向x86汇编,最后以16进制编辑器搞定,但我的最终成果实际上只能打印"Hi World",这完全是根据ELF文件头中的魔数(magic number)所占的字节数来决定的,最终的x86 ELF二进制虽然已经被破坏的不成样子,但最重要的是它仍然可以照常运行。

From start to finish. 开始

 

The first thing you need to do is get an a proper environment setup.

  • Install Ubuntu (or a distro of your choice)
  • run: sudo apt-get install g++ gcc nasm
  • 如果你也想跟我一起来试试,那你要做的第一件事就应该是先配置环境:
  • 安装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

 

My first attempts started with C, the following is what I used for chello.c

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

Code: chello.c
#include 
int main() {
  printf ("Hi World\n");
  return 0;
}
Command: gcc
user@computer:~$ gcc -o chello chello.c
user@computer:~$ ./chello 
Hi World
My initial executable was 6363 bytes.You can use readelf to dump the ELF header from the executable.

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

Command: readelf
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 is useful for showing all the dynamic libraries an executable is linked to.

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

Command: ldd
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 will give you a description of what a file is.

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

Command: 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


 

The "not stripped" returned from the file command means that the debugging symbols haven't been stripped from the excutable.

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

Command: strip
user@computer:~$ strip -s chello


After stripping the executable was now 2984 bytes, still unacceptable! Time to take drastic measures... I scratched the C attempt and dropped using printf, instead opting for nasm x86 assembly. 

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

file: 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


Compiling the asm
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


Before stripping the file was 770 bytes after stripping 448 bytes. However there is still useless headers and sections to destroy.Open the binary in your favorite hex editor, I use the curses hexeditor and ghex2.

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

Smallest x86 ELF Hello World_第1张图片Delete everything including and past offset 0xAD, this will drop it down to 173 bytes

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

Smallest x86 ELF Hello World_第2张图片  Smallest x86 ELF Hello World_第3张图片Move 0xA4-0xAC to 0x7 and Change offset 0x86 from 0xA4 to its new location 0x07. Delete 0xA2 and 0xA3

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

Smallest x86 ELF Hello World_第4张图片The file should be 164 bytes and now its time to enter the twilight zone... The rest is a lot to explain, basically I attempted to find what I could change in the elf head with out having it segfault on me.I added some jmps and completely corrupted the executable, however it still runs :). Here is some useful information: In x86 0xD9D0 is nop or no operation, useful for just filling space if you need to. 0xEB followed by a single signed byte is a relative jmp. Really you should read the intel docs on x86 instructions A-M N-Z .

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

 

Smallest x86 ELF Hello World_第5张图片

Conclusion.

结论

Final size: 142 bytes

helloworld.tar.gz

I am certain that there are ways to get it even smaller. There may also be more things that can be removed from the header to increase size, but I didn't spend the enough time fully researching the ELF header format. Another option might be to use the a.out format instead of ELF may allow you to get even smaller.

Comments, suggestions, and critical criticism accepted: [email protected]

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

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

 

参考地址:http://timelessname.com/elfbin/     http://blog.csdn.net/fisher_jiang/article/details/6892036


 



 

你可能感兴趣的:(C语言基础)