
아키텍처에 구애받지 않는 ELF 파일 플래트너 for shellcode
elfloader는 ELF 파일을 위한 매우 간단한 로더로, ELF의 플랫 인메모리 표현을 생성합니다.
이를 Rust와 결합하면 이제 적절하고 안전하며 고수준의 언어로 셸코드를 작성할 수 있습니다. LLVM이 대상으로 삼을 수 있는 모든 대상(매우 이국적인 플랫폼과 ABI를 위한 사용자 정의 대상 사양 포함)을 사용할 수 있습니다. 32비트 시스템에서 u64 같은 것을 사용하거나, 경계 검사 배열, 할당의 드롭 처리 등을 즐기세요 :)
단순히 모든 LOAD 섹션을 함께 연결하며, 간격이 있으면 0으로 패딩하여 하나의 큰 플랫 파일을 만듭니다.
이 파일은 .bss 섹션의 0 초기화를 포함하므로 셸코드 페이로드로 직접 사용할 수 있습니다.
실패-열림(fail-open) 링커 스크립트로 시간을 낭비하고 싶지 않다면, 이것이 아마 좋은 방법일 것입니다.
이 도구는 재배치(relocation)를 처리하지 않습니다. 원래 ELF가 원하는 주소에 기반(base)을 두고 있는지 확인하는 것은 사용자의 책임입니다.
이 도구를 사용하려면 간단히:
Usage: elfloader [--perms] [--binary] [--base=<addr>] <input ELF> <output>
--binary - FELF를 출력하지 않고, 메타데이터 없이 원시 로드 이미지를 출력합니다.
--perms - 권한 데이터를 포함하는 FELF0002를 생성하며, --binary를 무시합니다.
--base=<addr> - 출력이 `<addr>`에서 시작하도록 강제하며, 필요한 경우 베이스에서 첫 번째 LOAD 세그먼트 시작까지 0으로 패딩합니다.
`<addr>`은 기본적으로 16진수이며, `0d`, `0b`, `0x`, 또는 `0o` 접두사로 재정의할 수 있습니다.
경고: 베이스로 _재배치_ 하는 것이 아니라, 단순히 출력을 `<addr>`에서 시작합니다(원래 ELF 베이스 대신 `<addr>`에서 로드할 수 있도록 0바이트를 추가).
<input ELF> - 입력 ELF의 경로
<output> - 출력 파일의 경로
이 도구를 설치하려면 다음을 실행하세요:
cargo install --path .
이제 셸 어디에서나 elfloader를 사용할 수 있습니다!
이 프로젝트는 라이브로 개발되었습니다:
https://www.youtube.com/watch?v=x0V-CEmXQCQ
example_small_program에 예제가 있습니다. 간단히 make 또는 nmake를 실행하면 8바이트인 example.bin이 생성됩니다.
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 섹션만을 제외한 다른 것은 신경 쓰지 않습니다. ELF 헤더에서 엔디언(리틀/빅)과 비트 수(32/64)를 확인하고, 그로부터 프로그램 헤더의 가상 주소(로드되는 위치), 파일 크기(초기화된 바이트 수), 메모리 크기(실제 메모리 영역의 크기)를 기반으로 플랫 이미지를 생성합니다. 바이트는 파일에서 오프셋과 파일 크기를 기준으로 초기화되며, 이후 메모리 크기까지 0으로 확장되거나(또는 메모리 크기가 파일 크기보다 작으면 잘림) 처리됩니다.
이러한 LOAD 섹션들은 간격이 있을 경우 0바이트 패딩으로 함께 연결됩니다.
이는 매우 간단하게 설계되었으며, ELF 입력에 대해 중립적입니다. 실행 파일, 개체 파일, 공유 라이브러리, 코어 덤프 등 무엇이든 상관하지 않습니다. 단순히 메모리의 플랫 표현을 제공할 뿐, 그 이상은 없습니다.
이를 통해 모든 ELF를 셸코드로 변환하거나, 임베디드 장치와 같이 접근하기 어려운 곳에 로드하기 쉬운 더 간단한 파일 형식으로 변환할 수 있습니다. 개인적으로 저는 MIPS NT 4.0 로더를 위해 이 도구를 개발했으며, 이를 통해 Rust 코드를 실행할 수 있습니다.
이 도구는 기본적으로 FELF 파일 형식을 생성합니다. 이는 Falk ELF입니다. 간단한 파일 형식입니다:
FELF0001 - 매직 헤더
entry - 64비트 리틀 엔디언 정수, 엔트리 포인트 주소
base - 64비트 리틀 엔디언 정수, 이미지를 로드할 베이스 주소
<image> - 파일의 나머지 부분은 원시 이미지로, `base`에 로드되고 `entry`로 점프합니다.
이 도구는 기본적으로 FELF 파일 형식을 생성합니다. 이는 Falk ELF입니다. 권한이 있는 간단한 파일 형식입니다:
FELF0002 - 매직 헤더
entry - 64비트 리틀 엔디언 정수, 엔트리 포인트 주소
base - 64비트 리틀 엔디언 정수, 이미지를 로드할 베이스 주소
<image> - 파일의 나머지 부분은 원시 이미지로, `base`에 로드되고 `entry`로 점프합니다.
<perms> - 권한. <image>의 바이트와 일치하며, 각 바이트는 다음 플래그를 비트 OR로 결합한 값을 포함합니다:
0x01 - 실행 가능, 0x02 - 쓰기 가능, 0x04 - 읽기 가능
패딩 바이트는 0x00이 되며, 따라서 어떤 접근에 대한 권한도 없습니다.