2012-11-12 8 views
7

Il progetto legacy su cui sto lavorando include alcune librerie esterne in una serie di file jar binari. Abbiamo deciso che per l'analisi e le potenziali patching, vogliamo ricevere le fonti di questa libreria, usarle per costruire nuovi binari e dopo aver eseguito il test di regressione dettagliato e abbastanza lungo su questi binari.Come verificare se i binari sono creati da particolari fonti

Si supponga di aver già recuperato e creato le origini (in realtà sono in fase di pianificazione). Prima di veri test, vorrei eseguire alcuni "controlli di compatibilità" per escludere la possibilità che le fonti rappresentino qualcosa di molto diverso da ciò che è presente nei "vecchi" binari.

Utilizzo dello strumento javap Sono stato in grado di estrarre la versione di JDK utilizzata per la compilazione (almeno credo sia la versione di JDK). Dice che i binari sono stati creati usando la versione principale 46 e la minore 0. Secondo this article, si associa a JDK 1.2.

Si supponga di utilizzare lo stesso JDK per la compilazione delle fonti.

La domanda è la seguente: Esiste un metodo di verifica affidabile e possibilmente efficace se entrambi questi binari sono creati dalle stesse origini? Vorrei sapere se tutte le firme dei metodi e le definizioni delle classi sono identiche e se la maggior parte o forse tutte le implementazioni dei metodi sono identiche/simili.

La libreria è abbastanza grande, quindi penso che l'analisi dettagliata dei binari decompilati potrebbe non essere un'opzione.

+0

Reflection ('java.lang.reflect') dovrebbe fare per le firme di classe e di metodo, ma non per l'attuazione. – SJuan76

+0

Che dire del confronto tra gli hash MD5 dei due binari? – sp00m

+1

Per riferimento futuro, il modo più semplice per capire come andare avanti (non ti aiuterà ora) è usare un sistema di controllo della versione come Git, Subversion o Mercurial, quindi includere il numero di revisione e/o l'ID del changeset nel tuo jar, come nel file manifest. – Brian

risposta

1

suggerisco un processo a più stadi:

Applicare la precedenza suggerito Jardiff o simili per vedere se ci sono delle differenze di API. Se possibile, scegli uno strumento che abbia un'opzione per la segnalazione di metodi privati, ecc. In pratica, qualsiasi modifica sostanziale dell'implementazione in Java rischia di cambiare alcuni metodi e classi, anche se l'API pubblica è invariata.

Se si dispone di una corrispondenza API, compilare alcuni file selezionati casualmente con il compilatore indicato, decompilare il risultato e i file di classe originali e confrontare i risultati. Se corrispondono, applica lo stesso processo a corpi di codice sempre più grandi finché non trovi una mancata corrispondenza o hai controllato tutto.

I differimenti del codice decompilato sono più propensi a fornire indicazioni sulla natura delle differenze e sono più facili da filtrare per differenze non significative rispetto ai file di classe effettivi.

Se si verifica una mancata corrispondenza, analizzarla. Potrebbe essere dovuto a qualcosa a cui non ti importa. In tal caso, provare a creare uno script che eliminerà tale forma di differenza e riprendere il processo di compilazione e confronto. Se si verificano disallineamenti diffusi, sperimentare parametri del compilatore come l'ottimizzazione. Se le regolazioni dei parametri del compilatore eliminano le differenze, continuare con il confronto di massa. L'obiettivo in questa fase è trovare una combinazione di parametri del compilatore e filtri di codice decompilati che producano una corrispondenza nei file di esempio e applicarli al confronto di massa della libreria.

Se non è possibile ottenere una corrispondenza ragionevolmente stretta nel codice decompilato, probabilmente non si dispone del codice sorgente corretto. Anche in questo caso, se si dispone di una corrispondenza API, potrebbe valere la pena costruire il proprio sistema ed eseguire i test utilizzando il risultato della compilazione. Se i test vengono eseguiti almeno altrettanto bene con la versione creata dall'utente, continuare a utilizzarlo.

+0

Ho deciso di utilizzare la maggior parte dei tuoi suggerimenti. Grazie :) –

0

Esistono numerosi strumenti di confronto JAR. Uno che era abbastanza buono è Jardiff. Non l'ho usato da un po 'ma sono sicuro che è ancora disponibile. Ci sono anche alcune offerte commerciali nello stesso spazio che potrebbero soddisfare le tue esigenze.

0

Jardiff che Percezione menzionata è un buon inizio, tuttavia non c'è modo di farlo in modo sicuro al 100% in teoria. Questo perché la stessa fonte può essere compilata con diversi compilatori e diverse configurazioni del compilatore e livelli di ottimizzazione. Quindi non c'è modo di confrontare il codice binario (bytecode) oltre le firme di classe e metodo.

Cosa intendi per "implementazione simile" di un metodo? Supponiamo che un compilatore intelligente cada un caso else perché capisce che la condizione potrebbe non essere mai vera. Sono i due simili? Sì e no .. :-)

Il modo migliore per andare IMHO è la creazione di casi di test di regressione molto buoni che controllano tutte le funzionalità chiave delle vostre librerie. Questo potrebbe essere un orrore, ma a lungo termine potrebbe essere più economico della caccia agli insetti. Tutto dipende dai tuoi piani futuri in questo progetto. Non è una decisione facile e banale.

0

Per le firme dei metodi, utilizzare uno strumento come jardiff.

Per la somiglianza dell'implementazione, è necessario ricorrere a un'ipotesi. Il confronto del bytecode a livello di opcode può dipendere dal compilatore e portare a un gran numero di falsi negativi. In tal caso, è possibile ricorrere a un confronto tra i metodi di una classe utilizzando LineNumberTable.

Fornisce un elenco di numeri di riga per ciascun metodo (purché il file di classe sia stato compilato con il flag di debug, che spesso manca nelle librerie molto vecchie o commerciali).

Se due file di classe sono compilati dallo stesso codice sorgente, almeno i numeri di riga di ciascun metodo devono corrispondere esattamente.

È possibile utilizzare una libreria come Apache BCEL per recuperare la LineNumberTable:

// import org.apache.bcel.classfile.ClassParser; 
    JavaClass fooClazz = new ClassParser("Foo.class").parse(); 
    for(Method m : fooClazz.getMethods()) 
    { 
    LineNumberTable lnt = m.getLineNumberTable(); 
    LineNumber[] tab = lnt.getLineNumberTable(); 
    for(LineNumber ln : tab) 
    { 
     System.out.println(ln.getLineNumber()); 
    } 
    }