Czy istnieje sposób na wykonanie natywnego pliku binarnego z potoku?

13
echo 'main(){}' | gcc -xc - -o /dev/stdout | ???

Czy istnieje sposób na uruchomienie wyjściowego pliku binarnego w systemie uniksopodobnym?

EDYCJA: Potrzebowałem go do uruchomienia wyjścia g ++ w środowisku piaskownicy, w którym nie mogę napisać żadnego pliku (nic złego, obiecuję).

Alex B.
źródło
Uważam, że podstawowe mechanizmy bezpieczeństwa muszą temu zapobiec. Ale jeśli masz zamiar uruchamiać kod C w locie, po prostu użyj csh.
rozcietrzewiacz

Odpowiedzi:

10

Nie wierzę, że to jest możliwe. Exec (2) połączenie systemu zawsze wymaga pliku lub całkowitej ścieżki (nazwa pliku jest zawsze char*). posix_spawnma również podobne wymagania dotyczące nazwy pliku.

Najbliższe, co możesz zrobić, to przesłać wyjście do nazwanego potoku i spróbować wykonać z potoku. Może to działać, chociaż powłoka może odmówić wykonania dowolnego pliku, który nie ma --x--x--xustawionych bitów. Utwórz potok za pomocą mkfifo(1)i sprawdź, czy możesz go uruchomić.

Innym podejściem byłoby napisanie czegoś, co odczytuje standardowe dane wejściowe, zapisuje plik do obszaru tymczasowego, ustawia na nim bity --x, rozwidla i wykonuje, a następnie usuwa plik. I-węzeł i zawartość pozostaną, dopóki program nie zakończy działania, ale nie będzie dostępny przez system plików. Po zakończeniu procesu i-węzeł zostanie zwolniony, a pamięć masowa zostanie zwrócona na bezpłatną listę.

EDYCJA: Jak zauważa Mat, pierwsze podejście nie zadziała, ponieważ moduł ładujący spróbuje wywołać stronę w pliku wykonywalnym, co wygeneruje losowy ruch związany z wyszukiwaniem pliku, a nie jest to możliwe w potoku. To pozostawia jakieś podejście jak drugie.

ConcernedOfTunbridgeWells
źródło
2
Byłbym naprawdę zaskoczony, gdyby sztuczka z fajką zadziałała - nie można wykonywać losowych wyszukiwań na rurze i nie można ich mapować - jestem pewien, że zirytuje moduł ładujący / łączący środowisko uruchomieniowe :) Twoja druga sugestia wydaje się dobra jednak nie może niczego wymyślić bez pliku tymczasowego.
Mat
@Mat - Myślę, że masz rację. Stronicowanie popytu w pliku wykonywalnym spowodowałoby ruch o swobodnym dostępie, który nie będzie działał na potoku. Jak na ironię, mogło faktycznie działać na SVR2.0 (ostatnia wersja, która nie używała stronicowania na żądanie) - aby pokazać swój wiek, tak naprawdę miałem AT&T 3B2 / 400 raz z SVR2.0 jako O / S.
ConcernedOfTunbridgeWells
Zastanawiając się nad tym, jestem pewien, że programy exe, takie jak UPX, mogą wykonać dekompresję i wykonanie na nośniku tylko do odczytu. Zmodyfikuj wszystkie kody pośredniczące, które dołączają, do spakowanych plików wykonywalnych w celu odczytu z potoku zamiast dekompresji, i ... może działać.
Mat.
Pakiety @Mat mają już załadowany obraz, nie rozpoczynają nowego procesu. Aby zrobić podobnie, muszę mieć jeden z procesów, aby wykonać dowolny skok do danych wejściowych (co byłoby uważane za lukę w zabezpieczeniach).
Alex B,
@Alex B: W szczególności pytasz, jak wykonać dowolny skok do danych wejściowych. Dlaczego miałbyś narzekać, skoro sugeruje się, że właśnie to robisz? Czy celem piaskownicy jest specjalnie zapobieganie temu, co próbujesz zrobić?
David Schwartz
7

Rozwiązanie wykorzystujące system memfd syscall: https://github.com/abbat/elfexec

Tworzy nazwany deskryptor pliku w pamięci, który można wykorzystać w exec. Pseudokod:

#include <linux/memfd.h>
...
int memfd = syscall(SYS_memfd_create, "someName", 0);
...
write(memfd,... elf-content...);
...
fexecve(memfd, argv, environ);
kan
źródło
1
Nie potrzebujesz memfd.hnagłówka, chyba że chcesz go użyć MFD_CLOEXEC(który zepsuje #! /bin/shskrypty z powodu błędów w systemie Linux fexecve()). To nie jest zbyt skomplikowane, możesz dołączyć 20-liniową próbkę roboczą do swojej odpowiedzi (np. Ten git gist - chociaż nie jest to zamiana drop-in dla ciebie elfexec, ponieważ pozwoli ci to określić argv[0]i uruchomi binarny tylko z fajki (mandat UUoC ;-))
mosvy
Więc nie rozumiem, jak to w ogóle powinno działać. gcc zapisze .opliki w / tmp i umrze, jeśli nie będzie.
Joshua
4

Możesz spróbować tcc , który skompiluje i uruchomi program w jednym kroku, bez zapisywania plików pośrednich. To nie jest gcc, co może być dla ciebie problemem, ale jest spektakularnie szybki, więc może nawet postawić lepiej niż gcc dla twoich celów.

TheQUUX
źródło
2

Spowoduje to automatyczne uruchomienie kompilacji kodu, ale w tym celu utworzy plik (tymczasowo) w systemie plików.

echo 'main(){}' | gcc -xc -o /tmp/a.out && chmod u+x /tmp/a.out && /tmp/a.out && rm -f /tmp/a.out

(Obecnie testuję to teraz, ale jestem całkiem pewien, że to, czy coś blisko to zadziała dla Ciebie)

EDYCJA: Jeśli celem twojego rurociągu jest wycięcie dysków fizycznych z równania prędkości, rozważ utworzenie dysku RAM, który pomieści plik pośredni.

dtyler
źródło
Działa to oczywiście, ale pomija główny punkt pytania - wykonanie kodu binarnego, który nigdy nie jest zapisywany na dysku.
rozcietrzewiacz
@rozcietrzewiacz Miałem nadzieję, że przydałoby się, gdyby celem było łatwe uruchomienie fragmentu kodu w locie bez zajmowania się wymaganym plikiem fizycznym.
dtyler
Tak, rozumiem. Ale do tego można po prostu użyć csh.
rozcietrzewiacz