Ich gehe durch MSIL und stelle fest, dass die MSIL viele NOP- Anweisungen enthält.
Der MSDN-Artikel besagt, dass sie keine Maßnahmen ergreifen und verwendet werden, um Speicherplatz zu füllen, wenn der Opcode gepatcht ist. Sie werden in Debug-Builds viel häufiger verwendet als in Release-Builds.
Ich weiß, dass diese Art von Anweisungen in Assemblersprachen verwendet werden, um spätere Anweisungen auszurichten, aber warum werden MSIL-Nops in MSIL benötigt?
(Anmerkung des Herausgebers: Die akzeptierte Antwort bezieht sich auf Maschinencode-NOPs, nicht auf MSIL / CIL-NOPs, nach denen die Frage ursprünglich gestellt wurde.)
Antworten:
NOPs dienen mehreren Zwecken:
quelle
Hier erfahren Sie , wie MSIL / CIL- Nops ( kein x86-Maschinencode)
nop
) beim Debuggen verwendet:Nops werden von Sprachcompilern (C #, VB usw.) verwendet, um implizite Sequenzpunkte zu definieren. Diese teilen dem JIT-Compiler mit, wo sichergestellt werden soll, dass Maschinenanweisungen wieder IL-Anweisungen zugeordnet werden können.
In Rick Byers Blogeintrag zu DebuggingModes.IgnoreSymbolStoreSequencePoints werden einige Details erläutert.
C # platziert Nops auch nach Anrufanweisungen, sodass der Ort der Rückgabestelle in der Quelle eher der Anruf als die Leitung nach dem Anruf ist.
quelle
Es bietet die Möglichkeit für zeilenbasierte Markierungen (z. B. Haltepunkte) im Code, bei denen ein Release-Build keine ausgeben würde.
quelle
Bei der Optimierung für bestimmte Prozessoren oder Architekturen kann Code auch schneller ausgeführt werden:
Prozessoren verwenden lange Zeit mehrere Pipelines, die ungefähr parallel arbeiten, sodass zwei unabhängige Anweisungen gleichzeitig ausgeführt werden können. Auf einem einfachen Prozessor mit zwei Pipelines unterstützt der erste möglicherweise alle Anweisungen, während der zweite nur eine Teilmenge unterstützt. Außerdem gibt es zwischen den Pipelines einige Verzögerungen, wenn auf das Ergebnis einer vorherigen Anweisung gewartet werden muss, die noch nicht abgeschlossen ist.
Unter diesen Umständen kann ein dedizierter NOP den nächsten Befehl in eine bestimmte Pipeline zwingen (den ersten oder nicht den ersten) und die Paarung der folgenden Befehle verbessern, so dass die Kosten des NOP mehr als amortisiert werden.
quelle
Kumpel! No-op ist großartig! Es ist eine Anweisung, die nichts anderes tut, als Zeit zu verbrauchen. In trüben dunklen Zeiten würden Sie es verwenden, um Mikroanpassungen im Timing in kritischen Schleifen vorzunehmen oder, was noch wichtiger ist, als Füllstoff für selbstmodifizierenden Code.
quelle
In einem Prozessor, für den ich kürzlich (vier Jahre lang) gearbeitet habe, wurde NOP verwendet, um sicherzustellen, dass der vorherige Vorgang beendet wurde, bevor der nächste Vorgang gestartet wurde. Zum Beispiel:
Ladewert zum Registrieren (dauert 8 Zyklen) nop 8 addiere 1 zum Registrieren
Dadurch wurde sichergestellt, dass das Register vor dem Hinzufügen den richtigen Wert hatte.
Eine andere Verwendung bestand darin, Ausführungseinheiten auszufüllen, beispielsweise die Interrupt-Vektoren, die eine bestimmte Größe (32 Bytes) haben mussten, da die Adresse für Vektor 0 beispielsweise 0 für Vektor 1 0x20 usw. war, sodass der Compiler dort NOPs einfügte, wenn erforderlich.
quelle
Sie könnten sie verwenden, um das Bearbeiten und Fortfahren beim Debuggen zu unterstützen. Es bietet dem Debugger Raum zum Arbeiten, um den alten Code durch neuen zu ersetzen, ohne Offsets usw. zu ändern.
quelle
Eine etwas unorthodoxe Verwendung sind NOP-Slides , die in Pufferüberlauf-Exploits verwendet werden.
quelle
50 Jahre zu spät, aber hey.
Nops sind nützlich, wenn Sie Assembler-Code von Hand eingeben. Wenn Sie Code entfernen müssten, könnten Sie die alten Opcodes nicht verwenden.
Ebenso können Sie neuen Code einfügen, indem Sie einen Opcode überschreiben und woanders springen. Dort setzen Sie die überschriebenen Opcodes ein und fügen Ihren neuen Code ein. Wenn Sie fertig sind, springen Sie zurück.
Manchmal musste man die verfügbaren Werkzeuge verwenden. In einigen Fällen war dies nur ein sehr einfacher Maschinencode-Editor.
Heutzutage machen die Techniken bei Compilern überhaupt keinen Sinn mehr.
quelle
Eine klassische Verwendung für sie ist, dass Ihr Debugger einer IL-Anweisung immer eine Quellcodezeile zuordnen kann.
quelle
In der Software-Cracking-Szene besteht eine klassische Methode zum Entsperren einer Anwendung darin, die Zeile, die nach dem Schlüssel oder der Registrierung oder dem Zeitraum sucht, mit einem NOP zu patchen, oder so, damit nichts unternommen wird und die Anwendung einfach weiter gestartet wird, als ob sie registriert wäre .
quelle
Ich habe auch NOPs in Code gesehen, der sich selbst modifiziert, um zu verschleiern, was er als Platzhalter tut (sehr alter Kopierschutz).
quelle
Wie ddaa sagte, können Sie mit nops die Varianz im Stapel berücksichtigen, sodass beim Überschreiben der Rücksprungadresse zum nop-Schlitten (viele nops hintereinander) gesprungen wird und dann der ausführbare Code korrekt getroffen wird, anstatt zu einigen zu springen Byte in der Anweisung, die nicht der Anfang ist.
quelle
Sie ermöglichen es dem Linker, einen längeren Befehl (normalerweise Weitsprung) durch einen kürzeren Befehl (Kurzsprung) zu ersetzen. Das NOP benötigt zusätzlichen Platz - der Code konnte nicht verschoben werden, da andere Sprünge nicht mehr funktionieren würden. Dies geschieht zur Verbindungszeit, sodass der Compiler nicht wissen kann, ob ein langer oder kurzer Sprung angemessen wäre.
Zumindest ist das eine ihrer traditionellen Anwendungen.
quelle
Dies ist keine Antwort auf Ihre spezielle Frage, aber früher konnten Sie einen NOP verwenden, um einen Zweigverzögerungsschlitz zu füllen , wenn Sie es nicht schaffen, ihn mit einer ansonsten nützlichen Anweisung zu füllen.
quelle
Richten die .NET-Compiler die MSIL-Ausgabe aus? Ich würde mir vorstellen, dass es nützlich sein könnte, um den Zugriff auf die IL zu beschleunigen ... Meines Wissens nach ist es auch portabel und auf einigen anderen Hardwareplattformen sind ausgerichtete Zugriffe erforderlich.
quelle
Die erste Assembly, die ich gelernt habe, war SPARC, daher bin ich mit dem Verzweigungsverzögerungsschlitz vertraut. Wenn Sie ihn nicht mit einer anderen Anweisung füllen können, normalerweise der Anweisung, die Sie über die Verzweigungsanweisung setzen oder einen Zähler in Schleifen erhöhen möchten, die Sie verwenden ein NOP.
Ich bin nicht mit Cracken vertraut, aber ich denke, es ist üblich, den Stapel mit NOP zu überschreiben, sodass Sie nicht genau berechnen müssen, wo Ihre böswillige Funktion beginnt.
quelle
Ich habe NOPs verwendet, um die nach Eingabe eines ISR akkumulierte Latenz automatisch anzupassen. Sehr praktisch, um das Timing auf den Punkt zu bringen.
quelle
nop
wird bei der Speicherbeschädigung nützlich sein, um Nutzdaten auszunutzen. Natürlichnop
ist ähnlich wiexchg eax, eax
quelle