C (x86_64), 11, 30, 34 lub 34 + 15 = 49 bajtów
main[]="/";
c=6;main(){((void(*)())&c)();}
main(){int c=6;((void(*)())&c)();}
Przedstawiłem kilka rozwiązań, które używają funkcji bibliotecznych do rzucania SIGILL
na różne sposoby, ale prawdopodobnie to oszustwo, ponieważ funkcja biblioteczna rozwiązuje problem. Oto szereg rozwiązań, które nie wykorzystują funkcji bibliotecznych i przyjmują różne założenia dotyczące tego, gdzie system operacyjny jest skłonny pozwolić na wykonanie kodu niewykonywalnego. (Stałe tutaj są wybrane dla x86_64, ale możesz je zmienić, aby uzyskać działające rozwiązania dla większości innych procesorów, które mają nielegalne instrukcje).
06
jest bajtem kodu maszynowego o najniższym numerze, który nie odpowiada zdefiniowanej instrukcji procesora x86_64. Więc wszystko, co musimy zrobić, to go wykonać. (Alternatywnie 2F
jest również niezdefiniowany i odpowiada jednemu drukowanemu znakowi ASCII.) Żadnemu z nich nie gwarantuje się, że zawsze będzie niezdefiniowany, ale na dzień dzisiejszy nie jest zdefiniowany.
Pierwszy program tutaj wykonuje się 2F
z segmentu danych tylko do odczytu. Większość łączniki nie są zdolne do wytwarzania skok roboczy od .text
do .rodata
(lub równoważny ich systemów operacyjnych), jak to nie jest coś, co nigdy nie będzie przydatna w prawidłowej segmentacji programu; Nie znalazłem jeszcze systemu operacyjnego, na którym to działa. Trzeba też dopuścić fakt, że wiele kompilatorów chce, aby dany ciąg był szerokim ciągiem, co wymagałoby dodatkowegoL
; Zakładam, że każdy system operacyjny, na którym działa, ma dość przestarzały obraz rzeczy, dlatego domyślnie buduje się na standard wcześniejszy niż C94. Możliwe, że nigdzie ten program nie działa, ale jest też możliwe, że gdzieś ten program działa, dlatego wymienię go w tej kolekcji bardziej wątpliwych do mniej wątpliwych potencjalnych odpowiedzi. (Po opublikowaniu tej odpowiedzi Dennis wspomniał również o możliwości main[]={6}
na czacie, która jest tej samej długości i która nie ma problemów z szerokością postaci, a nawet zasygnalizowała potencjał main=6
; Nie mogę racjonalnie twierdzić, że te odpowiedzi są mój, bo sam o nich nie myślałem.)
Drugi program tutaj wykonuje się 06
z segmentu danych do odczytu i zapisu. W większości systemów operacyjnych spowoduje to błąd segmentacji, ponieważ zapisywalne segmenty danych są uważane za wadę projektową, która sprawia, że exploity są prawdopodobne. Jednak nie zawsze tak było, więc prawdopodobnie działa na wystarczająco starej wersji Linuksa, ale nie mogę tego łatwo przetestować.
Trzeci program wykonuje się 06
ze stosu. Ponownie powoduje to obecnie błąd segmentacji, ponieważ ze względów bezpieczeństwa stos jest zwykle klasyfikowany jako niepisujący. Dokumentacja konsolidatora, którą widziałem, wyraźnie sugeruje, że wykonywanie ze stosu było legalne (w przeciwieństwie do poprzednich dwóch przypadków, czasami jest to przydatne), więc chociaż nie mogę tego przetestować, jestem pewien, że jest trochę wersja systemu Linux (i prawdopodobnie inne systemy operacyjne), na których to działa.
Wreszcie, jeśli podasz -Wl,-z,execstack
(kara 15 bajtów) dla gcc
(jeśli używasz GNU ld
jako części backendu), to jawnie wyłączy ochronę wykonywalnego stosu, pozwalając na działanie trzeciego programu i podając nieprawidłowy sygnał operacji zgodnie z oczekiwaniami. I zostały przetestowane i zweryfikowane tej wersji 49-bajtowy do pracy. (Dennis wspomina na czacie, że ta opcja najwyraźniej działa main=6
, co dałoby wynik 6 + 15. Jestem dość zaskoczony, że to działa, biorąc pod uwagę, że 6 rażąco nie jest na stosie; opcja link najwyraźniej działa więcej niż jego nazwa sugeruje.)
raise(SIGILL)
?