如何使用十六进制编辑器在 Linux 中创建可执行 ELF 文件?

2024-10-14 08:40:00
admin
原创
68
摘要:问题描述:只是好奇。这显然不是实际编程的好解决方案,但假设我想在 Bless(十六进制编辑器)中制作一个可执行文件。我的架构是 x86。我可以编写一个非常简单的程序吗?一个 hello world?一个无限循环?与这个问题类似,但在 Linux 中。解决方案 1:反编译 NASM hello world 并理...

问题描述:

只是好奇。这显然不是实际编程的好解决方案,但假设我想在 Bless(十六进制编辑器)中制作一个可执行文件。

我的架构是 x86。我可以编写一个非常简单的程序吗?一个 hello world?一个无限循环?与这个问题类似,但在 Linux 中。


解决方案 1:

反编译 NASM hello world 并理解其中的每个字节

这个答案的版本具有很好的目录和更多内容:http://www.cirosantilli.com/elf-hello-world(此处达到 30k 个字符的限制)

标准

ELF 由 LSB 指定:

LSB 基本上与其他标准相链接,并进行了少量扩展,特别是:

  • 通用(均由 SCO 提供):

+ System V ABI 4.1 (1997) http://www.sco.com/developers/devspecs/gabi41.pdf,没有 64 位,尽管为其保留了一个魔法数字。核心文件也是如此。
+ System V ABI Update DRAFT 17 (2003) http://www.sco.com/developers/gabi/2003-12-17/contents.html,添加了 64 位。仅更新了上一文档的第 4 章和第 5 章:其他内容仍然有效并仍可引用。
  • 架构特定:

+ IA-32: http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-IA32/LSB-Core-IA32/elf-ia32.html,主要指向http://www.sco.com/developers/devspecs/abi386-4.pdf
+ AMD64:http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-AMD64/LSB-Core-AMD64/elf-amd64.html,主要指向http://www.x86-64.org/documentation/abi.pdf

您可以在此处找到一份便捷的摘要:

man elf

readelf可以通过和等实用程序以人类可读的方式检查其结构objdump

生成示例

让我们分解一个最小可运行的 Linux x86-64 示例:

section .data
    hello_world db "Hello world!", 10
    hello_world_len  equ $ - hello_world
section .text
    global _start
    _start:
        mov rax, 1
        mov rdi, 1
        mov rsi, hello_world
        mov rdx, hello_world_len
        syscall
        mov rax, 60
        mov rdi, 0
        syscall

编译自:

nasm -w+all -f elf64 -o 'hello_world.o' 'hello_world.asm'
ld -o 'hello_world.out' 'hello_world.o'

版本:

  • NASM 2.10.09

  • Binutils 版本 2.24(包含ld

  • Ubuntu 14.04

我们不使用 C 程序,因为那会使分析复杂化,那将是第 2 级 :-)

十六进制转储

hd hello_world.o
hd hello_world.out

输出于:https: //gist.github.com/cirosantilli/7b03f6df2d404c0862c6

全局文件结构

一个ELF文件包含以下几个部分:

  • ELF 头。指向节头表和程序头表的位置。

  • 节头表(可执行文件上可选)。每个表都有e_shnum节头,每​​个节头指向一个节的位置。

  • N 个部分,带有N <= e_shnum(可执行文件可选)

  • 程序头表(仅在可执行文件中)。每个程序都有e_phnum程序头,每个程序头都指向一个段的位置。

  • N 段,带有N <= e_phnum(可执行文件可选)

这些部分的顺序不是固定的:唯一固定的是 ELF 头,它必须是文件中的第一部分:通用文档说:

ELF 头

观察标题的最简单方法是:

readelf -h hello_world.o
readelf -h hello_world.out

输出于:https: //gist.github.com/cirosantilli/7b03f6df2d404c0862c6

目标文件中的字节数:

00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  01 00 3e 00 01 00 00 00  00 00 00 00 00 00 00 00  |..>.............|
00000020  00 00 00 00 00 00 00 00  40 00 00 00 00 00 00 00  |........@.......|
00000030  00 00 00 00 40 00 00 00  00 00 40 00 07 00 03 00  |....@.....@.....|

可执行文件:

00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 3e 00 01 00 00 00  b0 00 40 00 00 00 00 00  |..>.......@.....|
00000020  40 00 00 00 00 00 00 00  10 01 00 00 00 00 00 00  |@...............|
00000030  00 00 00 00 40 00 38 00  02 00 40 00 06 00 03 00  |....@.8...@.....|

代表结构:

typedef struct {
    unsigned char   e_ident[EI_NIDENT];
    Elf64_Half      e_type;
    Elf64_Half      e_machine;
    Elf64_Word      e_version;
    Elf64_Addr      e_entry;
    Elf64_Off       e_phoff;
    Elf64_Off       e_shoff;
    Elf64_Word      e_flags;
    Elf64_Half      e_ehsize;
    Elf64_Half      e_phentsize;
    Elf64_Half      e_phnum;
    Elf64_Half      e_shentsize;
    Elf64_Half      e_shnum;
    Elf64_Half      e_shstrndx;
} Elf64_Ehdr;

手动细分:

  • 0 0: EI_MAG= 7f 45 4c 46= 0x7f 'E', 'L', 'F': ELF 魔法数字

  • 0 4: EI_CLASS= 02= ELFCLASS64: 64 位精灵

  • 0 5: EI_DATA= 01= ELFDATA2LSB: 大端数据

  • 0 6: EI_VERSION= 01: 格式版本

  • 0 7:(EI_OSABI仅在 2003 年更新)= 00= ELFOSABI_NONE:没有扩展。

  • 0 8: EI_PAD= 8x 00:保留字节。必须设置为 0。

  • 1 0: e_type= 01 00= 1 (大端) = ET_REl: 可重定位格式

在可执行文件中它02 00ET_EXEC

  • 1 2: e_machine= 3e 00= 62= EM_X86_64: AMD64 架构

  • 1 4: e_version= 01 00 00 00: 必须为 1

  • 1 8:e_entry= 8x 00:执行地址入口点,如果不适用,则为 0,例如对于目标文件,因为没有入口点。

在可执行文件中,它是b0 00 40 00 00 00 00 00。TODO:我们还能将其设置为什么?内核似乎将 IP 直接放在该值上,它不是硬编码的。

  • 2 0: e_phoff=8x 00:程序头表偏移量,如果不存在则为0。

40 00 00 00在可执行文件中,即它在 ELF 头之后立即启动。

  • 2 8: e_shoff= 407x 00= 0x40:节头表文件偏移量,如果不存在则为 0。

  • 3 0:e_flags= 00 00 00 00TODO。特定于 Arch。

  • 3 4: e_ehsize= 40 00:此 elf 标头的大小。TODO 为什么是此字段?它如何变化?

  • 3 6: e_phentsize= 00 00:每个程序头的大小,如果不存在则为 0。

38 00可执行文件:长度为 56 字节

  • 3 8: e_phnum= 00 00:程序头条目的数量,如果不存在则为 0。

02 00在可执行文件中:有 2 个条目。

  • 3 A:e_shentsizee_shnum= 40 00 07 00:节头大小和条目数

  • 3 E: e_shstrndx( Section Header STRing iNDeX) = 03 00:部分索引.shstrtab

节标题表

结构数组Elf64_Shdr

每个条目包含有关给定部分的元数据。

e_shoffELF 头给出了起始位置,这里是 0x40。

e_shentsizee_shnumELF 标头来看,我们有 7 个条目,每个0x40条目长一个字节。

因此该表占用从 0x40 到0x40 + 7 + 0x40 - 10x1FF 的字节。

某些部分名称是为某些部分类型保留的:http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#special_sections例如.text需要SHT_PROGBITS类型和SHF_ALLOC+SHF_EXECINSTR

readelf -S hello_world.o

There are 7 section headers, starting at offset 0x40:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .data             PROGBITS         0000000000000000  00000200
       000000000000000d  0000000000000000  WA       0     0     4
  [ 2] .text             PROGBITS         0000000000000000  00000210
       0000000000000027  0000000000000000  AX       0     0     16
  [ 3] .shstrtab         STRTAB           0000000000000000  00000240
       0000000000000032  0000000000000000           0     0     1
  [ 4] .symtab           SYMTAB           0000000000000000  00000280
       00000000000000a8  0000000000000018           5     6     4
  [ 5] .strtab           STRTAB           0000000000000000  00000330
       0000000000000034  0000000000000000           0     0     1
  [ 6] .rela.text        RELA             0000000000000000  00000370
       0000000000000018  0000000000000018           4     2     4
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

struct每个条目代表:

typedef struct {
    Elf64_Word  sh_name;
    Elf64_Word  sh_type;
    Elf64_Xword sh_flags;
    Elf64_Addr  sh_addr;
    Elf64_Off   sh_offset;
    Elf64_Xword sh_size;
    Elf64_Word  sh_link;
    Elf64_Word  sh_info;
    Elf64_Xword sh_addralign;
    Elf64_Xword sh_entsize;
} Elf64_Shdr;

章节

索引 0 部分

包含在字节 0x40 至 0x7F 中。

第一部分总是很神奇:http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html说:

如果节数大于或等于 SHN_LORESERVE (0xff00),则 e_shnum 的值为 SHN_UNDEF (0),并且节头表条目的实际数量包含在索引 0 处的节头的 sh_size 字段中(否则,初始条目的 sh_size 成员包含 0)。

中还详细介绍了其他魔法部分Figure 4-7: Special Section Indexes

空值

在索引 0 中,SHT_NULL是强制性的。它还有其他用途吗:ELF 中的 SHT_NULL 部分有什么用处??

.data 部分

.data是第 1 部分:

00000080  01 00 00 00 01 00 00 00  03 00 00 00 00 00 00 00  |................|
00000090  00 00 00 00 00 00 00 00  00 02 00 00 00 00 00 00  |................|
000000a0  0d 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000b0  04 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  • 80 0: sh_name= :字符串表01 00 00 00中的索引 1.shstrtab

这里,1表示本节的名称从该节的第一个字符开始,到第一个 NUL 字符结束,组成字符串.data

.data是具有预定义含义的部分名称之一http://www.sco.com/developers/gabi/2003-12-17/ch4.strtab.html

这些部分保存了构成程序内存映像的初始化数据。

  • 80 4: sh_type= 01 00 00 00: SHT_PROGBITS: 节内容不是由 ELF 指定的,而是由程序如何解释它指定的。由于是.data节,因此正常。

  • 80 8: sh_flags= 037x 00:SHF_ALLOCSHF_EXECINSTR: http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#sh_flags,根据需要从一.data

  • 90 0: sh_addr=8x 00:执行期间该部分将被放置在哪个虚拟地址中,0如果没有放置

  • 90 8:sh_offset= 00 02 00 00 00 00 00 00= 0x200:从程序开始到本节第一个字节的字节数

  • a0 0:sh_size=0d 00 00 00 00 00 00 00

如果我们从 sh_offset200 开始取 0xD 字节,我们会看到:

00000200  48 65 6c 6c 6f 20 77 6f  72 6c 64 21 0a 00        |Hello world!..  |

啊哈!所以我们的"Hello world!"字符串位于数据部分,就像我们在 NASM 上所说的那样。

一旦我们从毕业hd,我们将会像这样查找:

readelf -x .data hello_world.o

输出:

Hex dump of section '.data':
  0x00000000 48656c6c 6f20776f 726c6421 0a       Hello world!.

NASM 为该部分设置了合适的属性,因为它可以.data神奇地处理:http://www.nasm.us/doc/nasmdoc7.html#section-7.9.2

还要注意,这是一个糟糕的部分选择:一个好的 C 编译器会将字符串放入其中.rodata,因为它是只读的,并且可以进一步进行操作系统优化。

  • a0 8:sh_linksh_info= 8x 0:不适用于此部分类型。http ://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#special_sections

  • b0 0: sh_addralign= 04= TODO: 为什么需要进行这种对齐?它仅适用于sh_addr,还是也适用于 内部的符号sh_addr

  • b0 8:sh_entsize= 00= 该节不包含表。如果 != 0,则表示该节包含固定大小条目的表。在此文件中,我们从输出中看到和节readelf的情况就是这样。.symtab`.rela.text`

.text 部分

现在我们已经手动完成了一个部分,让我们毕业并使用readelf -S其他部分。

  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 2] .text             PROGBITS         0000000000000000  00000210
       0000000000000027  0000000000000000  AX       0     0     16

.text可执行但不可写:如果我们尝试写入它,Linux 段错误。让我们看看那里是否真的有一些代码:

objdump -d hello_world.o

给出:

hello_world.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <_start>:
   0:       b8 01 00 00 00          mov    $0x1,%eax
   5:       bf 01 00 00 00          mov    $0x1,%edi
   a:       48 be 00 00 00 00 00    movabs $0x0,%rsi
  11:       00 00 00
  14:       ba 0d 00 00 00          mov    $0xd,%edx
  19:       0f 05                   syscall
  1b:       b8 3c 00 00 00          mov    $0x3c,%eax
  20:       bf 00 00 00 00          mov    $0x0,%edi
  25:       0f 05                   syscall

如果我们b8 01 00 00对 进行grep hd,我们会发现这只发生在00000210,这正是该部分所说的。而且 Size 是 27,也匹配。所以我们一定在谈论正确的部分。

这看起来是正确的代码:awrite后跟exit

最有趣的部分是a下面这一行:

movabs $0x0,%rsi

将字符串的地址传递给系统调用。目前,0x0只是一个占位符。链接后,它将被修改为包含:

4000ba: 48 be d8 00 60 00 00    movabs $0x6000d8,%rsi

由于该部分的数据,这种修改是可能的.rela.text

字符串表

带有 的部分sh_type == SHT_STRTAB称为字符串表

它们保存一个以空值分隔的字符串数组。

当要使用字符串名称时,其他部分将使用这些部分。using 部分说明:

  • 他们正在使用哪个字符串表

  • 目标字符串表上字符串开始的索引是什么

例如,我们可以有一个包含以下内容的字符串表:TODO:它必须以 开头吗

Data:  a b c  d e f 
Index: 0 1 2 3  4 5 6 7  8

如果另一个部分想要使用该字符串d e f,他们必须指向5该部分的索引(字母d)。

值得注意的字符串表部分:

  • .shstrtab

  • .strtab

.shstrtab

部分类型:sh_type == SHT_STRTAB

通用名称:节头字符串表

该部分名称.shstrtab是保留的。标准规定:

此部分包含部分名称。

e_shstrnd此部分由ELF 头本身的字段指向。

本节的字符串索引由sh_name节头字段指向,表示字符串。

此部分没有SHF_ALLOC标记,因此不会出现在执行程序中。

readelf -x .shstrtab hello_world.o

给出:

Hex dump of section '.shstrtab':
  0x00000000 002e6461 7461002e 74657874 002e7368 ..data..text..sh
  0x00000010 73747274 6162002e 73796d74 6162002e strtab..symtab..
  0x00000020 73747274 6162002e 72656c61 2e746578 strtab..rela.tex
  0x00000030 7400                                t.

本节数据有固定格式:http://www.sco.com/developers/gabi/2003-12-17/ch4.strtab.html

如果我们查看其他部分的名称,我们会发现它们都包含数字,例如.text部分是数字7

然后,每个字符串在找到第一个 NUL 字符时结束,例如字符12在之后.text

.符号表

部分类型:sh_type == SHT_SYMTAB

通用名称:符号表

首先我们注意到:

  • sh_link=5

  • sh_info=6

对于SHT_SYMTAB部分而言,这些数字意味着:

  • 给出符号名称的字符串在第 5 节中,.strtab

  • 重定位数据位于第 6 节,.rela.text

反汇编该部分的一个好的高级工具是:

nm hello_world.o

其结果为:

0000000000000000 T _start
0000000000000000 d hello_world
000000000000000d a hello_world_len

然而,这是一个高级视图,它忽略了某些类型的符号以及符号类型。可以使用以下命令获得更详细的反汇编:

readelf -s hello_world.o

其结果为:

Symbol table '.symtab' contains 7 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello_world.asm
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2
     4: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT    1 hello_world
     5: 000000000000000d     0 NOTYPE  LOCAL  DEFAULT  ABS hello_world_len
     6: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT    2 _start

该表的二进制格式记录在http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html

数据是:

readelf -x .symtab hello_world.o

得出:

Hex dump of section '.symtab':
  0x00000000 00000000 00000000 00000000 00000000 ................
  0x00000010 00000000 00000000 01000000 0400f1ff ................
  0x00000020 00000000 00000000 00000000 00000000 ................
  0x00000030 00000000 03000100 00000000 00000000 ................
  0x00000040 00000000 00000000 00000000 03000200 ................
  0x00000050 00000000 00000000 00000000 00000000 ................
  0x00000060 11000000 00000100 00000000 00000000 ................
  0x00000070 00000000 00000000 1d000000 0000f1ff ................
  0x00000080 0d000000 00000000 00000000 00000000 ................
  0x00000090 2d000000 10000200 00000000 00000000 -...............
  0x000000a0 00000000 00000000                   ........

条目类型为:

typedef struct {
    Elf64_Word  st_name;
    unsigned char   st_info;
    unsigned char   st_other;
    Elf64_Half  st_shndx;
    Elf64_Addr  st_value;
    Elf64_Xword st_size;
} Elf64_Sym;

就像在节表中一样,第一个条目是神奇的,并设置为固定的无意义的值。

同步文件

条目 1 有ELF64_R_TYPE == STT_FILEELF64_R_TYPE在 内部继续st_info

字节分析:

  • 10 8: st_name= 01000000= 中的字符 1 ,直到.strtab以下`hello_world.asm`

链接器可以使用此信息文件来决定要放入哪个段部分。

  • 10 12:st_info=04

位 0-3 = ELF64_R_TYPE= 类型 = 4= STT_FILE:该条目的主要用途是用来st_name指示生成此目标文件的文件的名称。

位 4-7 = ELF64_ST_BIND= 绑定 = 0= STB_LOCAL。所需值STT_FILE

  • 10 13:st_shndx= 符号表节头索引 = f1ff= SHN_ABS。为 所必需STT_FILE

  • 20 0: st_value= 8x 00: 需要的值STT_FILE

  • 20 8: st_size= 8x 00: 没有分配大小

现在从中readelf,我们快速解释一下其余部分。

STT_SECTION

有两个这样的条目,一个指向.data,另一个指向.text(节索引12)。

Num:    Value          Size Type    Bind   Vis      Ndx Name
  2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
  3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2

TODO 他们的目的是什么?

STT_NOTYPE

接下来是最重要的符号:

Num:    Value          Size Type    Bind   Vis      Ndx Name
  4: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT    1 hello_world
  5: 000000000000000d     0 NOTYPE  LOCAL  DEFAULT  ABS hello_world_len
  6: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT    2 _start

hello_world字符串位于.data节 (索引 1) 中。它的值为 0:指向该节的第一个字节。

_start因为我们写道,所以标记为GLOBAL可见:

global _start

在 NASM 中。这是必要的,因为它必须被视为入口点。与 C 不同,默认情况下 NASM 标签是本地的。

SHN_ABS

hello_world_len指向特殊之处st_shndx == SHN_ABS == 0xF1FF

0xF1FF被选择为不与其他部分发生冲突。

st_value == 0xD == 13这是我们在程序集中存储的值:字符串的长度Hello World!

这意味着重定位不会影响这个值:它是一个常量。

这是我们的汇编程序为我们做的一个小优化,并且支持 ELF。

如果我们在任何地方使用了的地址hello_world_len,汇编程序就不能将其标记为SHN_ABS,并且链接器稍后会对其进行额外的重定位工作。

可执行文件上的 SHT_SYMTAB

默认情况下,NASM 也会将 放在.symtab可执行文件中。

这仅用于调试。没有符号,我们完全是盲目的,必须对所有内容进行逆向工程。

您可以使用 来剥离它objcopy,而可执行文件仍可运行。此类可执行文件称为剥离的可执行文件

.strtab

保存符号表的字符串。

本节有sh_type == SHT_STRTAB

sh_link == 5它由部分指出.symtab

readelf -x .strtab hello_world.o

给出:

Hex dump of section '.strtab':
  0x00000000 0068656c 6c6f5f77 6f726c64 2e61736d .hello_world.asm
  0x00000010 0068656c 6c6f5f77 6f726c64 0068656c .hello_world.hel
  0x00000020 6c6f5f77 6f726c64 5f6c656e 005f7374 lo_world_len._st
  0x00000030 61727400                            art.

这意味着全局变量不能包含 NUL 字符是 ELF 级别的限制。

.rela.文本

部分类型:sh_type == SHT_RELA

通用名称:重定位部分

.rela.text保存重定位数据,该数据表示在链接最终可执行文件时应如何修改地址。这指向文本区域的字节,当链接恰好指向正确的内存位置时必须修改这些字节。

基本上,它翻译包含占位符 0x0 地址的对象文本:

   a:       48 be 00 00 00 00 00    movabs $0x0,%rsi
  11:       00 00 00

到包含最终 0x6000d8 的实际可执行代码:

4000ba: 48 be d8 00 60 00 00    movabs $0x6000d8,%rsi
4000c1: 00 00 00

它被该部分的sh_info=所指向。6`.symtab`

readelf -r hello_world.o给出:

Relocation section '.rela.text' at offset 0x3b0 contains 1 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
00000000000c  000200000001 R_X86_64_64       0000000000000000 .data + 0

可执行文件中不存在该部分。

实际的字节数是:

00000370  0c 00 00 00 00 00 00 00  01 00 00 00 02 00 00 00  |................|
00000380  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

struct代表为:

typedef struct {
    Elf64_Addr  r_offset;
    Elf64_Xword r_info;
    Elf64_Sxword    r_addend;
} Elf64_Rela;

所以:

  • 370 0: r_offset= 0xC: 地址进入.text此重定位将修改的地址

  • 370 8:r_info= 0x200000001。包含2个字段:

+ `ELF64_R_TYPE`= 0x1:含义取决于具体架构。
+ `ELF64_R_SYM`= 0x2:地址指向的部分的索引,因此`.data`位于索引 2。AMD64 ABI 表示该类型`1`被调用,`R_X86_64_64`并且它代表以下操作`S + A`:
+ `S`:对象文件上的符号的值,这里`0`因为我们指向`00 00 00 00 00 00 00 00`的`movabs $0x0,%rsi`
+ `A`:字段中的加数`r_added`该地址被添加到重定位所操作的部分。

本次重定位操作一共作用于8个字节。

  • 380 0:r_addend= 0

因此,在我们的示例中,我们得出结论,新地址将是:S + A= .data + 0,因此是数据部分中的第一件事。

程序头表

仅出现在可执行文件中。

包含如何将可执行文件放入进程虚拟内存的信息。

可执行文件由链接器从目标文件生成。链接器的主要工作是:

  • 确定目标文件的哪些部分将进入可执行文件的哪些段。

在 Binutils 中,这归结为解析链接器脚本并处理一堆默认值。

您可以获取使用的链接器脚本ld --verbose,并使用设置自定义的链接器脚本ld -T

  • 对文本部分进行重定位。这取决于多个部分如何放入内存。

readelf -l hello_world.out给出:

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

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x00000000000000d7 0x00000000000000d7  R E    200000
  LOAD           0x00000000000000d8 0x00000000006000d8 0x00000000006000d8
                 0x000000000000000d 0x000000000000000d  RW     200000

 Section to Segment mapping:
  Segment Sections...
   00     .text
   01     .data

在 ELF 头上,e_phoffe_phnume_phentsize告诉我们有 2 个程序头,它们从 开始0x400x38每个头的长度为 字节,因此它们是:

00000040  01 00 00 00 05 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 40 00 00 00 00 00  00 00 40 00 00 00 00 00  |..@.......@.....|
00000060  d7 00 00 00 00 00 00 00  d7 00 00 00 00 00 00 00  |................|
00000070  00 00 20 00 00 00 00 00                           |.. .....        |

和:

00000070                           01 00 00 00 06 00 00 00  |        ........|
00000080  d8 00 00 00 00 00 00 00  d8 00 60 00 00 00 00 00  |..........`.....|
00000090  d8 00 60 00 00 00 00 00  0d 00 00 00 00 00 00 00  |..`.............|
000000a0  0d 00 00 00 00 00 00 00  00 00 20 00 00 00 00 00  |.......... .....|

结构代表http://www.sco.com/developers/gabi/2003-12-17/ch5.pheader.html

typedef struct {
    Elf64_Word  p_type;
    Elf64_Word  p_flags;
    Elf64_Off   p_offset;
    Elf64_Addr  p_vaddr;
    Elf64_Addr  p_paddr;
    Elf64_Xword p_filesz;
    Elf64_Xword p_memsz;
    Elf64_Xword p_align;
} Elf64_Phdr;

第一个的细分:

  • 40 0: p_type= 01 00 00 00= PT_LOAD: TODO。我认为这意味着它将真正加载到内存中。其他类型不一定如此。

  • 40 4:p_flags==05 00 00 00执行和读取权限,无写入TODO

  • 40 8: p_offset= 8x 00TODO:这是什么?看起来像是从段开头偏移的。但这是否意味着某些段是交织在一起的?可以使用以下命令稍微玩一下:gcc -Wl,-Ttext-segment=0x400030 hello_world.c

  • 50 0: p_vaddr= 00 00 40 00 00 00 00 00:将此段加载到的初始虚拟内存地址

  • 50 8: p_paddr= 00 00 40 00 00 00 00 00:加载到内存中的初始物理地址。这只对程序可以设置其物理地址的系统有用。否则,就像在 System V 之类的系统中一样,可以是任何东西。NASM 似乎只是复制p_vaddrr

  • 60 0:p_filesz= d7 00 00 00 00 00 00 00:待办事项 vsp_memsz

  • 60 8:p_memsz= d7 00 00 00 00 00 00 00:待办事项

  • 70 0: p_align= 00 00 20 00 00 00 00 00: 0 或 1 表示无需对齐 TODO 这是什么意思?否则与其他字段重复

第二个也是类似的。

然后是:

 Section to Segment mapping:

部分readelf告诉我们:

  • 0 是.text段。啊哈,这就是为什么它是可执行的,而不是可写的

  • 1 是.data段。

解决方案 2:

正如我在评论中提到的,您实际上将为可执行文件编写自己的 elf-header,以消除不需要的部分。仍然有几个必需的部分。Muppetlabs -TinyPrograms上的文档很好地解释了这个过程。为了好玩,这里有几个例子:

相当于 /bin/true (45 字节):

00000000  7F 45 4C 46 01 00 00 00  00 00 00 00 00 00 49 25  |.ELF..........I%|
00000010  02 00 03 00 1A 00 49 25  1A 00 49 25 04 00 00 00  |......I%..I%....|
00000020  5B 5F F2 AE 40 22 5F FB  CD 80 20 00 01           |[_..@"_... ..|
0000002d

经典的“Hello World!”(160 字节):

00000000  7f 45 4c 46 01 01 01 03  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 03 00 01 00 00 00  74 80 04 08 34 00 00 00  |........t...4...|
00000020  00 00 00 00 00 00 00 00  34 00 20 00 02 00 28 00  |........4. ...(.|
00000030  00 00 00 00 01 00 00 00  74 00 00 00 74 80 04 08  |........t...t...|
00000040  74 80 04 08 1f 00 00 00  1f 00 00 00 05 00 00 00  |t...............|
00000050  00 10 00 00 01 00 00 00  93 00 00 00 93 90 04 08  |................|
00000060  93 90 04 08 0d 00 00 00  0d 00 00 00 06 00 00 00  |................|
00000070  00 10 00 00 b8 04 00 00  00 bb 01 00 00 00 b9 93  |................|
00000080  90 04 08 ba 0d 00 00 00  cd 80 b8 01 00 00 00 31  |...............1|
00000090  db cd 80 48 65 6c 6c 6f  20 77 6f 72 6c 64 21 0a  |...Hello world!.|
000000a0

不要忘记使它们可执行...

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用