簡體   English   中英

如何使用十六進制編輯器在 Linux 中制作可執行的 ELF 文件?

[英]How to make an executable ELF file in Linux using a hex editor?

只是好奇。 對於實際編程來說,這顯然不是一個很好的解決方案,但假設我想在 Bless(一個十六進制編輯器)中制作一個可執行文件。

我的架構是 x86。 我可以制作什么非常簡單的程序? 一個你好世界? 無限循環? 問題類似,但在 Linux 中。

反編譯NASM hello世界並理解其中的每個字節

這個答案的版本有一個很好的TOC和更多內容: http//www.cirosantilli.com/elf-hello-world (在這里達到30k char限制)

標准

ELF由LSB指定:

LSB基本上鏈接到具有次要擴展的其他標准,特別是:

可以在以下位置找到方便的摘要:

man elf

它的結構可以通過readelfobjdump等實用程序以人類可讀的方式進行檢查。

生成示例

讓我們分解一個最小的可運行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級:-)

Hexdumps

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 = EI_MAG 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 :可重定位格式

    在可執行文件上, ET_EXEC02 00

  • 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 = 40 7x 00 = 0x40 :節頭表文件偏移量,如果不存在則為0。

  • 3 0: e_flags = 00 00 00 00 TODO。 Arch具體。

  • 3 4: e_ehsize = 40 00 :此精靈標題的大小。 TODO為什么要這個領域? 它怎么變化?

  • 3 6: e_phentsize = 00 00 :每個程序頭的大小,如果不存在,則為0。

    38 00 on executable:它長56個字節

  • 3 8: e_phnum = 00 00 :程序頭條目數,如果不存在,則為0。

    02 00 on executable:有2個條目。

  • 3 A: e_shentsizee_shnum = 40 00 07 00 :節標題大小和條目數

  • 3 E: e_shstrndxSection Header STRing iNDeX )= 03 00.shstrtab部分的索引。

節頭表

Elf64_Shdr結構數組。

每個條目都包含有關給定部分的元數據。

ELF頭的e_shoff給出了起始位置0x40。

ELF頭中的e_shentsizee_shnum表示我們有7個條目,每個條目長0x40個字節。

因此該表從0x40到0x40 + 7 + 0x40 - 1 = 0x1FF獲取字節。

某些部分名稱保留用於某些部分類型: 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

SHT_NULL

在索引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.shstrtab字符串表中的索引1

    這里, 1表示該部分的名稱從該部分的第一個字符開始,並以第一個NUL字符結束,構成字符串.data

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

    這些部分包含有助於程序內存映像的初始化數據。

  • 80 4: sh_type = 01 00 00 00SHT_PROGBITS :ELF未指定節內容,僅由程序如何解釋。 正常,因為.data部分。

  • 80 8: sh_flags = 03 7x 00SHF_ALLOCSHF_EXECINSTRhttp//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_offset 200開始采用0xD字節,我們會看到:

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

    AHA! 所以我們的"Hello world!" 字符串在數據部分,就像我們告訴它在NASM上一樣。

    一旦我們畢業的hd ,我們將看到這個像:

     readelf -x .data hello_world.o 

    哪個輸出:

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

    NASM為該部分設置了不錯的屬性,因為它神奇地處理.datahttp//www.nasm.us/doc/nasmdoc7.html#section-7.9.2

    另請注意,這是一個糟糕的部分選擇:一個好的C編譯器會將字符串放在.rodata ,因為它是只讀的,它將允許進一​​步的OS優化。

  • 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

objdump -d hello_world.o

.text是可執行的但不可寫:如果我們嘗試寫入Linux段錯誤。 讓我們看看我們是否真的有一些代碼:

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

得到:

 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 

如果我們在hd上grep b8 01 00 00 ,我們看到這只發生在00000210 ,這就是該部分所說的。 大小是27,也匹配。 所以我們必須談論正確的部分。

這看起來像正確的代碼: writeexit

最有趣的部分是a行:

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

將字符串的地址傳遞給系統調用。 目前, 0x0只是一個占位符。 鏈接發生后,它將被修改為包含:

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

由於.rela.text部分的數據,這種修改是可能的。

SHT_STRTAB

帶有sh_type == SHT_STRTAB節被稱為字符串表

它們包含一個空分隔的字符串數組。

當使用字符串名稱時,其他部分將使用這些部分。 使用部分說:

  • 他們正在使用哪個字符串表
  • 字符串開始的目標字符串表上的索引是什么

例如,我們可以有一個字符串表包含:TODO:它必須以\\0開頭嗎?

 Data: \\0 abc \\0 def \\0 Index: 0 1 2 3 4 5 6 7 8 

如果另一部分想要使用字符串def ,則必須指向本節的索引5 (字母d )。

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

  • .shstrtab
  • .strtab

.shstrtab

節類型: sh_type == SHT_STRTAB

通用名稱: 節頭字符串表

節名稱.shstrtab是保留的。 標准說:

此部分包含部分名稱。

ELF標題本身的e_shstrnd字段指向此部分。

此節的字符串索引由節標題的sh_name字段指向,表示字符串。

此部分沒有標記SHF_ALLOC ,因此它不會出現在執行程序中。

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.

得到:

 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\\0.text\\0.text\\0

的.symtab

節類型: sh_type == SHT_SYMTAB

通用名稱: 符號表

首先,我們注意到:

  • sh_link = 5
  • sh_info = 6

對於SHT_SYMTAB部分,這些數字表示:

  • 提供符號名稱的字符串在第5節.strtab
  • 重定位數據在第6節.rela.text

拆卸該部分的一個很好的高級工具是:

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

這使:

 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

數據是:

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;

條目類型:

 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; 

就像在section表中一樣,第一個條目是神奇的,並設置為固定的無意義值。

STT_FILE

條目1具有ELF64_R_TYPE == STT_FILE ELF64_R_TYPE持續的內部st_info

字節分析:

  • 10 8: st_name = 01000000 =在字符1 .strtab ,它直到下面\\0使得hello_world.asm

    鏈接器可以使用這條信息文件來決定哪些段部分。

  • 10 12: st_info = 04

    位0-3 = ELF64_R_TYPE = Type = 4 = STT_FILE :此條目的主要目的是使用st_name指示生成此目標文件的文件的名稱。

    位4-7 = ELF64_ST_BIND = Binding = 0 = STB_LOCAL STT_FILE必需值。

  • 10 13: st_shndx =符號表部分標題索引= f1ff = SHN_ABS STT_FILE必需。

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

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

現在,從readelf ,我們很快解釋其他人。

STT_SECTION

有兩個這樣的條目,一個指向.data ,另一個指向.text (段索引12 )。

global _start

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!的長度Hello World!

這意味着重定位不會影響此值:它是一個常量。

這是我們的匯編程序為我們做的小優化,並且具有ELF支持。

如果我們在任何地方使用過hello_world_len的地址,那么匯編程序就無法將其標記為SHN_ABS ,並且鏈接器稍后會對其進行額外的重定位工作。

可執行文件上的SHT_SYMTAB

默認情況下,NASM .symtab在可執行文件上放置.symtab

這僅用於調試。 沒有符號,我們完全失明,必須對所有事情進行逆向工程。

您可以使用objcopy刪除它,並且可執行文件仍將運行。 此類可執行文件稱為剝離的可執行文件

.strtab

保存符號表的字符串。

此部分有sh_type == SHT_STRTAB

它由.symtab部分的sh_link == 5 .symtab

 readelf -x .strtab 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

這意味着全局變量不能包含NUL字符是ELF級別限制。

.rela.text

節類型: 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 

該部分在可執行文件中不存在。

實際字節是:

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

表示的struct是:

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

所以:

  • 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 RE 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個程序頭,它們從0x40開始,每個長度為0x38字節,所以它們是:

 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 00 TODO:這是什么? 看起來像段開頭的偏移量。 但這意味着某些細分市場交織在一起? 可以使用它來玩它: 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 :TODO vs p_memsz
  • 60 8: p_memsz = d7 00 00 00 00 00 00 00 :TODO
  • 70 0: p_align = 00 00 20 00 00 00 00 00 :0或1表示無需對齊TODO是什么意思? 否則與其他領域重復

第二個是類似的。

那么:

  Section to Segment mapping: 

readelf部分告訴我們:

  • 0是.text段。 啊哈,所以這就是為什么它是可執行的,而不是可寫的
  • 1是.data段。

正如我的評論中所提到的,你將基本上為可執行文件編寫自己的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

別忘了讓它們可執行......

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM