2009-03-22 13 views
16

Perché è così facile decompilare il codice IL in codice sorgente, rispetto alla decompilazione dei binari nativi x86? (Reflector produce codice sorgente abbastanza buono la maggior parte del tempo, mentre decompilare l'output di un compilatore C++ è quasi impossibile.)Perché è così facile decompilare il codice .NET IL?

È perché IL contiene molti metadati? Oppure perché IL è un'astrazione più elevata rispetto alle istruzioni x86? Ho fatto qualche ricerca e ho trovato i seguenti due articoli utili, ma nessuno dei due risponde alla mia domanda.

+0

Ci sono (o sono stati) decompilatori piuttosto buoni per C/C++, con plugin di libreria per diverse versioni di Watcom, Borland, Microsoft e altri popolari compilatori. Per esempio. IDA. Ciò non rende ciò che dici sbagliato, tuttavia, CLI * è * un ambiente di runtime più astratto, di livello superiore ma più pulito di x86. –

+0

IDA non è un decompilatore, sebbene la società che lo produce faccia decompilare i raggi esagonali. La qualità della decompilazione x86 è molto più bassa della qualità della decompilazione jvm o msil. –

risposta

24

Penso che tu abbia già i bit più importanti.

  • Come dici tu, sono disponibili più metadati. Non conosco i dettagli di ciò che viene emesso da un compilatore C o C++, ma ho il sospetto che lo lontano altri nomi e informazioni simili siano inclusi in IL. Basta vedere ciò che il decompilatore sa su cosa si trova in un particolare stack frame, ad esempio - per quanto riguarda x86, si sa solo come lo stack è utilizzato; in IL sai che contenuto della pila rappresenta (o almeno, il tipo - non il significato semantico!)
  • Ancora, come hai già detto, IL è un'astrazione di livello superiore rispetto a x86. x86 non ha idea di cosa sia una chiamata a metodo o funzione, o un evento o una proprietà, ecc. IL ha tutte le informazioni ancora al suo interno.
  • In genere i compilatori C e C++ ottimizzano molto più pesantemente del (diciamo) compilatore C#. Questo perché il compilatore C# presuppone che la maggior parte dell'ottimizzazione possa ancora essere eseguita in seguito, dal JIT.In qualche modo ha senso che il compilatore C# non sia per cercare di fare molta ottimizzazione, poiché ci sono varie informazioni che sono disponibili per il JIT ma non per il compilatore C#. Il codice ottimizzato è più difficile da decompilare, perché è più lontano dall'essere una rappresentazione naturale del codice sorgente originale.
  • IL è stato progettato per essere compilato JIT; x86 è stato progettato per essere eseguito in modo nativo (ovviamente tramite micro-codice). Le informazioni richieste dal compilatore JIT sono simili a quelle che un decompilatore vorrebbe, quindi un decompilatore ha un tempo più semplice con IL. In un certo senso, questa è solo una riaffermazione del secondo punto.
+4

Motivo del bonus: IL deve essere verificabile in base al tipo di errore, che limita i tipi di ottimizzazioni disponibili, altrimenti il ​​verificatore non sarà in grado di dire "Sì, questo codice non infrange alcuna regola. –

4

C# e IL quasi mappa uno-a-uno. (Questo è meno vero con alcune nuove funzionalità di C# 3.0.) La vicinanza della mappatura (e la mancanza di un ottimizzatore nel compilatore C#) rende le cose così 'reversibili'.

9

Ci sono un certo numero di cose che rendono il reverse engineering abbastanza facile.

  • Informazioni tipo. Questo è enorme. Nell'assembler x86, devi inferire i tipi di variabili in base a come vengono utilizzati.

  • struttura. Le informazioni sulla struttura dell'applicazione sono più disponibili nei disassemblaggi. Questo, combinato con le informazioni sul tipo, ti offre una quantità incredibile di dati. A questo punto stai lavorando ad un livello piuttosto alto (relativo all'assembler x86). Nell'assemblatore nativo, è necessario dedurre i layout della struttura (e anche il fatto che si tratti di strutture) in base a come vengono utilizzati i dati. Non impossibile, ma richiede molto più tempo.

  • nomi. Conoscere i nomi delle cose può essere utile.

Queste cose, combinate, significa che hai un sacco di dati sull'eseguibile. Fondamentalmente sta lavorando a un livello molto più vicino alla fonte di quello che sarebbe un compilatore di codice nativo. Più alto è il livello del bytecode, più semplice è il reverse engineering, in generale.

3

estensione corretta risposta di Brian

Se pensate che tutto IL è facilmente riconvertibile, vi suggerisco di scrivere un programma # F non banale e il tentativo di decompilare il codice. F # esegue molte trasformazioni di codice e quindi ha una mappatura molto scarsa dall'IL effettivamente emesso e dalla base di codice originale. IMHO, è molto più difficile guardare il codice F # decompilato e recuperare il programma originale piuttosto che per C# o VB.Net.