Jaki jest cel kodu operacji CIL nop?

82

Przechodzę przez MSIL i zauważam, że w MSIL jest wiele instrukcji nop .

Artykuł MSDN mówi, że nie podejmują żadnych działań i są używane do wypełniania miejsca, jeśli kod operacji jest załatany. Są używane znacznie częściej w kompilacjach do debugowania niż w kompilacjach wydań.

Wiem, że tego rodzaju instrukcje są używane w językach asemblera do wyrównywania późniejszych instrukcji, ale dlaczego nops MSIL są potrzebne w MSIL?

(Uwaga redaktora: przyjęta odpowiedź dotyczy NOP kodu maszynowego, a nie NOP MSIL / CIL, o które pierwotnie zadano pytanie).

Dan Goldstein
źródło
19
W tych odpowiedziach występuje masowe zamieszanie między instrukcją MSIL nop emitowaną przez kompilator języka do zestawu a instrukcjami nop x86 (na tej platformie) emitowanymi przez kompilator JIT podczas uruchamiania zestawu. [W rzeczywistości akceptowana odpowiedź dotyczy x86 nops i nie ma związku z MSIL.] W idealnym przypadku to pytanie powinno zostać podzielone na 2 różne pytania: cel MSIL :: nop? i cel natywnej platformy nop?
Steve Steiner

Odpowiedzi:

107

NOP służą kilku celom:

  • Pozwalają debugerowi na umieszczenie punktu przerwania w wierszu, nawet jeśli jest on połączony z innymi w wygenerowanym kodzie.
  • Pozwala ładującemu załatać skok z przesunięciem celu o różnej wielkości.
  • Pozwala na wyrównanie bloku kodu na określonej granicy, co może być dobre do buforowania.
  • Pozwala na przyrostowe łączenie w celu nadpisania fragmentów kodu wywołaniem nowej sekcji bez martwienia się o zmianę rozmiaru całej funkcji.
Anthony Williams
źródło
Z Wikipedii : „NOP jest najczęściej używany do celów czasowych, aby wymusić wyrównanie pamięci, aby zapobiec zagrożeniom, aby zająć gniazdo opóźnienia gałęzi, aby unieważnić istniejącą instrukcję, taką jak skok, lub jako element zastępczy do zastąpienia przez aktywne instrukcje później podczas tworzenia programu (lub w celu zastąpienia usuniętych instrukcji, gdy refaktoryzacja byłaby problematyczna lub czasochłonna). W niektórych przypadkach NOP może mieć niewielkie skutki uboczne; na przykład w procesorach Motorola z serii 68000 NOP opcode powoduje synchronizację potoku. "
mbomb007
11

Oto jak nops MSIL / CIL ( nie kod maszynowy x86nop ) są używane przez debugowanie:

Nops są używane przez kompilatory języków (C #, VB itp.) Do definiowania niejawnych punktów sekwencji. Informują one kompilator JIT, gdzie ma zapewnić możliwość odwzorowania instrukcji maszynowych z powrotem na instrukcje IL.

Wpis na blogu Ricka Byera na temat DebuggingModes.IgnoreSymbolStoreSequencePoints wyjaśnia kilka szczegółów.

C # umieszcza również instrukcje Nops po wywołaniu, tak aby lokalizacja witryny zwrotnej w źródle była wywołaniem, a nie wierszem po wywołaniu.

Steve Steiner
źródło
C # umieszcza również instrukcje Nops po wywołaniu, tak aby lokalizacja witryny zwrotnej w źródle była wywołaniem, a nie wierszem po wywołaniu. Nie wiem, czy to rozumiem. Czy masz dostępne referencje?
user492238
8

Zapewnia możliwość umieszczania w kodzie markerów opartych na wierszach (np. Punktów przerwania), w przypadku których kompilacja wydania nie wyemituje żadnych.

harpo
źródło
Czy punkty przerwania muszą mieć wartość nop? Dlaczego po prostu nie umieścić punktu przerwania w zwykłym kodzie operacyjnym?
Dan Goldstein,
4
wiele zwykłych kodów operacyjnych jest optymalizowanych w kompilacjach wydań. To zepsułoby twoje punkty przerwania, chyba że byłby symbol zastępczy, nie tam, na które punkty przerwania nadal wskazywałyby
Jimmy
1
W kompilacjach debugowania są one również używane do dostarczania instrukcji przerywania, gdy kod nie ma instrukcji. Na przykład szelki otwierające.
Greg D,
1
Możesz dodać odwołanie do blogs.msdn.com/oldnewthing/archive/2007/08/17/4422794.aspx jako źródło.
Greg D,
1
Cóż, dziękuję Gregowi i Jimmy'emu za uczynienie tego prawdziwą odpowiedzią.
harpo
6

Może również przyspieszyć działanie kodu podczas optymalizacji pod kątem określonych procesorów lub architektur:

Procesory przez długi czas wykorzystują wiele potoków, które działają mniej więcej równolegle, więc dwie niezależne instrukcje mogą być wykonywane w tym samym czasie. Na prostym procesorze z dwoma potokami pierwszy może obsługiwać wszystkie instrukcje, podczas gdy drugi obsługuje tylko podzbiór. Ponadto między potokami występują przestoje, gdy trzeba czekać na wynik poprzedniej instrukcji, która nie jest jeszcze ukończona.

W takich okolicznościach dedykowany nop może wymusić następną instrukcję na konkretnym potoku (pierwszym lub nie pierwszym) i poprawić parowanie następujących instrukcji, tak aby koszt nop był więcej niż zamortyzowany.

peterchen
źródło
5

Koleś! No-op jest niesamowity! Jest to instrukcja, która tylko pochłania czas. W mrocznych wiekach używałbyś go do mikroregulacji czasu w krytycznych pętlach lub, co ważniejsze, do wypełniania samomodyfikującego się kodu.

cokół
źródło
Rozumiem jego użycie, gdy jest uruchamiany bezpośrednio na sprzęcie, ale MSIL jest JIT.
Dan Goldstein,
MSIL jest JIT tylko w systemach, które mają JIT - MSIL nie wymaga JIT.
cokół
5

W jednym procesorze, dla którego pracowałem ostatnio (przez cztery lata), zastosowano NOP, aby upewnić się, że poprzednia operacja zakończyła się przed rozpoczęciem następnej. Na przykład:

wartość obciążenia do rejestru (trwa 8 cykli) nop 8 dodaj 1 do rejestru

Dzięki temu rejestr miał prawidłową wartość przed operacją dodawania.

Innym zastosowaniem było wypełnienie jednostek wykonawczych, takich jak wektory przerwań, które musiały mieć określony rozmiar (32 bajty), ponieważ adres dla wektora 0 był, powiedzmy, 0, dla wektora 1 0x20 i tak dalej, więc kompilator wstawił tam NOPy, jeśli potrzebne.

Makis
źródło
4

Mogą ich używać do obsługi edycji i kontynuowania podczas debugowania. Zapewnia debugerowi miejsce do pracy, aby zastąpić stary kod nowym bez zmiany offsetów itp.

Rob Walker
źródło
4
Zaimplementowałem obsługę debugera w VS do edycji i kontynuuj (nie zaimplementowałem CLR ani części kompilatora). Nops są częścią historii, aby zapewnić prawidłowe mapowanie ze starej do nowej wersji metody (szczególnie w przypadku wyskakiwania z kodu obsługującego wykonanie). Jednak zastąpienie MSIL odbywa się na podstawie całej funkcji. W przypadku kodu zarządzanego CLR nie jest konieczne `` opuszczanie miejsca '' w msil, aby wykonać tę część. To, co tu mówisz, jest poprawne w odniesieniu do natywnej edycji i kontynuuj.
Steve Steiner
4

Nieco niekonwencjonalnym zastosowaniem są slajdy NOP , używane w exploitach przepełnienia bufora.

mdm
źródło
szukał tego :)
Suraj Jain
Popraw swój link, nie działa, zawsze upewnij się, że przesłałeś link do archiwum internetowego, ponieważ nie da 404 nie znaleziono.
Suraj Jain
Obecnie pracuję nad kodem powłoki i potrzebuję więcej kontekstu na temat NOP. Musiałem trochę cofnąć się, ale oto archiwum: web.archive.org/web/20110124015428/http://www.phreedom.org:80/…
saniboy
4

50 lat za późno, ale hej.

Nopy są przydatne, jeśli ręcznie wpisujesz kod asemblera. Gdybyś musiał usunąć kod, nie mógłbyś przestać stosować starych kodów operacyjnych.

podobnie, możesz wstawić nowy kod, nadpisując jakiś opcode i przeskoczyć gdzie indziej. Tam umieszczasz nadpisane kody rozkazów i wstawiasz nowy kod. Kiedy będziesz gotowy, wskocz z powrotem.

Czasami trzeba było skorzystać z dostępnych narzędzi. W niektórych przypadkach był to tylko bardzo podstawowy edytor kodu maszynowego.

W dzisiejszych czasach w przypadku kompilatorów techniki te nie mają już żadnego sensu.

Ropucha
źródło
3

Jednym z klasycznych zastosowań jest to, że debugger może zawsze skojarzyć wiersz kodu źródłowego z instrukcją IL.

Larry OBrien
źródło
Ponieważ msil jest kompilowany w JIT podczas uruchamiania, możliwe jest utracenie tego mapowania (np. Instrukcja natywna nie ma unikalnej instrukcji MSIL). NOP są używane jako mechanizm komunikacji z kompilatora języka do kompilatora JIT w celu zachowania takiego mapowania.
Steve Steiner
3

Na scenie łamania oprogramowania klasyczną metodą odblokowania aplikacji byłoby załatanie NOP linii, która sprawdza klucz, rejestrację lub okres czasu, czy tak dalej, aby nic nie robił i po prostu kontynuował uruchamianie aplikacji tak, jakby była zarejestrowana .

shoosh
źródło
5
Jestem prawie pewien, że instrukcje no-op nie zostały wymyślone, aby pomóc ludziom pirackim oprogramowaniem :-)
Simon Howard,
3

Widziałem również NOP w kodzie, który modyfikuje się, aby zaciemnić to, co robi jako symbol zastępczy (baaardzo stara ochrona przed kopiowaniem).

TheMarko
źródło
3

Jak powiedział ddaa, nops pozwala na uwzględnienie wariancji w stosie, więc kiedy nadpiszesz adres zwrotny, przeskakuje on na sanki nop (wiele nopsów z rzędu), a następnie poprawnie uderza w kod wykonywalny, zamiast przeskakiwać do niektórych bajt w instrukcji, który nie jest początkiem.

Alex Gartrell
źródło
1

Pozwalają one linkerowi zastąpić dłuższą instrukcję (zwykle skok w dal) krótszą (skok krótki). NOP zajmuje dodatkowe miejsce - kodu nie można przesuwać, ponieważ uniemożliwiłoby to działanie innych skoków. Dzieje się to w czasie łącza, więc kompilator nie może wiedzieć, czy długi czy krótki skok byłby odpowiedni.

Przynajmniej jest to jedno z ich tradycyjnych zastosowań.

MarkR
źródło
1

To nie jest odpowiedź na twoje konkretne pytanie, ale w dawnych czasach mógłbyś użyć NOP do wypełnienia pola opóźnienia gałęzi , jeśli nie udało ci się wypełnić go przydatną w inny sposób instrukcją.

Chris Conway
źródło
1

Czy kompilatory .NET dopasowują dane wyjściowe MSIL? Wyobrażam sobie, że może to być przydatne do przyspieszenia dostępu do IL ... Ponadto rozumiem, że jest zaprojektowany jako przenośny, a na niektórych innych platformach sprzętowych wymagane są wyrównane dostępy.

Brian Knoblauch
źródło
1

Pierwszym zestawem, którego się nauczyłem, był SPARC, więc jestem zaznajomiony z gniazdem opóźnienia gałęzi, jeśli nie możesz go wypełnić inną instrukcją, zwykle instrukcją, którą zamierzałeś umieścić nad instrukcją gałęzi lub zwiększać licznik w pętlach, używasz a NOP.

Nie jestem zaznajomiony z crackingiem, ale myślę, że nadpisywanie stosu za pomocą NOP jest powszechne, więc nie musisz dokładnie obliczać, gdzie zaczyna się Twoja złośliwa funkcja.

niedziela
źródło
1

Użyłem NOP do automagicznego dostosowania latencji zgromadzonej po wejściu do ISR. Bardzo przydatne do ustalenia czasu.


źródło
-1

nopbędzie przydatny w ładunku exploita powodującym uszkodzenie pamięci. Oczywiście nopjest podobniexchg eax, eax

Nie gwiazda
źródło
Inna odpowiedź wspomniała już slajdy NOP dotyczące exploitów typu code-injection. Podobnie jak inna odpowiedź na to samo pytanie. Ta odpowiedź nie wspomina, jak są przydatne, i nie jest to oczywiste, jeśli jeszcze tego nie wiesz.
Peter Cordes,