Faz mais ou menos um ano desde a última vez que participei de uma aula de montagem. Nessa aula, estávamos usando o MASM com as bibliotecas Irvine para facilitar a programação.
Depois de examinarmos a maioria das instruções, ele disse que a instrução NOP não fazia nada e não se preocupava em usá-la. De qualquer forma, foi no meio do semestre e ele tem algum código de exemplo que não funcionaria corretamente, então ele nos disse para adicionar uma instrução NOP e funcionou bem. Perguntei que estava depois da aula por que e o que realmente fazia, e ele disse que não sabia.
Alguém sabe?
Respostas:
Muitas vezes, o tempo
NOP
é usado para alinhar os endereços das instruções. Isso geralmente é encontrado, por exemplo, ao escrever o Shellcode para explorar o estouro de buffer ou formatar a vulnerabilidade de cadeia de caracteres .Digamos que você tenha um salto relativo para 100 bytes adiante e faça algumas modificações no código. As chances são de que suas modificações atrapalhem o endereço do alvo do salto e, como tal, você também precisará alterar o salto relativo acima mencionado. Aqui, você pode adicionar
NOP
s para enviar o endereço de destino. Se você tiver váriosNOP
s entre o endereço de destino e a instrução de salto, poderá remover osNOP
s para puxar o endereço de destino para trás.Isso não seria um problema se você estiver trabalhando com um montador que suporta etiquetas. Você pode simplesmente fazer
JXX someLabel
(onde JXX é algum salto condicional) e o montador substituirá osomeLabel
pelo endereço desse rótulo. No entanto, se você simplesmente modificar o código da máquina montado (os opcodes reais) manualmente (como às vezes acontece com a gravação do código do shell), também será necessário alterar a instrução de salto manualmente. Você o modifica ou move o endereço do código de destino usandoNOP
s.Outro caso de uso para
NOP
instruções seria algo chamado trenó NOP . Em essência, a idéia é criar uma série de instruções suficientemente grande que não cause efeitos colaterais (comoNOP
ou incrementar e depois decrementar um registro), mas aumente o ponteiro da instrução. Isso é útil, por exemplo, quando se deseja pular para um determinado pedaço de código cujo endereço não é conhecido. O truque é colocar o referido trenó NOP na frente do código de destino e depois pular em algum lugar para o referido trenó. O que acontece é que a execução continua, esperançosamente, a partir da matriz que não tem efeitos colaterais e percorre a instrução por instrução até atingir o trecho de código desejado. Essa técnica é comumente usada em explorações de buffer overflow mencionadas anteriormente e, especialmente, para combater medidas de segurança, como o ASLR .Ainda outro uso específico para a
NOP
instrução é quando alguém está modificando o código de algum programa. Por exemplo, você pode substituir partes de saltos condicionais porNOP
s e, como tal, contornar a condição. Esse é um método frequentemente usado ao " quebrar " a proteção contra cópias do software. No mais simples, trata-se de remover a construção do código de montagem daif(genuineCopy) ...
linha de código e substituir as instruções porNOP
s e .. Voilà! Nenhuma verificação é feita e a cópia não original funciona!Observe que, em essência, os dois exemplos de código de shell e cracking fazem o mesmo; modificar o código existente sem atualizar os endereços relativos das operações que dependem do endereço relativo.
fonte
Um nop pode ser usado em um slot de atraso quando nenhuma outra instrução pode ser reordenada para ser colocada nele.
No MIPS, isso seria um bug, porque no momento em que o jr estava lendo o registro v0, o registro v0 ainda não havia sido carregado com o valor da instrução anterior.
A maneira de corrigir isso seria:
Isso preenche os slots dealy após as instruções load word e jump register com um nop, para que a instrução load word seja concluída antes que o comando jump register seja executado.
Leitura adicional - um pouco sobre o preenchimento SPARC de slots de atraso . A partir desse documento:
Observe a terceira opção no o que colocar no slot de atraso. O bug que você viu foi provavelmente alguém preenchendo uma das coisas que não devem ser colocadas no slot de atraso. Colocar um nop nesse local corrigia o bug.
Nota: após reler a pergunta, era para x86, que não possui slots de atraso (ramificação, em vez disso, apenas paralisa o pipeline). Portanto, essa não seria a causa / solução do bug. Nos sistemas RISC, essa poderia ter sido a resposta.
fonte
pelo menos um motivo para usar o NOP é o alinhamento. Os processadores x86 lêem dados da memória principal em blocos bastante grandes e o início do bloco para leitura é sempre alinhado; portanto, se houver um bloco de código, isso será lido muito, esse bloco deverá ser alinhado. Isso resultará em pouca aceleração.
fonte
0x1002
, porque ainda existem 14 bytes de instruções no bloco alinhado de 16B que contém o endereço de destino, mas não é bom pular para0x099D
.Um objetivo do NOP (em assembléia geral, não apenas x86) é introduzir atrasos. Por exemplo, você deseja programar um microcontrolador que precisa emitir alguns LEDs com um atraso de 1 s. Esse atraso pode ser implementado com NOP (e ramificações). Claro que você poderia usar ADD ou algo mais, mas isso tornaria o código mais ilegível; ou talvez você precise de todos os registros.
fonte
loop
instrução para loops de atraso , não NOPs.Em geral, no 80x86, as instruções NOP não são necessárias para a correção do programa, embora, ocasionalmente, em algumas máquinas, os NOPs estrategicamente colocados possam fazer com que o código seja executado mais rapidamente. No 8086, por exemplo, o código seria buscado em blocos de dois bytes e o processador possuía um buffer interno de "pré-busca" que podia conter três desses blocos. Algumas instruções seriam executadas mais rapidamente do que poderiam ser buscadas, enquanto outras instruções levariam algum tempo para serem executadas. Durante as instruções lentas, o processador tentaria preencher o buffer de pré-busca, para que, se as próximas instruções fossem rápidas, pudessem ser executadas rapidamente. Se a instrução que segue a instrução lenta iniciar em um limite de palavras pares, os próximos seis bytes de instruções serão pré-buscados; se iniciar em um limite de bytes ímpares, apenas cinco bytes serão pré-buscados.
Esses problemas de alinhamento de memória podem afetar a velocidade do programa, mas geralmente não afetam a correção. Por outro lado, existem alguns problemas relacionados à pré-busca nos processadores mais antigos em que um NOP pode afetar a correção. Se uma instrução alterar um byte de código que já foi pré-buscado, o 8086 (e acho que o 80286 e o 80386) executará a instrução pré-buscada, mesmo que não corresponda mais ao que está na memória. Adicionar um NOP ou dois entre a instrução que altera a memória e o byte de código alterado pode impedir que o byte de código seja buscado até depois de ter sido gravado. Observe, a propósito, que muitos esquemas de proteção contra cópias exploravam esse tipo de comportamento; Observe também, no entanto, que esse comportamento não é garantido. Diferentes variações do processador podem lidar com a pré-busca de maneira diferente, alguns podem invalidar bytes pré-buscados se a memória da qual eles foram lidos for modificada e as interrupções geralmente invalidam o buffer de pré-busca; o código será buscado novamente quando as interrupções retornarem.
fonte
Há um caso específico do x86 ainda não descrito em outras respostas: tratamento de interrupção. Para alguns estilos, pode haver seções de código quando as interrupções são desativadas porque o código principal funciona com alguns dados compartilhados com os manipuladores de interrupção, mas é razoável permitir interrupções entre essas seções. Se alguém escreve ingenuamente
isso não processa interrupções pendentes porque, citando a Intel:
portanto, isso deve ser reescrito pelo menos como:
Na segunda variante, todas as interrupções pendentes serão processadas apenas entre o NOP e a CLI. (Obviamente, pode haver muitas variantes alternativas, como duplicar a instrução STI. Mas o NOP explícito é mais óbvio, pelo menos para mim.)
fonte
NOP significa Sem Operação
Geralmente é usado para inserir ou excluir código de máquina ou para atrasar a execução de um código específico.
Também usado por crackers e depuradores para definir pontos de interrupção.
Então provavelmente fazendo algo como: XCHG BX, BX também resultará no mesmo.
Parece-me como se houvesse poucas operações que ainda estavam em processo e, portanto, causou um erro.
Se você conhece o VB, posso dar um exemplo:
Se você criar um sistema de login em vb e carregar 3 páginas juntas - facebook, youtube e twitter em 3 abas diferentes.
E use 1 botão de login para todos. Pode ocorrer um erro se sua conexão à Internet estiver lenta. O que significa que uma das páginas ainda não foi carregada. Então, colocamos no Application.DoEvents para superar isso. Da mesma forma na montagem, o NOP pode ser usado.
fonte