Można stworzyć obszar pamięci, który jest jednocześnie zapisywalny i wykonywalny.
To już odchodzi do przeszłości. Zmiany od dłuższego czasu idą w tym kierunku, że system nie pozwala, aby ta sama strona pamięci wirtualnej była z prawami do zapisu i wykonania.
W niektórych systemach jest to zaimplementowane i domyślnie włączone, w innych nie.
Zazwyczaj jednak nic nie stoi na przeszkodzie, aby ten sam obszar pamięci był dostępny pod jednym adresem z prawami do oczytu i wykonania, a pod innym z prawami do zapisu i wykonania.
Jednak np. macOS/AArch64 tego zabrania, przez co trzeba zmienić JITy:
https://openjdk.java.net/jeps/391
macOS/AArch64 forbids memory segments from being executable and writeable at the same time, a policy known as write-xor-execute (W^X). The HotSpot VM routinely creates and modifies executable code, so this JEP will implement W^X support in HotSpot for macOS/AArch64.
https://en.wikipedia.org/wiki/W%5EX
A także w wielu innych systemach, np. w OpenBSD, gdzie jest konfigurowalne przez flagę montowania danego filesystemu i domyślnie włączone poza /usr/local.
@WeiXiao bez własnego runtime możesz to zrobić generalnie na 2 sposoby:
- mprotect() żeby zrobić jakiś kawałek pamięci "wykonywalny" (albo jakieś
gcc -z execstack
) i wtedy możesz do pamięci wrzucić shellcode i potem go wykonać
Akurat tak tego nie należy robić, chyba że rzeczywiście to jest jakiś shellcode...
Jeden problem z tym rozwiązaniem jest taki, że jeśli zmieniasz prawa dostępu do strony pamięci za pomocą mprotect, to operujesz na całych stronach, bo tak działa zarządzanie pamięcią we współczesnych procesorach.
Zasadniczo nie należy dawać jednocześnie prawa do zapisu i wykonania, wiele systemów na to nie pozwoli. A jeśli system pozwoli, to takie prawa zmniejszają bezpieczeństwo rozwiązania.
Należy więc dać prawo do odczytu i wykonania. Ale tak, jak napisałem wcześniej, operujesz na całych stronach, więc jeśli w ten sposób zrobisz "jakiś kawałem pamięci" w ten sposób wykonywalny, to odbierzesz prawo do zapisu także do tego, co w tych samych stronach obok tego wykonywalnego kodu może się znajdować.
I przy próbie zapisu danych znajdujących się obok tego kodu dostaniesz np. SIGSEGV.
Drugi problem z tym rozwiązaniem jest taki, że jeśli w ten sposób chcesz zmodyfikować istniejący fragment kodu programu, to aby modyfikacja była możliwa, należy chociażby na chwilę dać prawo do zapisu tego obszaru pamięci. A system może nie pozwolić na tak szerokie uprawnienia (RWX), więc będzie trzeba na czas modyfikacji odebrać prawo do wykonania tego kodu.
A ponieważ ochrona pamięci operuje na stronach, odbierzesz w ten sposób prawo wykonywania kodu znajdującego się w tych samych stronach pamięci. Jeśli będzie tam kod wykonywany podczas modyfikacji (np. kod zajmujący się modyfikacją), to dostaniesz np. SIGSEGV.