Hiểu định dạng tệp ELF

Understanding Elf File Format



Từ mã nguồn sang mã nhị phân

Lập trình bắt đầu bằng việc có một ý tưởng thông minh và viết mã nguồn bằng ngôn ngữ lập trình bạn chọn, ví dụ C, và lưu mã nguồn vào một tệp. Với sự trợ giúp của một trình biên dịch thích hợp, ví dụ như GCC, trước tiên, mã nguồn của bạn sẽ được dịch thành mã đối tượng. Cuối cùng, trình liên kết dịch mã đối tượng thành tệp nhị phân liên kết mã đối tượng với các thư viện được tham chiếu. Tệp này chứa các lệnh đơn dưới dạng mã máy được CPU hiểu và được thực thi ngay khi chạy chương trình đã biên dịch.

Tệp nhị phân được đề cập ở trên tuân theo một cấu trúc cụ thể và một trong những cấu trúc phổ biến nhất được đặt tên là ELF, viết tắt của Định dạng có thể thực thi và Có thể liên kết. Nó được sử dụng rộng rãi cho các tệp thực thi, tệp đối tượng có thể định vị lại, thư viện được chia sẻ và kết xuất lõi.







Hai mươi năm trước - vào năm 1999 - dự án 86open đã chọn ELF làm định dạng tệp nhị phân tiêu chuẩn cho các hệ thống giống Unix và Unix trên bộ xử lý x86. May mắn thay, định dạng ELF trước đây đã được ghi nhận trong cả Giao diện nhị phân ứng dụng Hệ thống V và Tiêu chuẩn giao diện công cụ [4]. Thực tế này đã đơn giản hóa rất nhiều thỏa thuận về tiêu chuẩn hóa giữa các nhà cung cấp và nhà phát triển khác nhau của hệ điều hành dựa trên Unix.



Lý do đằng sau quyết định đó là thiết kế ELF - tính linh hoạt, khả năng mở rộng và hỗ trợ đa nền tảng cho các định dạng endian và kích thước địa chỉ khác nhau. Thiết kế của ELF không giới hạn ở một bộ xử lý, tập lệnh hoặc kiến ​​trúc phần cứng cụ thể. Để có so sánh chi tiết về các định dạng tệp thực thi, hãy xem tại đây [3].



Kể từ đó, định dạng ELF được một số hệ điều hành khác nhau sử dụng. Trong số những người khác, điều này bao gồm Linux, Solaris / Illumos, Free-, Net- và OpenBSD, QNX, BeOS / Haiku và Fuchsia OS [2]. Hơn nữa, bạn sẽ tìm thấy nó trên các thiết bị di động chạy Android, Maemo hoặc Meego OS / Sailfish OS cũng như trên các bảng điều khiển trò chơi như PlayStation Portable, Dreamcast và Wii.





Đặc tả không làm rõ phần mở rộng tên tệp cho các tệp ELF. Đang được sử dụng là một loạt các kết hợp chữ cái, chẳng hạn như .axf, .bin, .elf, .o, .prx, .puff, .ko, .so và .mod, hoặc không.

Cấu trúc của tệp ELF

Trên thiết bị đầu cuối Linux, command man elf cung cấp cho bạn một bản tóm tắt hữu ích về cấu trúc của tệp ELF:



Liệt kê 1: Trang chủ của cấu trúc ELF

$ man mười một

ELF (5) Hướng dẫn lập trình viên Linux ELF (5)

TÊN
elf - định dạng tệp có thể thực thi và định dạng liên kết (ELF)

TÓM TẮC
#bao gồm

SỰ MÔ TẢ
Tệp tiêu đề xác định định dạng của tệp nhị phân thực thi ELF
các tập tin. Trong số các tệp này là các tệp thực thi bình thường, có thể định vị lại
tệp đối tượng, tệp lõi và thư viện được chia sẻ.

Một tệp thực thi sử dụng định dạng tệp ELF bao gồm một tiêu đề ELF,
theo sau là bảng tiêu đề chương trình hoặc bảng tiêu đề phần hoặc cả hai.
Tiêu đề ELF luôn ở độ lệch 0 của tệp. Chương trình
bảng tiêu đề và phần bù của bảng tiêu đề phần trong tệp là
được định nghĩa trong tiêu đề ELF. Hai bảng mô tả phần còn lại của
đặc điểm của tệp.

...

Như bạn có thể thấy từ mô tả ở trên, một tệp ELF bao gồm hai phần - một tiêu đề ELF và dữ liệu tệp. Phần dữ liệu tệp có thể bao gồm bảng tiêu đề chương trình mô tả không hoặc nhiều phân đoạn, bảng tiêu đề phần mô tả không hoặc nhiều phần, theo sau là dữ liệu được tham chiếu bởi các mục từ bảng tiêu đề chương trình và bảng tiêu đề phần. Mỗi phân đoạn chứa thông tin cần thiết cho thời gian chạy tệp, trong khi các phần chứa dữ liệu quan trọng để liên kết và di dời. Hình 1 minh họa điều này theo sơ đồ.

Tiêu đề ELF

Tiêu đề ELF dài 32 byte và xác định định dạng của tệp. Nó bắt đầu bằng một chuỗi bốn byte duy nhất là 0x7F, theo sau là 0x45, 0x4c và 0x46, dịch thành ba chữ cái E, L và F. Trong số các giá trị khác, tiêu đề cũng cho biết liệu đó có phải là tệp ELF cho 32 hay không Định dạng 64-bit, sử dụng ít hoặc nhiều, hiển thị phiên bản ELF cũng như hệ điều hành mà tệp được biên dịch để tương thích với giao diện nhị phân ứng dụng phù hợp (ABI) và tập lệnh cpu.

Hexdump của lần chạm tệp nhị phân trông như sau:

. Liệt kê 2: Bản hexdump của tệp nhị phân

$ hd / usr / bin / touch | đầu -5
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |. TỰ ........... |
00000010 02 00 3e 00 01 00 00 00 e3 25 40 00 00 00 00 00 | ..> ......% @ ..... |
00000020 40 00 00 00 00 00 00 00 28 e4 00 00 00 00 00 00 | @ ....... (....... |
00000030 00 00 00 00 40 00 38 00 09 00 40 00 1b 00 1a 00 | [email được bảo vệ] @ ..... |
00000040 06 00 00 00 05 00 00 00 40 00 00 00 00 00 00 00 | [email được bảo vệ] |

Debian GNU / Linux cung cấp lệnh readelf được cung cấp trong gói GNU ‘binutils’. Đi kèm với switch -h (phiên bản viết tắt của –file-header), nó hiển thị tiêu đề của tệp ELF một cách độc đáo. Liệt kê 3 minh họa điều này cho thao tác chạm lệnh.

. Liệt kê 3: Hiển thị tiêu đề của tệp ELF

$ readelf -h / usr / bin / touch
Tiêu đề ELF:
Phép thuật: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Lớp: ELF64
Dữ liệu: phần bổ sung của 2, phần cuối nhỏ
Phiên bản: 1 (hiện tại)
OS / ABI: UNIX - Hệ thống V
Phiên bản ABI: 0
Loại: EXEC (Tệp thực thi)
Máy: Advanced Micro Devices X86-64
Phiên bản: 0x1
Địa chỉ điểm vào: 0x4025e3
Bắt đầu của tiêu đề chương trình: 64 (byte vào tệp)
Đầu phần tiêu đề: 58408 (byte vào tệp)
Cờ: 0x0
Kích thước của tiêu đề này: 64 (byte)
Kích thước của tiêu đề chương trình: 56 (byte)
Số lượng tiêu đề chương trình: 9
Kích thước của tiêu đề phần: 64 (byte)
Số lượng tiêu đề phần: 27
Chỉ mục bảng chuỗi tiêu đề phần: 26

Tiêu đề chương trình

Tiêu đề chương trình hiển thị các phân đoạn được sử dụng trong thời gian chạy và cho hệ thống biết cách tạo hình ảnh quy trình. Tiêu đề từ Liệt kê 2 cho thấy rằng tệp ELF bao gồm 9 tiêu đề chương trình có kích thước mỗi tiêu đề là 56 byte và tiêu đề đầu tiên bắt đầu từ byte 64.

Một lần nữa, lệnh readelf giúp trích xuất thông tin từ tệp ELF. Công tắc -l (viết tắt của –program-headers hoặc –segment) tiết lộ nhiều chi tiết hơn như được hiển thị trong Liệt kê 4.

. Liệt kê 4: Hiển thị thông tin về tiêu đề chương trình

$ readelf -l / usr / bin / touch

Loại tệp Elf là EXEC (Tệp thực thi)
Điểm đầu vào 0x4025e3
Có 9 tiêu đề chương trình, bắt đầu từ độ lệch 64

Tiêu đề chương trình:
Nhập Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 1
[Yêu cầu trình thông dịch chương trình: /lib64/ld-linux-x86-64.so.2]
TẢI 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x000000000000d494 0x000000000000d494 R E 200000
TẢI 0x000000000000de10 0x000000000060de10 0x000000000060de10
0x0000000000000524 0x0000000000000748 RW 200000
ĐỘNG 0x00000000000000de28 0x000000000060de28 0x000000000060de28
0x00000000000001d0 0x00000000000001d0 RW 8
LƯU Ý 0x0000000000000254 0x0000000000400254 0x0000000000400254
0x0000000000000044 0x0000000000000044 R 4
GNU_EH_FRAME 0x000000000000bc40 0x000000000040bc40 0x000000000040bc40
0x00000000000003a4 0x00000000000003a4 R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 10
GNU_RELRO 0x00000000000000de10 0x000000000060de10 0x000000000060de10
0x00000000000001f0 0x00000000000001f0 R 1

Ánh xạ Phần tới Phân đoạn:
Phần phân đoạn ...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini. rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04. Động lực học
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got

Tiêu đề phần

Phần thứ ba của cấu trúc ELF là phần tiêu đề. Nó có nghĩa là để liệt kê các phần đơn lẻ của nhị phân. Công tắc -S (viết tắt của –section-headers hoặc –section) liệt kê các tiêu đề khác nhau. Đối với lệnh cảm ứng, có 27 tiêu đề phần và Liệt kê 5 chỉ hiển thị bốn tiêu đề đầu tiên cộng với tiêu đề cuối cùng. Mỗi dòng bao gồm kích thước phần, loại phần cũng như địa chỉ và khoảng trống bộ nhớ của nó.

. Liệt kê 5: Chi tiết phần do chính bạn tiết lộ

$ readelf -S / usr / bin / touch
Có 27 tiêu đề phần, bắt đầu từ phần bù 0xe428:

Tiêu đề phần:
[Nr] Tên Loại Địa chỉ Chênh lệch
Kích thước EntSize Flags Thông tin liên kết Căn chỉnh
[0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[1] .interp LỢI NHUẬN 0000000000400238 00000238
000000000000001c 0000000000000000 A 0 0 1
[2] Thẻ .note.ABI LƯU Ý 0000000000400254 00000254
0000000000000020 0000000000000000 A 0 0 4
[3] .note.gnu.build-i LƯU Ý 0000000000400274 00000274
...
...
[26] .shstrtab STRTAB 0000000000000000 0000e334
00000000000000ef 0000000000000000 0 0 1
Chìa khóa để gắn cờ:
W (ghi), A (cấp phát), X (thực hiện), M (hợp nhất), S (chuỗi), l (lớn)
I (thông tin), L (thứ tự liên kết), G (nhóm), T (TLS), E (loại trừ), x (không xác định)
O (yêu cầu xử lý thêm hệ điều hành) o (dành riêng cho hệ điều hành), p (dành riêng cho bộ xử lý)

Các công cụ để phân tích tệp ELF

Như bạn có thể đã lưu ý từ các ví dụ trên, GNU / Linux được bổ sung một số công cụ hữu ích giúp bạn phân tích tệp ELF. Ứng cử viên đầu tiên mà chúng tôi sẽ xem xét là tiện ích tệp.

tệp hiển thị thông tin cơ bản về tệp ELF, bao gồm kiến ​​trúc tập lệnh mà mã trong tệp đối tượng có thể định vị lại, thực thi hoặc chia sẻ được dự định. Trong danh sách 6, nó cho bạn biết rằng / bin / touch là một tệp thực thi 64-bit tuân theo Cơ sở Tiêu chuẩn Linux (LSB), được liên kết động và được xây dựng cho nhân GNU / Linux phiên bản 2.6.32.

. Liệt kê 6: Thông tin cơ bản sử dụng tệp

$ file / bin / touch
/ bin / touch: ELF 64-bit LSB thực thi, x86-64, phiên bản 1 (SYSV), được liên kết động, trình thông dịch / lib64 / l,
cho GNU / Linux 2.6.32, BuildID [sha1] = ec08d609e9e8e73d4be6134541a472ad0ea34502, bị tước bỏ
$

Ứng cử viên thứ hai là chính mình. Nó hiển thị thông tin chi tiết về tệp ELF. Danh sách các công tắc tương đối dài và bao gồm tất cả các khía cạnh của định dạng ELF. Sử dụng công tắc -n (viết tắt của –notes) Liệt kê 7 chỉ hiển thị các phần ghi chú tồn tại trong tệp chạm - thẻ phiên bản ABI và chuỗi bit ID bản dựng.

. Liệt kê 7: Hiển thị các phần đã chọn của tệp ELF

$ readelf -n / usr / bin / touch

Hiển thị các ghi chú được tìm thấy ở độ lệch tệp 0x00000254 với độ dài 0x00000020:
Kích thước dữ liệu chủ sở hữu Mô tả
GNU 0x00000010 NT_GNU_ABI_TAG (thẻ phiên bản ABI)
Hệ điều hành: Linux, ABI: 2.6.32

Hiển thị các ghi chú được tìm thấy ở độ lệch tệp 0x00000274 với độ dài 0x00000024:
Kích thước dữ liệu chủ sở hữu Mô tả
GNU 0x00000014 NT_GNU_BUILD_ID (chuỗi bit ID bản dựng duy nhất)
ID bản dựng: ec08d609e9e8e73d4be6134541a472ad0ea34502

Lưu ý rằng trong Solaris và FreeBSD, tiện ích elfdump [7] tương ứng với bản thân. Kể từ năm 2019, không có bản phát hành hoặc bản cập nhật mới nào kể từ năm 2003.

Thứ ba là gói có tên elfutils [6] hoàn toàn có sẵn cho Linux. Nó cung cấp các công cụ thay thế cho GNU Binutils, và cũng cho phép xác thực các tệp ELF. Lưu ý rằng tất cả tên của các tiện ích được cung cấp trong gói bắt đầu bằng eu cho ‘elf utils’.

Cuối cùng nhưng không kém phần quan trọng, chúng tôi sẽ đề cập đến objdump. Công cụ này tương tự như readelf nhưng tập trung vào các tệp đối tượng. Nó cung cấp một loạt thông tin tương tự về các tệp ELF và các định dạng đối tượng khác.

. Liệt kê 8: Thông tin tệp được trích xuất bởi objdump

$ objdump -f / bin / touch

/ bin / touch: định dạng tệp elf64-x86-64
kiến trúc: i386: x86-64, cờ 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
địa chỉ bắt đầu 0x00000000004025e3

$

Ngoài ra còn có một gói phần mềm được gọi là ‘elfkickers’ [9] chứa các công cụ để đọc nội dung của tệp ELF cũng như thao tác với nó. Thật không may, số lượng bản phát hành khá thấp và đó là lý do tại sao chúng tôi chỉ đề cập đến nó và không hiển thị thêm ví dụ.

Với tư cách là nhà phát triển, bạn có thể xem ‘pax-utils’ [10,11]. Bộ tiện ích này cung cấp một số công cụ giúp xác thực các tệp ELF. Ví dụ, bản kết xuất phân tích tệp ELF và trả về tệp tiêu đề C chứa các chi tiết - xem Hình 2.

Phần kết luận

Nhờ sự kết hợp giữa thiết kế thông minh và tài liệu xuất sắc, định dạng ELF hoạt động rất tốt và vẫn được sử dụng sau 20 năm. Các tiện ích hiển thị ở trên cho phép bạn có cái nhìn sâu sắc về tệp ELF và cho phép bạn tìm ra chương trình đang làm gì. Đây là những bước đầu tiên để phân tích phần mềm - chúc bạn hack!

Liên kết và tài liệu tham khảo
Sự nhìn nhận

Người viết xin chân thành cảm ơn sự hỗ trợ của Axel Beckert đối với việc chuẩn bị bài viết này.