一個最小x86 ELF Hello World程序的誕生


注:這裏的最小是指我能做到的

最終大小: 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


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

編譯:

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命令給它做個瘦身:

  1. 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

  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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章