
Un aplatisseur de fichiers ELF agnostique à l'architecture pour shellcode.
elfloader est un chargeur extrêmement simple pour les fichiers ELF qui génère une
représentation plate en mémoire de l'ELF.
Associez-le à Rust et vous pouvez désormais écrire votre shellcode dans un langage
propre, sûr et de haut niveau. N'importe quelle cible que LLVM peut cibler peut être
utilisée, y compris les spécifications de cible personnalisées pour des plateformes
vraiment exotiques et des ABI. Profitez de choses comme les u64 sur des systèmes
32 bits, les tableaux avec vérification des limites, la gestion de la libération des
allocations, etc :)
Il concatène simplement toutes les sections LOAD ensemble, en utilisant un
rembourrage de zéros s'il y a des écarts, en un seul gros fichier plat.
Ce fichier inclut l'initialisation à zéro des sections .bss, et peut donc être
utilisé directement comme charge utile de shellcode.
Si vous ne voulez pas perdre de temps avec des scripts de liaison qui échouent souvent, c'est probablement une excellente façon de procéder.
Cela ne gère aucune relocalisation, c'est à vous de vous assurer que l'ELF original est basé à l'adresse que vous souhaitez.
Pour utiliser cet outil, simplement :
Usage: elfloader [--perms] [--binary] [--base=<addr>] <input ELF> <output>
--binary - Don't output a FELF, output the raw loaded image with no
metadata
--perms - Create a FELF0002 which includes permission data, overrides
--binary
--base=<addr> - Force the output to start at `<addr>`, zero padding from
the base to the start of the first LOAD segment if needed.
`<addr>` is default hex, can be overrided with `0d`, `0b`,
`0x`, or `0o` prefixes.
Warning: This does not _relocate_ to base, it simply starts
the output at `<addr>` (adding zero bytes such that the
output image can be loaded at `<addr>` instead of the
original ELF base)
<input ELF> - Path to input ELF
<output> - Path to output file
Pour installer cet outil, exécutez :
cargo install --path .
Vous pouvez maintenant utiliser elfloader depuis n'importe où dans votre
terminal !
Ce projet a été développé en direct ici :
https://www.youtube.com/watch?v=x0V-CEmXQCQ
Il y a un exemple dans example_small_program, exécutez simplement make ou
nmake et cela devrait générer un example.bin de 8 octets.
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
Vous pouvez maintenant écrire votre shellcode en Rust, sans vous soucier de
savoir si vous émettez .data, .rodata, .bss, etc. Cela s'en occupe pour
vous !
Il y a aussi un exemple avec .bss et .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
Cet outil ne se soucie de rien d'autre que des sections LOAD. Il détermine
l'endianness (petit ou grand) et la taille en bits (32 ou 64) à partir de
l'en-tête ELF, et à partir de là, il crée une image plate basée sur les
adresses virtuelles des en-têtes de programme (là où il est chargé), la taille
du fichier (nombre d'octets initialisés) et la taille mémoire (taille de la
région mémoire réelle). Les octets sont initialisés à partir du fichier en
fonction du décalage et de la taille du fichier, puis étendus avec des zéros
jusqu'à la taille mémoire (ou tronqués si la taille mémoire est plus petite
que la taille du fichier).
Ces sections LOAD sont ensuite concaténées ensemble avec un rembourrage
d'octets nuls pour les écarts.
Cela est conçu pour être incroyablement simple et indépendant de l'entrée ELF. Il peut s'agir d'un exécutable, d'un fichier objet, d'un objet partagé, d'un vidage mémoire, etc., l'outil ne se soucie pas vraiment. Il vous donnera simplement la représentation plate de la mémoire, rien de plus.
Cela vous permet de transformer n'importe quel ELF en shellcode, ou en un format de fichier plus simple à charger dans des zones difficiles d'accès, comme les appareils embarqués. Personnellement, j'ai développé cela pour mon chargeur MIPS NT 4.0 qui me permet d'exécuter du code Rust.
Par défaut, cet outil génère un format de fichier FELF. C'est un Falk ELF. C'est un format de fichier simple :
FELF0001 - En-tête magique
entry - Entier 64 bits en little endian de l'adresse du point d'entrée
base - Entier 64 bits en little endian de l'adresse de base pour charger l'image
<image> - Le reste du fichier est l'image brute, à charger à `base` et à sauter
vers à `entry`
Par défaut, cet outil génère un format de fichier FELF. C'est un Falk ELF. C'est un format de fichier simple avec permissions :
FELF0002 - En-tête magique
entry - Entier 64 bits en little endian de l'adresse du point d'entrée
base - Entier 64 bits en little endian de l'adresse de base pour charger l'image
<image> - Le reste du fichier est l'image brute, à charger à `base` et à sauter
vers à `entry`
<perms> - Permissions, correspondant aux octets de <image> où l'octet contient
les drapeaux suivants combinés par OU bit à bit :
0x01 - Exécutable, 0x02 - Écriture, 0x04 - Lecture
Les octets de remplissage seront 0x00 et n'auront donc aucune permission
pour tout accès