Czy potrafisz wymyślić jakieś uzasadnione (inteligentne) zastosowania do modyfikacji kodu w czasie wykonywania (program modyfikujący swój własny kod w czasie wykonywania)?
Nowoczesne systemy operacyjne wydają się nienawidzić programów, które to robią, ponieważ wirusy wykorzystują tę technikę w celu uniknięcia wykrycia.
Wszystko, co przychodzi mi do głowy, to jakiś rodzaj optymalizacji środowiska wykonawczego, który usunąłby lub dodał kod, wiedząc w czasie wykonywania coś, czego nie można poznać w czasie kompilacji.
Odpowiedzi:
Istnieje wiele ważnych przypadków modyfikacji kodu. Generowanie kodu w czasie wykonywania może być przydatne do:
Czasami kod jest tłumaczony na kod w czasie wykonywania (nazywa się to dynamicznym tłumaczeniem binarnym ):
Modyfikacja kodu może posłużyć do obejścia ograniczeń zestawu instrukcji:
Więcej przypadków modyfikacji kodu:
źródło
Dokonano tego w grafice komputerowej, w szczególności w programach renderujących do celów optymalizacji. W czasie wykonywania programu sprawdzany jest stan wielu parametrów i generowana jest zoptymalizowana wersja kodu rasteryzatora (potencjalnie eliminująca wiele warunków), co pozwala na znacznie szybsze renderowanie elementów graficznych, np. Trójkątów.
źródło
Jednym z ważnych powodów jest to, że w zestawie instrukcji asm brakuje pewnych niezbędnych instrukcji, które można by stworzyć samodzielnie. Przykład: Na x86 nie ma możliwości utworzenia przerwania dla zmiennej w rejestrze (np. Wykonaj przerwanie z numerem przerwania w ax). Dozwolone były tylko liczby const zakodowane w opkodzie. Za pomocą samomodyfikującego się kodu można by naśladować to zachowanie.
źródło
Niektóre kompilatory używały go do inicjalizacji zmiennej statycznej, unikając kosztu warunkowego przy kolejnych dostępach. Innymi słowy, implementują „wykonaj ten kod tylko raz” przez nadpisanie tego kodu bez operacji przy pierwszym wykonaniu.
źródło
Jest wiele przypadków:
Modele bezpieczeństwa niektórych systemów operacyjnych oznaczają, że samomodyfikujący się kod nie może działać bez uprawnień roota / administratora, co sprawia, że jest niepraktyczny do użytku ogólnego.
Z Wikipedii:
W takich systemach operacyjnych nawet programy takie jak maszyna wirtualna Java potrzebują uprawnień roota / administratora, aby wykonać swój kod JIT. ( Więcej informacji można znaleźć pod adresem http://en.wikipedia.org/wiki/W%5EX )
źródło
System operacyjny Synthesis zasadniczo częściowo ocenił Twój program pod kątem wywołań API i zastąpił kod systemu operacyjnego wynikami. Główną korzyścią jest to, że zniknęło wiele sprawdzania błędów (ponieważ jeśli Twój program nie prosi systemu operacyjnego o zrobienie czegoś głupiego, nie musi tego sprawdzać).
Tak, to przykład optymalizacji czasu wykonywania.
źródło
Wiele lat temu spędziłem poranek próbując zdebugować jakiś samomodyfikujący się kod, jedna instrukcja zmieniła adres docelowy następnej instrukcji, tj. Obliczałem adres oddziału. Został napisany w języku asemblera i działał doskonale, kiedy przechodziłem przez program po jednej instrukcji na raz. Ale kiedy uruchomiłem program, nie udało się. W końcu zdałem sobie sprawę, że maszyna pobierała 2 instrukcje z pamięci i (ponieważ instrukcje były rozmieszczone w pamięci) instrukcja, którą modyfikowałem, została już pobrana, a zatem maszyna wykonywała niezmodyfikowaną (niepoprawną) wersję instrukcji. Oczywiście, kiedy debugowałem, wykonywałem tylko jedną instrukcję naraz.
Chodzi mi o to, że samomodyfikujący się kod może być wyjątkowo nieprzyjemny do testowania / debugowania i często ma ukryte założenia dotyczące zachowania maszyny (czy to sprzętowej, czy wirtualnej). Co więcej, system nigdy nie mógł udostępniać stron kodowych między różnymi wątkami / procesami wykonywanymi na (obecnie) maszynach wielordzeniowych. Eliminuje to wiele korzyści dla pamięci wirtualnej itp. Unieważniłoby również optymalizacje gałęzi wykonane na poziomie sprzętu.
(Uwaga - nie uwzględniam JIT w kategorii kodu samomodyfikującego się. JIT tłumaczy jedną reprezentację kodu na alternatywną reprezentację, nie modyfikuje kodu)
Podsumowując, to po prostu zły pomysł - naprawdę schludny, naprawdę niejasny, ale naprawdę zły.
oczywiście - jeśli wszystko, co masz, to 8080 i ~ 512 bajtów pamięci, być może będziesz musiał uciekać się do takich praktyk.
źródło
Z punktu widzenia jądra systemu operacyjnego każdy kompilator Just In Time i Linker Runtime dokonuje samodzielnej modyfikacji tekstu programu. Wybitnym przykładem może być Google V8 ECMA Script Interpreter.
źródło
Innym powodem samomodyfikacji kodu (właściwie kodu „samoczynnego”) jest implementacja mechanizmu kompilacji Just-In-Time w celu zwiększenia wydajności. Np. Program, który czyta wyrażenie algebiczne i oblicza je na podstawie szeregu parametrów wejściowych, może przekształcić wyrażenie w kodzie maszynowym przed podaniem obliczenia.
źródło
Wiesz, stary kasztan, że nie ma logicznej różnicy między sprzętem a oprogramowaniem ... można też powiedzieć, że nie ma logicznej różnicy między kodem a danymi.
Co to jest samomodyfikujący się kod? Kod, który umieszcza wartości w strumieniu wykonania, aby można je było interpretować nie jako dane, ale jako polecenie. Oczywiście, istnieje teoretyczny punkt widzenia w językach funkcjonalnych, że tak naprawdę nie ma różnicy. Mówię, że e może to zrobić w prosty sposób w językach imperatywnych i kompilatorach / interpreterach bez domniemania równego statusu.
Mam na myśli to, w praktycznym sensie, że dane mogą zmieniać ścieżki wykonywania programu (w pewnym sensie jest to niezwykle oczywiste). Myślę o czymś w rodzaju kompilatora-kompilatora, który tworzy tabelę (tablicę danych), przez którą przechodzi się podczas analizowania, przechodzenia od stanu do stanu (a także modyfikowania innych zmiennych), tak jak program przechodzi od polecenia do polecenia , modyfikowanie zmiennych w procesie.
Więc nawet w zwykłym przypadku, gdy kompilator tworzy przestrzeń kodu i odwołuje się do w pełni oddzielnej przestrzeni danych (sterty), nadal można zmodyfikować dane, aby jawnie zmienić ścieżkę wykonywania.
źródło
Zaimplementowałem program wykorzystujący ewolucję, aby stworzyć najlepszy algorytm. Wykorzystał samomodyfikujący się kod, aby zmodyfikować schemat DNA.
źródło
Jednym z przypadków użycia jest plik testowy EICAR, który jest legalnym plikiem wykonywalnym COM systemu DOS do testowania programów antywirusowych.
Musi używać modyfikacji kodu własnego, ponieważ plik wykonywalny musi zawierać tylko drukowalne / typowalne znaki ASCII z zakresu [21h-60h, 7Bh-7Dh], co znacznie ogranicza liczbę zakodowanych instrukcji
Szczegóły wyjaśniono tutaj
Jest również używany do wysyłania operacji zmiennoprzecinkowych w DOS
Niektóre kompilatory będą emitować
CD xx
xx z zakresu od 0x34-0x3B w miejsce instrukcji zmiennoprzecinkowych x87. PonieważCD
jest to kodint
instrukcji, przeskoczy do przerwania 34h-3Bh i emuluje tę instrukcję w oprogramowaniu, jeśli koprocesor x87 nie jest dostępny. W przeciwnym razie program obsługi przerwań zastąpi te 2 bajty9B Dx
, aby późniejsze wykonania były obsługiwane bezpośrednio przez x87 bez emulacji.Jaki jest protokół emulacji zmiennoprzecinkowej x87 w systemie MS-DOS?
źródło
Linux Kernel ma ładowalnych modułów jądra, które nie tylko to.
Emacs również ma tę zdolność i cały czas z niej korzystam.
Wszystko, co obsługuje dynamiczną architekturę wtyczki, zasadniczo modyfikuje jej kod w czasie wykonywania.
źródło
Przeprowadzam analizy statystyczne na podstawie stale aktualizowanej bazy danych. Mój model statystyczny jest zapisywany i przepisywany za każdym razem, gdy kod jest wykonywany, aby uwzględnić nowe dane, które stają się dostępne.
źródło
Scenariusz, w którym można to wykorzystać, to program nauczania. W odpowiedzi na dane wejściowe użytkownika program uczy się nowego algorytmu:
Powstaje pytanie, jak to zrobić w Javie: Jakie są możliwości samodzielnej modyfikacji kodu Java?
źródło
Najlepszą wersją mogą być makra Lisp. W przeciwieństwie do makr C, które są tylko preprocesorem, Lisp zapewnia dostęp do całego języka programowania przez cały czas. Jest to najpotężniejsza funkcja seplenienia i nie istnieje w żadnym innym języku.
W żadnym wypadku nie jestem ekspertem, ale jeden z facetów sepleniących mówi o tym! Jest powód, dla którego mówią, że Lisp jest najpotężniejszym językiem, a mądrzy ludzie nie, że prawdopodobnie mają rację.
źródło