当前位置: 首页 > news >正文

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 <stdio.h>
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 stripping448 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。

  • Delete everything including and past offset 0xAD, this will drop it down to 173 bytes

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

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

  • 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;
    
    

     

  • 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: henszey@gmail.com

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

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

     

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


     



     

相关文章:

  • 使用vim编辑可执行文件--16进制模式
  • Understanding ELF using readelf and objdump
  • C语言字节对齐
  • C语言可变长参数函数与默认参数提升
  • Essential GNU Linker Concepts for Embedded Systems Programmers
  • A Sample Linker Script
  • Load-time relocation of shared libraries
  • Position Independent Code (PIC) in shared libraries
  • Position Independent Code (PIC) in shared libraries on x64
  • getchar()函数的思考与总结
  • sturct stat 结构体中 st_mode 的含义
  • 环境变量及其函数
  • BIOS和DOS建立的中断向量表
  • CMOS RAM 各字节的含义
  • ubuntu命令行方式启动
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • dva中组件的懒加载
  • es6(二):字符串的扩展
  • js对象的深浅拷贝
  • JS基础之数据类型、对象、原型、原型链、继承
  • Leetcode 27 Remove Element
  • PAT A1017 优先队列
  • PermissionScope Swift4 兼容问题
  • PHP 7 修改了什么呢 -- 2
  • Phpstorm怎样批量删除空行?
  • python docx文档转html页面
  • 翻译--Thinking in React
  • 新版博客前端前瞻
  • 一个普通的 5 年iOS开发者的自我总结,以及5年开发经历和感想!
  • 1.Ext JS 建立web开发工程
  • #{} 和 ${}区别
  • #git 撤消对文件的更改
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • (4)事件处理——(7)简单事件(Simple events)
  • (C#)一个最简单的链表类
  • (DenseNet)Densely Connected Convolutional Networks--Gao Huang
  • (剑指Offer)面试题34:丑数
  • (最全解法)输入一个整数,输出该数二进制表示中1的个数。
  • **CI中自动类加载的用法总结
  • *p=a是把a的值赋给p,p=a是把a的地址赋给p。
  • .mat 文件的加载与创建 矩阵变图像? ∈ Matlab 使用笔记
  • .NET 5种线程安全集合
  • .net oracle 连接超时_Mysql连接数据库异常汇总【必收藏】
  • .Net Web窗口页属性
  • .Net 访问电子邮箱-LumiSoft.Net,好用
  • .NET 跨平台图形库 SkiaSharp 基础应用
  • .NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • .NetCore项目nginx发布
  • .net中生成excel后调整宽度
  • @SuppressWarnings注解
  • [Asp.net mvc]国际化
  • [C++]二叉搜索树
  • [C++]运行时,如何确保一个对象是只读的
  • [ComfyUI进阶教程] animatediff视频提示词书写要点