Cela fait environ un an que j'ai suivi un cours d'assemblage pour la dernière fois. Dans cette classe, nous utilisions MASM avec les bibliothèques Irvine pour faciliter la programmation.
Après avoir parcouru la plupart des instructions, il a dit que l'instruction NOP ne faisait essentiellement rien et ne se souciait pas de l'utiliser. Quoi qu'il en soit, c'était à mi-parcours et il a un exemple de code qui ne fonctionnerait pas correctement, alors il nous a dit d'ajouter une instruction NOP et cela a bien fonctionné. J'ai demandé après le cours pourquoi et ce que cela avait réellement fait, et il a répondu qu'il ne savait pas.
Quelqu'un le sait?
Réponses:
Souvent, le temps
NOP
est utilisé pour aligner les adresses des instructions. Cela se produit généralement par exemple lors de l'écriture de Shellcode pour exploiter le débordement de tampon ou la vulnérabilité de chaîne de format .Supposons que vous ayez un saut relatif de 100 octets vers l'avant et apportez quelques modifications au code. Il est probable que vos modifications gâchent l'adresse de la cible de saut et en tant que tel, vous devrez également modifier le saut relatif susmentionné. Ici, vous pouvez ajouter des
NOP
s pour pousser l'adresse cible vers l'avant. Si vous avez plusieursNOP
s entre l'adresse cible et l'instruction de saut, vous pouvez supprimer lesNOP
s pour tirer l'adresse cible vers l'arrière.Ce ne serait pas un problème si vous travaillez avec un assembleur qui prend en charge les étiquettes. Vous pouvez simplement le faire
JXX someLabel
(où JXX est un saut conditionnel) et l'assembleur remplacera lesomeLabel
par l'adresse de cette étiquette. Cependant, si vous modifiez simplement le code machine assemblé (les opcodes réels) à la main (comme cela arrive parfois lors de l'écriture du shellcode), vous devez également modifier manuellement l'instruction de saut. Soit vous le modifiez, soit vous déplacez l'adresse de code cible à l'aide deNOP
s.Un autre cas d'utilisation pour l'
NOP
instruction serait quelque chose appelé un traîneau NOP . En substance, l'idée est de créer un tableau d'instructions suffisamment grand qui ne provoque aucun effet secondaire (commeNOP
ou incrémenter puis décrémenter un registre) mais augmenter le pointeur d'instruction. Ceci est utile par exemple lorsque l'on veut passer à un certain morceau de code dont l'adresse n'est pas connue. L'astuce consiste à placer ledit traîneau NOP devant le code cible, puis à sauter quelque part vers ledit traîneau. Ce qui se passe, c'est que l'exécution se poursuit, espérons-le, à partir du tableau qui n'a aucun effet secondaire et qu'elle avance instruction par instruction jusqu'à ce qu'elle atteigne le morceau de code souhaité. Cette technique est couramment utilisée dans les exploits de débordement de tampon susmentionnés et en particulier pour contrer les mesures de sécurité telles que ASLR .Encore une autre utilisation particulière de l'
NOP
instruction est lorsque l'on modifie le code d'un programme. Par exemple, vous pouvez remplacer des parties de sauts conditionnels parNOP
s et ainsi contourner la condition. Il s'agit d'une méthode souvent utilisée pour " craquer " la protection contre la copie des logiciels. Au plus simple, il s'agit simplement de supprimer la construction du code assembleur pour laif(genuineCopy) ...
ligne de code et de remplacer les instructions parNOP
s et .. Voilà! Aucun contrôle n'est effectué et la copie non authentique fonctionne!Notez qu'en substance, les deux exemples de shellcode et de cracking font la même chose; modifier le code existant sans mettre à jour les adresses relatives des opérations qui reposent sur un adressage relatif.
la source
Un nop peut être utilisé dans un intervalle de retard lorsqu'aucune autre instruction ne peut être réorganisée pour y être placée.
Dans MIPS, ce serait un bogue car au moment où le jr lisait le registre v0, le registre v0 n'avait pas encore été chargé avec la valeur de l'instruction précédente.
La solution serait de:
Ceci remplit les créneaux sourds après le mot de chargement et les instructions de registre de saut avec un nop afin que l'instruction de mot de chargement soit terminée avant l'exécution de la commande de registre de saut.
Pour en savoir plus - un peu sur le remplissage SPARC des créneaux de retard . De ce document:
Notez la troisième option dans le quoi mettre dans le slot de retard. Le bug que vous avez vu était probablement quelqu'un remplissant l'une des choses qui ne doivent pas être placées dans le slot de retard. Mettre un nop à cet emplacement résoudrait alors le bogue.
Remarque: après avoir relu la question, c'était pour x86, qui n'a pas de créneaux de retard (la ramification bloque plutôt le pipeline). Ce ne serait donc pas la cause / solution du bogue. Sur les systèmes RISC, cela aurait pu être la réponse.
la source
au moins une raison d'utiliser NOP est l'alignement. Les processeurs x86 lisent les données de la mémoire principale dans des blocs assez gros, et le début du bloc à lire est toujours aligné, donc si l'on a un bloc de code, ce sera beaucoup lu, ce bloc doit être aligné. Cela se traduira par peu d'accélération.
la source
0x1002
, car il y a toujours 14 octets d'instructions dans le bloc aligné de 16B qui contient l'adresse cible, mais pas bien de sauter0x099D
.Un but pour NOP (en assemblée générale, pas seulement x86) c'est d'introduire des délais. Par exemple, vous voulez programmer un microcontrôleur qui doit sortir sur certaines LED avec un retard de 1 s. Ce délai peut être implémenté avec NOP (et branches). Bien sûr, vous pourriez utiliser un ADD ou autre chose, mais cela rendrait le code plus illisible; ou peut-être avez-vous besoin de tous les registres.
la source
loop
instruction pour les boucles de retard , pas les NOP.En général sur le 80x86, les instructions NOP ne sont pas nécessaires pour la correction du programme, bien que parfois sur certaines machines un NOP stratégiquement placé puisse faire exécuter le code plus rapidement. Sur le 8086, par exemple, le code serait récupéré en morceaux de deux octets, et le processeur avait un tampon interne de "pré-lecture" qui pouvait contenir trois de ces morceaux. Certaines instructions s'exécuteraient plus rapidement qu'elles ne pourraient être récupérées, tandis que d'autres instructions prendraient un certain temps à s'exécuter. Pendant les instructions lentes, le processeur tenterait de remplir le tampon de prélecture, de sorte que si les quelques instructions suivantes étaient rapides, elles pouvaient être exécutées rapidement. Si l'instruction suivant l'instruction lente commence sur une limite de mot pair, les six octets suivants d'instructions seront prélus; s'il démarre sur une limite d'octets impairs, seuls cinq octets seront prélus.
Ces problèmes d'alignement de la mémoire peuvent affecter la vitesse du programme, mais ils n'affectent généralement pas l'exactitude. D'un autre côté, il existe des problèmes liés à la prélecture sur les anciens processeurs où un NOP peut affecter l'exactitude. Si une instruction modifie un octet de code qui a déjà été préchargé, le 8086 (et je pense que les 80286 et 80386) exécutera l'instruction préchargée même si elle ne correspond plus à ce qui est en mémoire. L'ajout d'un NOP ou deux entre l'instruction qui modifie la mémoire et l'octet de code qui est modifié peut empêcher la récupération de l'octet de code jusqu'à ce qu'il soit écrit. Notez, en passant, que de nombreux schémas de protection contre la copie exploitaient ce type de comportement; notez également, cependant, que ce comportement n'est pas garanti. Différentes variantes de processeur peuvent gérer la prélecture différemment, certains peuvent invalider les octets prélus si la mémoire à partir de laquelle ils ont été lus est modifiée, et les interruptions invalideront généralement le tampon de prélecture; le code sera récupéré lorsque les interruptions reviendront.
la source
Il existe un cas spécifique x86 qui n'est toujours pas décrit dans d'autres réponses: la gestion des interruptions. Pour certains styles, il peut y avoir des sections de code lorsque les interruptions sont désactivées car le code principal fonctionne avec certaines données partagées avec les gestionnaires d'interruptions, mais il est raisonnable d'autoriser les interruptions entre ces sections. Si on écrit naïvement
cela ne traitera pas les interruptions en attente car, citant Intel:
donc cela doit être réécrit au moins comme:
Dans la deuxième variante, toutes les interruptions en attente seront traitées uniquement entre NOP et CLI. (Bien sûr, il peut y avoir de nombreuses variantes alternatives, comme le doublement de l'instruction STI. Mais le NOP explicite est plus évident, du moins pour moi.)
la source
NOP signifie aucune opération
Il est généralement utilisé pour insérer ou supprimer du code machine ou pour retarder l'exécution d'un code particulier.
Également utilisé par les crackers et les débogueurs pour définir des points d'arrêt.
Donc, probablement en faisant quelque chose comme: XCHG BX, BX entraînera également la même chose.
Cela me semble comme s'il y avait peu d'opérations qui étaient encore en cours et, par conséquent, cela a provoqué une erreur.
Si vous connaissez VB, je peux vous donner un exemple:
Si vous créez un système de connexion en vb et chargez 3 pages ensemble - facebook, youtube et twitter dans 3 onglets différents.
Et utilisez 1 bouton de connexion pour tous. Cela pourrait donner une erreur si votre connexion Internet est lente. Ce qui signifie que l'une des pages n'a pas encore été chargée. Nous avons donc mis Application.DoEvents pour surmonter cela. De la même manière dans l'assemblage, NOP peut être utilisé.
la source