Я занимался хакерством в двоичном коде для этой простой программы на C ++, чтобы понять заголовки программ для ELF:
int main(){ }
, скомпилированных с:
❯ make
g++ -O0 -fverbose-asm -no-pie -o main main.cpp
Я использовал readelf -l main
для получить следующее:
Elf file type is EXEC (Executable file)
Entry point 0x401020
There are 11 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x0000000000000268 0x0000000000000268 R 0x8
INTERP 0x00000000000002a8 0x00000000004002a8 0x00000000004002a8
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000004c0 0x00000000000004c0 R 0x1000
...
Я вижу в этой документации: http://man7.org/linux/man-pages/man5/elf.5.html для PHDR:
Элемент массива, если присутствует, задает loca - количество и размер самой таблицы заголовков программы, как в файле, так и в образе памяти программы. Этот тип сегмента может встречаться в файле не более одного раза. Более того, это может произойти, только если таблица заголовков программы является частью образа памяти программы. Если он присутствует, он должен предшествовать любой загружаемой записи сегмента.
Наличие if present
в цитате заставило меня задуматься, что произойдет, если я просто перепрыгну через заголовок PHDR. Я использовал шестнадцатеричный редактор vim, чтобы изменить двоичный формат main
, используя :%!xxd
(обязательно запустите :%!xxd -r
перед сохранением, иначе это уже не двоичный файл), чтобы получить:
00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000 .ELF............
00000010: 0200 3e00 0100 0000 2010 4000 0000 0000 ..>..... .@.....
00000020: 4000 0000 0000 0000 1839 0000 0000 0000 @........9......
до:
00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000 .ELF............
00000010: 0200 3e00 0100 0000 2010 4000 0000 0000 ..>..... .@.....
00000020: 7800 0000 0000 0000 1839 0000 0000 0000 @........9......
(только изменение 20-го байта), чтобы перепрыгнуть через длину заголовка PHDR. Я снова запускаю readelf
, чтобы убедиться, что это по-прежнему действительный файл ELF:
❯ readelf -l main
Elf file type is EXEC (Executable file)
Entry point 0x401020
There are 11 program headers, starting at offset 120
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
INTERP 0x00000000000002a8 0x00000000004002a8 0x00000000004002a8
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
...
И, что удивительно, программа все еще прекрасно работает. Зачем нам вообще нужен заголовок PHDR? Полезно ли это для ссылок и / или других ситуаций? Кажется, что он вообще не используется во время выполнения, так почему у нас это лежит?