通过简单的一些 Case,通过实际操作对 ELF32-i386 文件做概要分析。

使用 Docker 来统一整体的开发、测试环境,适用于 Linux、Windows、MacOS(包括 Intel、M1 芯片)。

ELF Header 回顾

image

ELF 中,最核心的字段如上标注:

  • Program Header 的起始地址 0x34,其实和 ELF Header 的大小 e_ehsize 是一样的,也就是说 Pargram Header 是紧跟在 ELF Header 之后的
    • 数量 * 大小 = 0x02 * 0x20 = 64 字节
    • 所以地址范围为: 0x340x74
  • Section Header 的起始地址 0x1024
    • 数量 * 大小 = 0x03 * 0x28 = 120 字节
    • 所以地址范围为 0x10240x109c

Program

从 ELF Header 中获取 Program Header 的位置之后,就可以得到以下 Header 的具体信息

  • 第一个 Load 的 offset 为 0x0,FileSiz 为 0x74,就是 ELF Header + Program Header 的总长度
  • 第二个 Load 的 offset 为 0x1000,FileSize 为 0x11,就是真实程序执行地址
    • 由于程序一般运行在保护模式下,所以会有虚拟地址的映射,页就是运行时的地址,即 0x804900
$ readelf -l ./plus

Elf file type is EXEC (Executable file)
Entry point 0x8049000
There are 2 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x08048000 0x08048000 0x00074 0x00074 R   0x1000
  LOAD           0x001000 0x08049000 0x08049000 0x00011 0x00011 R E 0x1000

Section

从 ELF Header 中获取 Section Header 的位置之后,就可以得到以下 Header 的具体信息。

Section 的信息是从后往前引用的。

  • 第一个 Section 为空
  • 第二个 Section 指向真实的主程序代码地址
  • 第三个 Section 指向 StringTable
    • 地址范围 0x10110x1022
    • StringTable 的分别是给 Section Name 命名的
$ readelf -S ./plus
There are 3 section headers, starting at offset 0x1024:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        08049000 001000 000011 00  AX  0   0 16
  [ 2] .shstrtab         STRTAB          00000000 001011 000011 00      0   0  1

.data

使用 .data section 来打印 Hello World

section	.text
   global _start     ;must be declared for linker (ld)

_start:	            ;tells linker entry point

   mov	eax,4       ; system call number (sys_write),
                     ; https://www.tutorialspoint.com/assembly_programming/assembly_system_calls.htm
                     ; https://en.wikipedia.org/wiki/Write_(system_call)

   mov	ebx,1       ; file descriptor (stdout)
                     ; https://en.wikipedia.org/wiki/File_descriptor

   mov	ecx,msg     ;message to write
   mov	edx,len     ;message length
   int	0x80        ;call kernel
                     ; https://en.wikipedia.org/wiki/X86_instruction_listings
                     ; https://stackoverflow.com/questions/1817577/what-does-int-0x80-mean-in-assembly-code 80 是调用 Linux 内核

   mov	eax,1       ;system call number (sys_exit)
   int	0x80        ;call kernel

section	.data
   msg   db   'Hello, world!'    ;string to be printed
   len   equ  $ - msg            ;length of the string, $ 表示当前地址 msg 表示 msg 的地址,相减即为 msg 的长度

References

  1. elf-1