介绍
我正在尝试了解ELF
文件,并对它们进行一些实验。目前,我正在遵循此处的教程,该教程是关于创建一个仅以代码 42 退出的小型 32 位ELF
文件。本教程使用NASM
和ld
来创建ELF
文件。但是,我想使用GNU Assembler (GAS)
创建 64 位ELF
文件(而不是 32 位),因为我对它有点熟悉(但不是使用它的专家!)
我目前陷入了教程的部分,他们ELF
从头开始创建文件 - 这涉及编写ELF
标头和程序标头。由于 32 位和 64 位ELF
文件略有不同(例如,在 ELF 标头大小方面),我的.s
文件版本如下:
.intel_syntax noprefix
ehdr:
.byte 0x7F
.byte 0x45 # E
.byte 0x4c # L
.byte 0x46 # F
.byte 0x02 # 64 bit ; CLASS
.byte 0x01 # lsb ; DATA
.byte 0x01 # ; VERSION
.byte 0x00 # None/Sytem V ; OS ABI
.8byte 0x0 # ABI VERSION + PADDING
.2byte 0x02 # ET_EXEC ; E_TYPE
.2byte 0x3E # AMD64 ; E_MACHINE
.4byte 0x01 # 1 ; E_VERSION
.8byte _start # ; E_ENTRY
.8byte phdr - ehdr # offset into program header ; E_PHOFF
.8byte 0x00 # offset into section header ; E_SHOFF
.4byte 0x00 # flag ; E_FLAGS
.2byte ehdrsize # ELF header size ; E_EHSIZE
.2byte phdrsize # Program header size ; E_PHSIZE
.2byte 0x01 # Number of program headers ; E_PHNUM
.2byte 0x00 # Section header size ; E_SHENTSIZE
.2byte 0x00 # Number of section headers ; E_SHNUM
.2byte 0x00 # Section header string table index ; E_SHSTRNDX
DECLARE_ELF_HEADER_SIZE:
.set ehdrsize, DECLARE_ELF_HEADER_SIZE - ehdr
phdr:
.4byte 0x01 # PT_LOAD ; P_TYPE
.4byte 0x00 # located at offset 0??? ; P_OFFSET
.8byte ehdr # ; P_VADDR
.8byte ehdr # ; P_PADDR
.8byte filesize # ; P_FILESIZE
.8byte filesize # ; P_MEMSZ
.8byte 5 # R-X ; P_FLAGS
.8byte 0x1000 # P_ALIGN
DECLARE_PHEADER_SIZE:
.set phdrsize, DECLARE_PHEADER_SIZE - phdr
_start:
mov eax, 60
mov edi, 42
syscall
DECLARE_FILE_SIZE:
.set filesize, DECLARE_FILE_SIZE - ehdr
有关 ELF 文件头的更多信息可以通过查看此处的相应源代码来找到。由于我仍在学习ELF
文件,上面的代码可能不正确(如果您发现任何错误,请告诉我!)
问题
假设上面的代码是正确的,我想将上面的.s
文件转换成一个ELF
文件。为此,本教程使用以下命令:
$ nasm -f bin -o a.out tiny.asm
但是,我无法找到我应该使用 GAS 的相应命令来创建相应的文件ELF
,这就是我面临的问题。
我尝试过的...
我尝试过各种方法。我将在下面列出它们:
- SO上有一个类似的问题,可以在这里找到。该方法使用以下2条命令来获取对应的ELF文件:
$ as --64 -o test.o test.s
$ ld -Ttext 200000 --oformat binary -o test.bin test.o
.s
对我编写的文件运行相同的命令,我确实得到了一个 ELF 文件,以及以下警告:
ld: warning: cannot find entry symbol _start; defaulting to 0000000000200000
运行生成的 ELF 文件会出现分段错误,并以代码 139 退出。生成的文件的标ELF
头如下(通过运行获得readelf -h <output ELF file>
):
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x200078
Start of program headers: 64 (bytes into file)
Start of section headers: 0 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 1
Size of section headers: 0 (bytes)
Number of section headers: 0
Section header string table index: 0
readelf: Error: the segment's file size is larger than its memory size
我相信发生了分段错误,因为它很可能是不正确的(错误消息中Entry Point address
也暗示了这一点)。ld
- 在这种方法中,我修改了命令:
$ as --64 -o test.o test.s
$ ld -Ttext 200000 --oformat binary -o test.bin test.o
到
$ as --64 -o test.o test.s (same as before)
$ ld -nmagic -s test.o (changed)
这也会创建一个 ELF 文件,但同样会出现以下警告:
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400078
这再次暗示我们将遇到分段错误,新ELF
文件确实就是这种情况。该文件的 ELF 标头是:
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400078
Start of program headers: 64 (bytes into file)
Start of section headers: 0 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 1
Size of section headers: 64 (bytes)
Number of section headers: 0
Section header string table index: 0
没有运气!
- 从给出的警告来看
ld
,看起来我们可能想要.global _start
在我们的.s
文件中添加。.s
下面是也包含此行的新文件:
.intel_syntax noprefix
.global _start # Added here
ehdr:
.byte 0x7F
.byte 0x45 # E
.byte 0x4c # L
.byte 0x46 # F
.byte 0x02 # 64 bit ; CLASS
.byte 0x01 # lsb ; DATA
.byte 0x01 # ; VERSION
.byte 0x00 # None/Sytem V ; OS ABI
.8byte 0x0 # ABI VERSION + PADDING
.2byte 0x02 # ET_EXEC ; E_TYPE
.2byte 0x3E # AMD64 ; E_MACHINE
.4byte 0x01 # 1 ; E_VERSION
.8byte _start # ; E_ENTRY
.8byte phdr - ehdr # offset into program header ; E_PHOFF
.8byte 0x00 # offset into section header ; E_SHOFF
.4byte 0x00 # flag ; E_FLAGS
.2byte ehdrsize # ELF header size ; E_EHSIZE
.2byte phdrsize # Program header size ; E_PHSIZE
.2byte 0x01 # Number of program headers ; E_PHNUM
.2byte 0x00 # Section header size ; E_SHENTSIZE
.2byte 0x00 # Number of section headers ; E_SHNUM
.2byte 0x00 # Section header string table index ; E_SHSTRNDX
DECLARE_ELF_HEADER_SIZE:
.set ehdrsize, DECLARE_ELF_HEADER_SIZE - ehdr
phdr:
.4byte 0x01 # PT_LOAD ; P_TYPE
.4byte 0x00 # located at offset 0??? ; P_OFFSET
.8byte ehdr # ; P_VADDR
.8byte ehdr # ; P_PADDR
.8byte filesize # ; P_FILESIZE
.8byte filesize # ; P_MEMSZ
.8byte 5 # R-X ; P_FLAGS
.8byte 0x1000 # P_ALIGN
DECLARE_PHEADER_SIZE:
.set phdrsize, DECLARE_PHEADER_SIZE - phdr
_start:
mov eax, 60
mov edi, 42
syscall
DECLARE_FILE_SIZE:
.set filesize, DECLARE_FILE_SIZE - ehdr
再次,我们使用以下命令为上述代码创建 ELF 文件:
$ as --64 -o test.o test.s
$ ld -nmagic -s test.o
这次,我们没有收到任何错误,并且 ELF 文件执行了它应该执行的操作(即以代码 42 退出)。该文件的 ELF 标头是:
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x4000f0
Start of program headers: 64 (bytes into file)
Start of section headers: 0 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 1
Size of section headers: 64 (bytes)
Number of section headers: 0
Section header string table index: 0
问题解决了,嗯?
不幸的是,我不这么认为。深入研究二进制文件发现它没有ELF
使用我们在文件中编写的手写标头.s
:
gef➤ search-pattern 'ELF'
[+] Searching 'ELF' in memory
[+] In '/home/VM/Desktop/Experiments/ELF/a.out'(0x400000-0x401000), permission=r-x
0x400001 - 0x400004 → "ELF[...]"
0x400079 - 0x40007c → "ELF[...]"
哎呀,看起来ld
(或者as
,我不确定!)创建了自己的ELF
标头(从有 2 个位置包含字符串这一事实可以明显看出。ELF
根据我的理解,ELF
字符串 at0x400001
是由 ( 贡献的ld
)或as
),其中的内容0x400079
是我包含在.s
文件中的内容。
然而,这违背了从头开始编写 ELF 文件的初衷,包括ELF
头文件和程序头文件!我也确信我之前列出的方法 1 和方法 2 中存在此问题,但由于它们无论如何都因分段错误而退出,因此我没有在这些方法中突出显示此问题。
现在,我不知道该怎么做,所以我非常感谢任何帮助ELF
正确构建文件的帮助!
多谢!
有关的消息
_start
是一个转移注意力的消息。这通常用于填充入口点,但由于您手动执行此操作并且没有使用链接器来发出 ELF 标头,因此这是无关紧要的。你把phdr搞错了。特别地,p_offset
应该是8字节,p_flags
应该是4字节并且被放置为第二字段。固定版本是: