
Linux共有ライブラリからシェルコードローダー
Donut for Linuxは、Linux ELF共有オブジェクト(.so)を位置独立な.binブロブに変換し、メモリから直接実行できるようにします(例えば、mmap領域にコピーして関数ポインタとしてジャンプするなど)。
このプロジェクトは意図的にmemfd(memfd_create、execveat on memfdなど)を回避しています。
amd64arm64386要件:
go generateおよびテストに必要)CLIをビルド:
make
hello-worldの.soをビルドし、それを.binに変換し、付属のランナーで実行します:
# Build payload .so
go build -buildmode=c-shared -o /tmp/hello.so ./testdata/hello
# Convert .so -> .bin (call the exported symbol "Hello")
./malasada --call-export Hello -o /tmp/hello.bin /tmp/hello.so
# Optional: compress the embedded payload (stage0 will depack before loading)
./malasada --compress --call-export Hello -o /tmp/hello.compressed.bin /tmp/hello.so
# Build the runner (PIC shellcode executor) with zig cc
zig cc -O2 -o /tmp/runner ./testdata/runner/runner.c
# Run it (stage0 hands off to ld-linux; runner will not return)
/tmp/runner /tmp/hello.bin
期待される出力には次のものが含まれます:
hello from go
リポジトリには事前ビルドされたstage0ブロブが埋め込まれています:
internal/stage0/stage0_linux_amd64.bininternal/stage0/stage0_linux_arm64.bininternal/stage0/stage0_linux_386.bininternal/stage0/stage0.cまたはinternal/stage0/linker.ldを編集した場合は、再度生成します:
go generate ./...
CLIは常に埋め込まれたstage0ブロブを使用します(ランタイムにZigは不要)。stage0を変更するには、internal/stage0/stage0.cを編集して再実行します:
go generate ./...
testdata/DockerfileはCLIをビルドし、helloの.soをビルドし、.binに変換し、Zigでランナーをビルドし、Linuxコンテナ内でエンドツーエンドテストを実行します。
例:
docker buildx build --platform linux/amd64 -f testdata/Dockerfile .
docker buildx build --platform linux/arm64 -f testdata/Dockerfile .
docker buildx build --platform linux/386 -f testdata/Dockerfile .
# Or via Makefile:
make docker-test-386