Pergunta

Por que é tão fácil de decompor .NET IL-código em código-fonte, em comparação com Decompiling binários x86 nativas? (Refletor produz muito bom código-fonte a maior parte do tempo, enquanto Decompiling a saída de um compilador C ++ é quase impossível.)

É porque IL contém uma grande quantidade de dados meta? Ou é porque IL é uma abstração maior que x86 instruções? Eu fiz alguma pesquisa e encontrou os seguintes dois artigos teis, mas nenhum deles responde a minha pergunta.

Foi útil?

Solução

Eu acho que você tem os bits mais importantes já.

  • Como você disse, há mais metadados disponíveis. Eu não sei os detalhes do que é emitido por um compilador C ou C ++, mas eu suspeito muito mais nomes e informações semelhantes estão incluídos no IL. Basta olhar para o que o decompiler sabe sobre o que está em um quadro de pilha particular, por exemplo -, tanto quanto o x86 está em causa, você só sabe como a pilha é usado ; em IL você sabe o que o conteúdo da pilha representa (ou, pelo menos, o tipo - não o significado semântico)
  • Mais uma vez, como você já mencionado, IL é uma abstração nível superior x86. x86 não tem idéia do que um método ou função de chamada é, ou um evento, ou uma propriedade etc. IL tem toda essa informação ainda dentro dela.
  • Normalmente C e compiladores C ++ otimizar muito mais fortemente do que (digamos) o compilador C #. Isso ocorre porque o compilador C # assume que a maior parte da otimização ainda pode ser realizada mais tarde - pelo JIT. De certa forma, faz sentido para o compilador C # não para tentar fazer muito otimização, uma vez que existem vários pedaços de informações que estão disponíveis para o JIT, mas não o compilador C #. código otimizado é mais difícil de descompilar, porque é mais longe de ser uma representação natural do código-fonte original.
  • IL foi projetado para ser compilado-JIT; x86 foi projetado para ser executado nativamente (reconhecidamente via micro-código). As informações que o compilador JIT precisa é semelhante ao que um decompiler gostaria, assim que um decompiler tem um tempo mais fácil com IL. De certa forma isso é realmente apenas uma reafirmação do segundo ponto.

Outras dicas

Há uma série de coisas que fazem il engenharia reversa bastante fácil.

  • Tipo de informações. Este é enorme. Em x 86 assembler, você tem que inferir os tipos de variáveis ??com base em como eles são usados.

  • estrutura. Informações sobre a estrutura da aplicação é mais disponível em desmontagens il. Isto, combinado com informações de tipo, dá-lhe uma incrível quantidade de dados. Você está trabalhando em um nível bastante elevado neste momento (em relação ao assembler x86). Em assembler nativa, você tem que deduzir os layouts estrutura (e até mesmo o fato de que eles são estruturas) com base em como os dados são usados. Não é impossível, mas muito mais demorado.

  • Nomes. Sabendo os nomes das coisas pode ser útil.

Essas coisas, combinado, significa que você tem um monte de dados sobre o executável. Il é basicamente trabalhando em um nível muito mais próximo da fonte do que um compilador de código nativo seria. O nível mais elevado do bytecode trabalha, a engenharia reversa mais fácil é, de um modo geral.

C # e IL quase mapear um-para-um. (Isso é menos assim com alguns C # 3.0 recursos mais recentes.) A proximidade do mapeamento (e a falta de um otimizador no compilador C #) torna as coisas tão 'reversível'.

Estendendo resposta correta de Brian

Se você acha que tudo IL é facilmente descompilável, sugiro escrever um programa não-trivial F # e tentar descompilar esse código. F # faz uma série de transformações de código e, portanto, tem uma muito pobre mapeamento do IL emitida real e a base de código original. IMHO, é significativamente mais difícil olhar para código compilado F # e voltar ao programa original do que é para C # ou VB.Net.

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