
Архитектурно-независимый уплощитель ELF-файлов для shellcode
elfloader — это чрезвычайно простой загрузчик ELF-файлов, который формирует плоское (flat) представление ELF в памяти.
Объедините это с Rust, и теперь вы сможете писать свой шелл-код на полноценном, безопасном, высокоуровневом языке. Подойдёт любая платформа, которую поддерживает LLVM, включая пользовательские спецификации целевых платформ для действительно экзотических систем и ABI. Наслаждайтесь использованием таких вещей, как u64 на 32-битных системах, массивы с проверкой границ, обработка освобождения выделений и т. д. :)
Он просто объединяет все секции LOAD вместе, используя заполнение нулями, если есть пробелы, в один большой плоский файл.
Этот файл включает нулевую инициализацию секций .bss и, следовательно, может использоваться непосредственно как полезная нагрузка шелл-кода.
Если вы не хотите тратить время на компоновочные скрипты, которые работают «как получится», это, вероятно, отличный способ.
Этот инструмент не обрабатывает перемещения (relocations); ответственность за то, чтобы исходный ELF был размещён по нужному вам адресу, лежит на вас.
Чтобы использовать этот инструмент, просто:
Usage: elfloader [--perms] [--binary] [--base=<addr>] <input ELF> <output>
--binary - Не выводить FELF, вывести необработанный загруженный образ без метаданных
--perms - Создать FELF0002, который включает данные о правах доступа; переопределяет --binary
--base=<addr> - Принудительно начать вывод с адреса `<addr>`, при необходимости дополнив нулями от базы до начала первого сегмента LOAD.
`<addr>` по умолчанию в hex, можно переопределить префиксами `0d`, `0b`, `0x` или `0o`.
Предупреждение: это не _перемещает_ (relocate) по базе, а просто начинает вывод с `<addr>` (добавляя нулевые байты так, чтобы выходной образ можно было загрузить по `<addr>` вместо исходной базы ELF)
<input ELF> - Путь к входному ELF
<output> - Путь к выходному файлу
Чтобы установить этот инструмент, выполните:
cargo install --path .
Теперь вы можете использовать elfloader из любого места в вашей оболочке!
Этот проект разрабатывался в прямом эфире здесь:
https://www.youtube.com/watch?v=x0V-CEmXQCQ
Пример есть в example_small_program, просто выполните make или nmake, и это должно сгенерировать example.bin размером 8 байт.
pleb@gamey ~/elfloader/example_small_program $ make
cargo build --release
Finished release [optimized] target(s) in 0.03s
elfloader --binary target/aarch64-unknown-none/release/example_small_program example.bin
pleb@gamey ~/elfloader/example_small_program $ ls -l ./example.bin
-rw-r--r-- 1 pleb pleb 8 Nov 8 12:27 ./example.bin
pleb@gamey ~/elfloader/example_small_program $ objdump -d target/aarch64-unknown-none/release/example_small_program
target/aarch64-unknown-none/release/example_small_program: file format elf64-littleaarch64
Disassembly of section .text:
00000000133700b0 <_start>:
133700b0: 8b000020 add x0, x1, x0
133700b4: d65f03c0 ret
Теперь вы можете писать свой шелл-код на Rust и не беспокоиться о том, используете ли вы .data, .rodata, .bss и т. д. Этот инструмент позаботится обо всём сам!
Также есть пример с .bss и .rodata
pleb@gamey ~/elfloader/example_program_with_data $ make
cargo build --release
Finished release [optimized] target(s) in 0.04s
elfloader --binary target/aarch64-unknown-none/release/example_program_with_data example.bin
pleb@gamey ~/elfloader/example_program_with_data $ ls -l ./example.bin
-rw-r--r-- 1 pleb pleb 29 Nov 8 12:39 ./example.bin
pleb@gamey ~/elfloader/example_program_with_data $ objdump -d target/aarch64-unknown-none/release/example_program_with_data
target/aarch64-unknown-none/release/example_program_with_data: file format elf64-littleaarch64
Disassembly of section .text:
0000000013370124 <_start>:
13370124: 90000000 adrp x0, 13370000 <_start-0x124>
13370128: 90000008 adrp x8, 13370000 <_start-0x124>
1337012c: 52800029 mov w9, #0x1 // #1
13370130: 91048000 add x0, x0, #0x120
13370134: 3904f109 strb w9, [x8, #316]
13370138: d65f03c0 ret
pleb@gamey ~/elfloader/example_program_with_data $ readelf -l target/aarch64-unknown-none/release/example_program_with_data
Elf file type is EXEC (Executable file)
Entry point 0x13370124
There are 4 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000120 0x0000000013370120 0x0000000013370120
0x0000000000000004 0x0000000000000004 R 0x1
LOAD 0x0000000000000124 0x0000000013370124 0x0000000013370124
0x0000000000000018 0x0000000000000018 R E 0x4
LOAD 0x000000000000013c 0x000000001337013c 0x000000001337013c
0x0000000000000000 0x0000000000000001 RW 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x0
Section to Segment mapping:
Segment Sections...
00 .rodata
01 .text
02 .bss
03
Этот инструмент не заботится ни о чём, кроме секций LOAD. Он определяет порядок байт (little vs big) и разрядность (32 vs 64) из заголовка ELF, а затем создаёт плоский образ на основе виртуальных адресов программных заголовков (куда загружается), размера файла (количество инициализированных байт) и размера памяти (размер фактической области памяти). Байты инициализируются из файла на основе смещения и размера файла, а затем дополняются нулями до размера памяти (или обрезаются, если размер памяти меньше размера файла).
Эти секции LOAD затем объединяются вместе с нулевым заполнением для пробелов.
Этот инструмент разработан так, чтобы быть невероятно простым и независимым от входного ELF. Это может быть исполняемый файл, объектный файл, разделяемый объект, дамп ядра и т. д. — инструменту всё равно. Он просто выдаст плоское представление памяти и ничего больше.
Это позволяет превращать любой ELF в шелл-код или в более простой формат файла, который легче загружать в труднодоступных местах, например, во встраиваемых устройствах. Лично я разработал это для своего загрузчика MIPS NT 4.0, который позволяет мне запускать код на Rust.
По умолчанию этот инструмент генерирует файл в формате FELF. Это Falk ELF. Это простой формат файла:
FELF0001 - Magic header
entry - 64-битное целое число в формате little endian, адрес точки входа
base - 64-битное целое число в формате little endian, базовый адрес загрузки образа
<image> - Остальная часть файла — необработанный образ, который нужно загрузить по адресу `base` и перейти на `entry`
По умолчанию этот инструмент генерирует файл в формате FELF. Это Falk ELF. Это простой формат файла с правами доступа:
FELF0002 - Magic header
entry - 64-битное целое число в формате little endian, адрес точки входа
base - 64-битное целое число в формате little endian, базовый адрес загрузки образа
<image> - Остальная часть файла — необработанный образ, который нужно загрузить по адресу `base` и перейти на `entry`
<perms> - Права доступа, соответствующие байтам <image>, где каждый байт содержит следующие флаги, объединённые поразрядным ИЛИ:
0x01 - Исполняемый, 0x02 - Записываемый, 0x04 - Читаемый
Заполняющие байты будут 0x00 и, следовательно, не будут иметь никаких прав доступа.