Pergunta

Estou passando pelo MSIL e notando que há muitos não, não instruções.O artigo do MSDN diz que eles não realizam nenhuma ação e são usados ​​para preencher espaço se o código de operação for corrigido.Eles são usados ​​muito mais em compilações de depuração do que em compilações de lançamento.Eu sei que esses tipos de instruções são usados ​​em linguagens assembly para garantir que um código de operação se encaixe no limite de uma palavra, mas por que ele é necessário no MSIL?

Foi útil?

Solução

Os NOPs servem vários propósitos:

  • Eles permitem que o depurador coloque um ponto de interrupção em uma linha, mesmo que ele seja combinado com outros no código gerado.
  • Ele permite que o carregador corrija um salto com um deslocamento de alvo de tamanho diferente.
  • Ele permite que um bloco de código seja alinhado em um limite específico, o que pode ser bom para armazenamento em cache.
  • Ele permite links incrementais para substituir pedaços de código com uma chamada para uma nova seção sem ter que se preocupar com a mudança de tamanho geral da função.

Outras dicas

Veja como os nops são usados ​​pela depuração:

Nops são usados ​​por compiladores de linguagem (C#, VB, etc.) para definir pontos de sequência implícitos.Eles informam ao compilador JIT onde garantir que as instruções de máquina possam ser mapeadas de volta para instruções IL.

Entrada do blog de Rick Byer em DebuggingModes.IgnoreSymbolStoreSequencePoints, explica alguns detalhes.

C# também coloca instruções Nops após a chamada para que o local do site de retorno na origem seja a chamada, e não a linha após a chamada.

Ele oferece uma oportunidade para marcadores baseados em linhas (por exemplo,pontos de interrupção) no código onde uma compilação de lançamento não emitiria nenhum.

Também pode tornar o código executado mais rapidamente, ao otimizar para processadores ou arquiteturas específicas:

Por muito tempo, os processadores empregaram vários pipelines que funcionam aproximadamente em paralelo, de modo que duas instruções independentes podem ser executadas ao mesmo tempo.Em um processador simples com dois pipelines, o primeiro pode suportar todas as instruções, enquanto o segundo suporta apenas um subconjunto.Além disso, existem algumas paralisações entre os pipelines quando é necessário aguardar o resultado de uma instrução anterior que ainda não foi concluída.

Nestas circunstâncias, um dedicado não, não pode forçar a próxima instrução em um pipeline específico (o primeiro, ou não o primeiro) e melhorar o emparelhamento das instruções seguintes para que o custo do não, não é mais que amortizado.

Cara!O modo autônomo é incrível!É uma instrução que não faz nada além de consumir tempo.Na obscura idade das trevas, você o usaria para fazer microajustes no tempo em loops críticos ou, mais importante, como preenchimento em código automodificável.

Em um processador em que trabalhei recentemente (por quatro anos), o NOP foi usado para garantir que a operação anterior fosse concluída antes que a próxima operação fosse iniciada.Por exemplo:

Valor de carga para se registrar (leva 8 ciclos) NOP 8 Adicione 1 para registrar

Isso garantiu que o registro tivesse o valor correto antes da operação de adição.

Outro uso era preencher unidades de execução, como os vetores de interrupção que tinham que ter um determinado tamanho (32 bytes) porque o endereço do vetor0 era, digamos, 0, para o vetor 1 0x20 e assim por diante, então o compilador colocava NOPs lá se necessário.

Um uso clássico para eles é que seu depurador sempre possa associar uma linha de código-fonte a uma instrução IL.

Eles poderiam estar usando-os para apoiar editar e continuar durante a depuração.Ele fornece ao depurador espaço para trabalhar para substituir o código antigo por um novo sem alterar as compensações, etc.

Nop é essencial na carga útil de explorações de buffer overflow.

Como disse o ddaa, nops permite contabilizar a variação na pilha, de modo que, quando você sobrescreve o endereço de retorno, ele salta para o trenó nop (muitos nops seguidos) e, em seguida, atinge o código executável corretamente, em vez de pular para algum byte na instrução que não é o começo.

50 anos tarde demais, mas ei.

Os Nop's são úteis se você estiver digitando o código assembly manualmente.Se você tivesse que remover o código, não conseguiria identificar os opcodes antigos.

da mesma forma, você pode inserir um novo código substituindo algum código de operação e pular para outro lugar.Lá você coloca os opcodes sobrescritos e insere seu novo código.Quando estiver pronto, você pula para trás.

Às vezes você tinha que usar as ferramentas que estavam disponíveis.Em alguns casos, este era apenas um editor de código de máquina muito básico.

Hoje em dia com compiladores as técnicas não fazem mais sentido.

No cenário de cracking de software, um método clássico para desbloquear um aplicativo seria corrigir com um NOP a linha que verifica a chave ou registro ou período de tempo ou algo assim, de modo que não faria nada e simplesmente continuaria iniciando o aplicativo como se estivesse registrado .

Também vi NOPs em código que se modifica para ofuscar o que faz como espaço reservado (proteção contra cópia muito antiga).

Um uso um tanto heterodoxo é Slides NOP, usado em explorações de buffer overflow.

Eles permitem que o vinculador substitua uma instrução mais longa (normalmente salto em distância) por uma mais curta (salto curto).O NOP ocupa o espaço extra - o código não pode ser movido, pois isso impediria o funcionamento de outros saltos.Isso acontece no momento do link, portanto o compilador não consegue saber se um salto longo ou curto seria apropriado.

Pelo menos, esse é um dos seus usos tradicionais.

Esta não é uma resposta à sua pergunta específica, mas antigamente você poderia usar um NOP para preencher um slot de atraso de ramificação, se você não conseguir preenchê-lo com uma instrução útil.

Os compiladores .NET alinham a saída MSIL?Imagino que possa ser útil para acelerar o acesso ao IL...Além disso, meu entendimento é que ele foi projetado para ser portátil e acessos alinhados são necessários em algumas outras plataformas de hardware.

O primeiro assembly que aprendi foi o SPARC então estou familiarizado com o slot de atraso de ramificação, se você não consegue preenchê-lo com outra instrução, geralmente a instrução que você iria colocar acima da instrução de ramificação ou incrementar um contador em loops, você usa um NOP.

Não estou familiarizado com cracking, mas acho comum substituir a pilha usando NOP para que você não precise calcular exatamente onde começa sua função maliciosa.

Usei NOPs para ajustar automaticamente a latência acumulada após entrar em um ISR.Muito útil para acertar o tempo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top