我的架构是x86。我能做的一个非常简单的程序是什么?一个你好的世界?无限循环?类似于这个问题,但在Linux中。
man elf
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'
hd hello_world.o
hd hello_world.out
readelf -h hello_world.o
readelf -h hello_world.out
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 magic number
07:ei_osabi
(仅在2003年更新中)=00
=elfosabi_none
:没有扩展。
08:ei_pad
=8x00
:保留字节。必须设置为0。
1 0:e_type
=0100
=1(big endian)=et_rel
:可重定位格式
1 4:E_version
=01000000
:必须为1
1 8:e_entry
=8x00
:执行地址入口点,如果不适用,则为0,因为没有入口点。
在可执行文件上,它是b000400000000000
。TODO:我们还能把它调成什么?内核似乎把IP直接放在那个值上,它不是硬编码的。
3 0:e_flags
=00000000
TODO。拱门特定。
3 4:e_ehsize
=4000
:此elf头的大小。为什么要做这个领域?怎么会有变化呢?
3 6:e_phentsize
=0000
:每个程序头的大小,如果不存在则为0。
可执行文件上的0200
:有2个条目。
3 A:e_shentsize
和e_shnum
=40 00 07 00
:节头大小和条目数
3 e:e_shstrndx
(节头字符串索引
)=0300
:.shstrtab
节的索引。
ELF头的e_shoff
给出起始位置,这里是0x40。
ELF头中的e_shentsize
和e_shnum
表示我们有7个条目,每个0x40
字节长。
因此,该表接受从0x40到0x40+7+0x40-1
=0x1ff的字节。
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)
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;
包含在字节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)。
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 |................|
>
800:sh_name
=01000000
:.shstrtab
字符串表中的索引1
这里,1
表示该节的名称从该节的第一个字符开始,到第一个NUL字符结束,组成字符串.data
。
.data
是节名之一,它具有预定义的含义http://www.sco.com/developers/gabi/2003-12-17/ch4.strtab.html
804:sh_type
=01 000000
:sht_progbits
:区段内容不是由ELF指定的,而是由程序如何解释它指定的。正常,因为.data
节。
808:sh_flags
=03
7x00
:shf_alloc
和shf_execinstr
:http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#sh_flags,根据.data
部分的要求
90 0:sh_addr
=8x00
:在执行期间该节将放置在哪个虚拟地址中,如果未放置则0
90 8:sh_offset
=00 02 0000000000000000
=0x200
:从程序开始到本节中第一个字节的字节数
a0 0:sh_size
=0d 000000000000000000
如果从sh_offset
200开始取0xD字节,我们会看到:
00000200 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 |Hello world!.. |
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
中,因为它是只读的,并且允许进一步的OS优化。
A08:sh_link
和sh_info
=8x0:不适用于此节类型。http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#special_sections
现在我们已经手动完成了一个部分,让我们毕业并使用其他部分的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 segfaults。让我们看看我们是否真的有一些代码:
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
movabs $0x0,%rsi
4000ba: 48 be d8 00 60 00 00 movabs $0x6000d8,%rsi
具有sh_type==sht_strtab
的节称为字符串表。
它们保存一个空分隔的字符串数组。
当要使用字符串名称时,这些节由其他节使用。使用部分说:
Data: \0 a b c \0 d e f \0
Index: 0 1 2 3 4 5 6 7 8
节类型:sh_type==sht_strtab
。
通用名称:节头字符串表。
保留节名.shstrtab
。标准说:
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
部分是number7
。
然后,当找到第一个NUL字符时,每个字符串结束,例如,字符12
是\0
紧随.text\0
之后的\0
。
首先,我们注意到:
sh_link
=5
sh_info
=6
对于sht_symtab
节,这些数字表示:
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;
链接器可以使用这段信息文件来决定哪些区段区段。
10 12:st_info
=04
位0-3=elf64_r_type
=Type=4
=stt_file
:该条目的主要用途是使用st_name
指示生成此对象文件的文件名。
20 8:st_size
=8x00
:未分配大小
现在,从Readel
开始,我们将快速解释其他内容。
有两个这样的条目,一个指向.data
,另一个指向.text
(节索引1
和2
)。
Num: Value Size Type Bind Vis Ndx Name
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
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
_start
标记为全局
可见性,因为我们编写了:
global _start
在纳斯姆。这是必要的,因为它必须被视为切入点。与C语言不同,默认情况下NASM标签是本地的。
hello_world_len
指向特殊的st_shndx==SHN_ABS==0xf1ff
。
如果我们在任何地方使用hello_world_len
的地址,汇编程序将无法将其标记为shn_abs
,链接器稍后将对其进行额外的重新定位。
默认情况下,NASM还在可执行文件上放置.symtab
。
这仅用于调试。没有符号,我们就完全是盲目的,必须对一切进行逆向工程。
它由.symtab
节的sh_link==5
指向。
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级别的限制。
a: 48 be 00 00 00 00 00 movabs $0x0,%rsi
11: 00 00 00
4000ba: 48 be d8 00 60 00 00 movabs $0x6000d8,%rsi
4000c1: 00 00 00
它由.symtab
节的sh_info
=6
指向。
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 |................|
typedef struct {
Elf64_Addr r_offset;
Elf64_Xword r_info;
Elf64_Sxword r_addend;
} Elf64_Rela;
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
,其中:
此重定位操作总共作用于8个字节。
380:r_addend
=0
因此,在我们的示例中,我们得出结论,新地址将是:s+a
=.data+0
,因此是数据部分的第一个内容。
在Binutils中,这归结为解析链接器脚本和处理一系列默认值。
您可以获得与ld--verbose
一起使用的链接器脚本,并使用ld-t
设置一个自定义链接器脚本。
对文本部分进行重新定位。这取决于将多个节放入内存的方式。
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
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 |.......... .....|
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;
第一项的细分:
p_type
=01 000000
=pt_load
:TODO。我想这意味着它将被实际加载到内存中。其他类型不一定是。p_flags
=05000000
=执行和读取权限,没有写权限P_offset
=8x00
TODO:这是什么?看起来像是从段开始的偏移量。但这意味着一些片段是交织在一起的?可以使用以下方法来玩:gcc-wl,-ttext-segment=0x400030 hello_world.c
P_vaddr
=0000400000000000
:将此段加载到p_paddr
=0000400000000000
:加载到内存中的初始物理地址。只有在程序可以设置物理地址的系统中才重要。否则,就像在系统V中一样,可以是任何东西。NASM似乎只是复制p_vaddrr
p_filesz
=d7 00000000000000000000
:TODO vsp_memsz
p_memsz
=d7 000000000000000000
:todoP_align
=00 00 20 00 00 00 00 00
:0或1表示不需要对齐,这是什么意思?否则与其他字段冗余第二个是类比的。
Section to Segment mapping:
问题内容: 只是好奇。显然,这对于实际编程而言不是一个很好的解决方案,但我想在Bless(十六进制编辑器)中创建一个可执行文件。 我的体系结构是x86。我可以制作一个非常简单的程序?你好,世界?无限循环?与此问题类似,但在Linux中。 问题答案: 如我的评论中所述,您实际上将为可执行文件编写自己的elf-header,以消除不需要的部分。仍然需要几个部分。Muppetlabs- TinyProg
问题内容: 我需要一个适用于Linux的HEX编辑器,我的意思是: 快速 搜索/替换功能 不仅可以以十六进制显示数据,还可以以二进制,八进制等显示数据。 可以处理大型(> 1 gb)文件,而不会变慢且无响应(此要求很重要) (可选)具有一些比较/差异功能 你有什么建议? 问题答案: Bless 是高质量的全功能十六进制编辑器。 它是用mono / Gtk#编写的,其主要平台是GNU / Linux
这只是纯粹的好奇。不...我不打算直接用二进制机器代码编写程序(我甚至不经常编写汇编代码,我只是大部分时间使用C/C++作为我最低级的工具)。我只是想看看是否有可能做到这一点,因为在计算机出现的早期,可能有人必须做到这一点。 附注:我知道关于这个话题有类似的问题,但没有一个提供一个可行的例子。我只想要一个简单的例子,这样它可以帮助我理解编译器和汇编器如何生成一个可执行文件。我是说...一定有人在过
问题内容: 我爱得如此之多的两件事通常并不会引起我那么多的烦恼(除了我的孩子们)。我已经在工作中编写了一个Haskell程序,该程序使用诸如文本,xml- enumerator,attoparsec-text等库。我在Windows计算机,Ubuntu虚拟机(32位), Ubuntu桌面(再次为32位)和运行Ubuntu(64位)的EC2实例。 我们的客户端正在运行64位CentOS 5.3。我无
我从下面的另一个问题中看到了一个代码,但我不知道如何利用它。
问题内容: 如何在JavaScript中将十进制值转换为等效的十六进制值? 问题答案: 使用以下命令将数字转换为十六进制字符串: 并通过以下步骤逆向处理: