Frage

Ich gehe durch MSIL und bemerke, dass es viel gibt NOP Anweisungen. Der MSDN -Artikel besagt, dass sie keine Maßnahmen ergreifen und zum Ausfüllen von Platz verwendet werden, wenn der Opcode gepatcht wird. Sie werden in Debug -Builds viel mehr verwendet als Release -Builds. Ich weiß, dass diese Art von Aussagen in den Montagesprachen verwendet werden, um sicherzustellen, dass ein Opcode auf eine Wortgrenze passt, aber warum wird er in MSIL benötigt?

War es hilfreich?

Lösung

NOPs dienen mehreren Zwecken:

  • Sie ermöglichen dem Debugger, einen Haltepunkt in eine Zeile zu setzen, auch wenn er mit anderen im generierten Code kombiniert wird.
  • Es ermöglicht dem Loader, einen Sprung mit einem Zielversatz mit unterschiedlich großer Größe zu pflücken.
  • Es ermöglicht es, einen Codeblock an einer bestimmten Grenze auszurichten, was zum Ausbruch gut sein kann.
  • Es ermöglicht eine inkrementelle Verknüpfung, um Codebrocken mit einem Aufruf eines neuen Abschnitts zu überschreiben, ohne sich um die Gesamtfunktion zu kümmern, die die Größe ändern muss.

Andere Tipps

So werden NOPS durch Debuggen verwendet:

NOPS werden von Sprach Compilern (C#, VB usw.) verwendet, um implizite Sequenzpunkte zu definieren. Diese geben dem JIT -Compiler mit, wo Maschinenanweisungen sichergestellt werden können, dass die Anweisungen der Maschinen zurückgeführt werden können.

Rick Byers Blogeintrag auf DebuggingModes.ignoresymbolstoresequencePoints, erklärt einige Details.

C# platziert NOPS auch nach Anrufanweisungen, so dass der Standort der Rückkehrort in der Quelle eher der Anruf als die Zeile nach dem Anruf ist.

Es bietet die Möglichkeit für Linienbasierte Marker (z. B. Haltepunkte) in dem Code, in dem ein Release-Build keine abgibt.

Es kann auch den Code schneller ausgeführt werden, wenn bestimmte Prozessoren oder Architekturen optimiert werden:

Prozessoren verwenden lange Zeit mehrere Pipelines, die ungefähr parallel funktionieren. Daher können gleichzeitig zwei unabhängige Anweisungen überzeugt werden. Bei einem einfachen Prozessor mit zwei Pipelines kann der erste alle Anweisungen unterstützen, während der zweite nur eine Teilmenge unterstützt. Es gibt auch einige Stände zwischen den Pipelines, wenn man auf das Ergebnis einer früheren Anweisung warten muss, die noch nicht fertig ist.

Unter diesen Umständen ein engagierter NOP kann den nächsten Anweisungen in eine bestimmte Pipeline (die erste oder nicht die erste) erzwingen und die Paarung der folgenden Anweisungen verbessern, damit die Kosten der Kosten NOP ist mehr als amortisiert.

Alter! No-op ist großartig! Es ist eine Anweisung, die nichts anderes tut, als Zeit zu verbrauchen. In den schwachen dunklen Zeiten würden Sie es verwenden, um im Timing in kritischen Schleifen im Timing oder vor allem als Füllstoff im selbstmodifizierenden Code zu tun.

In einem Prozessor arbeitete ich kürzlich (seit vier Jahren) NOP, um sicherzustellen, dass der vorherige Vorgang vor Beginn der nächsten Operation abgeschlossen wurde. Zum Beispiel:

Lastwert zum Registrieren (8 Zyklen) NOP 8 hinzufügen 1 zum Registrieren

Dies stellte sicher, dass das Register den richtigen Wert vor dem Vorgang hinzufügen.

Eine weitere Verwendung bestand darin, Ausführungseinheiten wie die Interrupt -Vektoren auszufüllen, die eine bestimmte Größe (32 Bytes) haben mussten, da die Adresse für Vector0 0 für Vector 1 0x20 usw. war. erforderlich.

Eine klassische Verwendung für sie ist, dass Ihr Debugger immer eine Quellcode-Linie mit einer IL-Anweisung in Verbindung bringen kann.

Sie könnten sie verwenden, um sie zu unterstützen bearbeiten und kontinuieren Beim Debuggen. Es bietet dem Debugger Raum, um den alten Code durch neue Offsets usw. zu ersetzen, um den alten Code zu ersetzen.

NOP ist für die Nutzlast von Pufferüberlauf von wesentlicher Bedeutung.

Wie DDAA sagte, können Sie mit NOPS die Abweichung im Stapel berücksichtigen, so dass beim Überschreiben der Rückgabeadresse sie zum NOP -Schlitten (viele NOPS hintereinander) springt und dann den ausführbaren Code richtig trifft, anstatt zu einigen zu springen Byte in der Anweisung, die nicht der Anfang ist.

50 Jahre zu spät aber hey.

NOPs sind nützlich, wenn Sie Assembly -Code von Hand eingeben. Wenn Sie Code entfernen müssten, können Sie die alten Opcodes nopieren.

Ähnlich können Sie einen neuen Code einfügen, indem Sie etwas Opcode überschreiben und woanders springen. Dort setzen Sie die überschriebenen Opcodes und fügen Ihren neuen Code ein. Wenn Sie bereit sind, springen Sie zurück.

Manchmal mussten Sie die verfügbaren Werkzeuge verwenden. In einigen Fällen war dies nur ein sehr grundlegender Machinecode -Editor.

Heutzutage machen die Techniken mit Compilern überhaupt keinen Sinn mehr.

In der Software -Cracking -Szene besteht eine klassische Methode zum Entsperren einer Anwendung darin, mit einer NOP die Zeile zu patchen, die nach Schlüssel oder Registrierung oder Zeitraum oder so weiter prüft, damit sie nichts bewirkt und einfach die Anwendung so starten würde, als ob sie registriert wäre .

Ich habe auch NOPS in Code gesehen, das sich verändert, um zu verschleiern, was es als Platzhalter macht (Veeery Old Copy Protection).

Ein etwas unorthodoxer Gebrauch sind Nop-Slides, verwendet im Pufferüberlauf Exploits.

Sie ermöglichen dem Linker, einen längeren Befehl (normalerweise Weitsprung) durch einen kürzeren (kurzer Sprung) zu ersetzen. Die NOP nimmt den zusätzlichen Platz ein - der Code konnte nicht herumgezogen werden, da andere Sprünge von der Arbeit verhindern würden. Dies geschieht zur Link-Zeit, sodass der Compiler nicht wissen kann, ob ein langer oder kurzer Sprung angemessen wäre.

Zumindest ist das eine ihrer traditionellen Zwecke.

Dies ist keine Antwort auf Ihre spezifische Frage, aber in den alten Zeiten können Sie eine NOP verwenden, um a zu füllen Zweigverzögerungsschlitz, Wenn Sie es nicht schaffen, es mit einer ansonsten nützlichen Anweisung zu füllen.

Richten die .NET -Compiler die MSIL -Ausgabe aus? Ich würde mir vorstellen, dass es nützlich sein könnte, den Zugriff auf die IL zu beschleunigen ... auch mein Verständnis ist, dass es auf einigen anderen Hardware -Plattformen tragbar ist und ausgerichtete Zugriffe erforderlich sind.

Die erste Versammlung, die ich gelernt habe, war sparc, also bin ich mit dem Zweig -Verzögerungs -Steckplatz vertraut. Wenn Sie ihn nicht mit einer anderen Anweisung füllen können, normalerweise die Anweisung, die Sie über die Zweiganweisung setzen oder einen Zähler in Schleifen erhöhen, verwenden Sie Ein NOP.

Ich bin mit Cracking nicht vertraut, aber ich denke, dass es häufig ist, den Stapel mit NOP zu überschreiben, sodass Sie nicht genau berechnen müssen, wo Ihre böswillige Funktion beginnt.

Ich habe NOPS verwendet, um die nach Eingabe eines ISR akkumulierte Latenz automatisch einzustellen. Sehr praktisch, um das Timing tot zu nageln.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top