Scrivi un codice Java per rilevare la versione JVM


17

L'obiettivo è scrivere codice java che rilevi la versione JVM basandosi su modifiche di compatibilità, effetti collaterali, bug e / o comportamenti indefiniti che funzionano in un modo in una versione e in un altro modo in un'altra versione. Inoltre, il codice dovrebbe essere almeno un po 'leggibile, senza sacrificare spazi bianchi e nomi di variabili leggibili.

Per garantire tale obiettivo, le regole formali esatte sono:

  1. Il codice deve essere scritto in Java e dovrebbe generare la versione JRE in cui è in esecuzione.

  2. Il codice non deve utilizzare alcuna API JDK o JRE fornita specificamente per rilevare la versione java o che fornisce gratuitamente la versione JDK o JRE.

  3. Il codice non deve usare la riflessione.

  4. Il codice è necessario solo per funzionare in Hotspot Java SE 5, 6 e 7, ma può funzionare in altre JVM.

  5. Il codice non deve utilizzare librerie di terze parti nel percorso di classe.

  6. Il codice non deve avviare altri processi, java o meno.

  7. Il codice non deve utilizzare le variabili di ambiente.

  8. Il codice non deve cercare nel file system alla ricerca di file o cartelle preesistenti.

  9. Il codice deve essere contenuto in un singolo file ed essere chiamato tramite public static void main(String[] args)o public static void main(String... args).

  10. Il codice non deve utilizzare alcuna API non pubblica presente in JRE.

  11. Il codice non deve generare alcun NoClassDefFoundError, NoSuchMethodError, ClassNotFoundException o NoSuchMethodException durante la sua esecuzione.

  12. Il codice dovrebbe essere eseguito in un sistema disconnesso da Internet o da qualsiasi rete locale.

  13. Dovresti fornire una spiegazione del perché si comporta in un modo in una versione e in un altro in un'altra versione.

punteggio

Il metodo utilizzato per misurare la soluzione migliore è max (n / s), dove n è il numero di diverse versioni java rilevate senza violare nessuna di queste regole (almeno versioni 5, 6 e 7) e s è il numero di token lessicali nella soluzione.


Impossibile trovare un tag migliore e ho dovuto fornire almeno due. Inoltre, non ho abbastanza rappresentante per creare nuovi tag. Il motivo di Java è perché si suppone che sia un linguaggio molto portatile, quindi scrivere sarebbe molto interessante. Inoltre, le versioni java sono definite in modo da poter confrontare le voci che rilevano l'ambiente con uniformità, senza finire per confrontare le arance con le mele.
Victor Stafusa,

Potresti considerare [subdolo] sostenere che il rilevamento della versione della macchina virtuale è un passo nell'attacco al sistema. Non posso dire di avere un altro suggerimento.
dmckee,

@dmckee Hai eliminato il tag [code-golf]. Aggiungi il tag [underhanded]. Potresti creare il tag [java] per favore?
Victor Stafusa,

4
Sto votando per chiudere questa domanda come fuori tema perché le sfide subdole non sono più in argomento su questo sito. meta.codegolf.stackexchange.com/a/8326/20469
cat

@cat, dovresti invece aver rimosso il tag, perché non si adattava alla domanda.
Peter Taylor,

Risposte:


9

6/102 = 0.0588

Rileva 6 versioni. Dispone di 102 gettoni lessicali (in calo dal 103, dopo ho cancellato publicin public class).

import java.security.Signature;

class GuessVersion {
        public static void main(String[] args) {
                String version = "Java 1.1";
                try {
                        "".getBytes("ISO8859_13");
                        version = "Java 1.3";

                        "".getBytes("ISO8859_15");
                        version = "Java 1.4";

                        Signature.getInstance("SHA256withRSA");
                        version = "Java 5";

                        "".getBytes("KOI8_U");
                        version = "Java 6";

                        Signature.getInstance("SHA256withECDSA");
                        version = "Java 7";
                } catch(Exception e) {}
                System.out.println(version);
        }
}

Java 1.1 ha introdotto la codifica dei caratteri e gli algoritmi crittografici in Java. Le versioni successive hanno aggiunto più codifiche e algoritmi. Questo programma tenta di utilizzare codifiche e algoritmi fino a quando non rileva un'eccezione. Mi aspetto che venga lanciata una codifica mancante e che venga lanciato java.io.UnsupportedEncodingExceptionun algoritmo mancante java.security.NoSuchAlgorithmException.

Avevo un vecchio PowerPC Macintosh con quattro vecchie versioni di Java. La mia macchina OpenBSD ha altre due versioni, quindi ho testato queste sei versioni:

  • Java 1.1.8 in MRJ 2.2.6 per Mac OS 9.2.2
  • Java 1.3.1_16 per Mac OS X Panther
  • Java 1.4.2_21 per Mac OS X Tiger
  • Java 1.5.0_19 per Mac OS X Tiger
  • OpenJDK 1.6.0_32 per OpenBSD 5.5
  • OpenJDK 1.7.0_21 per OpenBSD 5.5

Questo programma può anche essere eseguito in JamVM 1.5.4 e gcj 4.8.2 per OpenBSD, ma non li identifica come implementazioni diverse. Stampa solo "Java 5".

Mac OS Runtime per Java

Grazie a "Scrivi una volta, esegui ovunque!", Posso scrivere questo programma una volta, compilarlo una volta ed eseguire una classe GuessVersion. in tutte e otto le macchine virtuali. Ho bisogno di un compilatore per Java 1.1, la versione più vecchia della mia collezione.

Il mio compilatore è lo javacstrumento di MRJ SDK 2.2. Poiché il Mac OS classico non aveva riga di comando, javacè uno strumento abbastanza grafico in cui seleziono file e opzioni e faccio clic su "Esegui Javac". Dopo aver modificato il mio codice, faccio di nuovo clic su "Esegui Javac".

javac di MRJ SDK 2.2 per Mac OS classico

Il modo più semplice per eseguire GuessVersion.class è aprirlo in JBindery, un altro strumento di MRJ SDK 2.2. Il runtime è MRJ 2.2.6, un'implementazione di Java 1.1.8.


22

Non sono sicuro di quale sia il mio punteggio, perché dipende da ciò che consideri esattamente un token lessicale, ma sto cercando di abusare il più possibile di quel sistema di conteggio con una lunga stringa ...

Dipende anche dal fatto che tu contenga questo come identificativo di 7 diverse versioni o 16 ... (Potrebbe essere banalmente esteso fino a 190).

class V extends ClassLoader
{
    public static void main(String[]args)
    {
        for(byte b=60;;)
            try {
                byte[]buf="\u00ca\u00fe\u00ba\u00be\u0000\u0000\u00002\u0000\u0005\u0007\u0000\u0003\u0007\u0000\u0004\u0001\u0000\u0001A\u0001\u0000\u0010java/lang/Object\u0006\u0000\u0000\u0001\u0000\u0002\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000".getBytes("ISO-8859-1");
                buf[7]=b--;
                new V().defineClass(buf,0,53);
                System.out.println(b-43);
                break;
            }
            catch(Throwable t){}
    }
}

Funziona tentando di definire un'interfaccia in un classloader personalizzato con numeri di versione principali decrescenti del formato di classe. Il primo che non lancia un java.lang.UnsupportedClassVersionErrorcorrisponde alla versione della VM.


Contati 84 token. Tuttavia non l'ho ancora testato.
Victor Stafusa,

La tua risposta è geniale. Potrebbe banalmente ridurre a 83 token usando String... args.
Victor Stafusa,

@Victor, ciò complicherebbe la questione se supporta ancora 7 versioni diverse. Non sono a conoscenza di alcun compilatore che supporti la sintassi Java 5 e compili in file di classe compatibili con Java 1.
Peter Taylor,

Buon punto. Me ne sono dimenticato.
Victor Stafusa,

1
Java 1.1.8 (in MRJ 2.2.6) non è riuscito a compilare questo, fino a quando ho aggiunto 17 più gettoni: protected Class loadClass(String name, boolean resolve) { return Object.class; }. Gli attuali documenti API trascurano di menzionare come questo fosse un metodo astratto prima di Java 1.2. Restituisco Object.class perché il metodo riceve una chiamata per "java.lang.Object".
kernigh,

8
class Evil {
    public static void main(String... args) {
        String s1 = "Good";
        s1 += "morning";
        int a = 7;
        if (s1 != s1.intern())
            try {
                a--;
                javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar().equals(null);
            } catch (Throwable e) {
                a--;
            }
        System.out.println(a);
    }
}

L'algoritmo di interning è cambiato tra Java 6 e 7. Vedi /programming//a/7224864/540552

XMLGregorianCalendar.equals (null) usato per lanciare NullPointerException in java 5, ma questo è stato risolto in java 6. Vedi http://bugs.sun.com/view_bug.do?bug_id=6285370

100 96 92 87 85 token qui. Grazie a Peter Taylor per aver ridotto 7 token.


1
È possibile salvare 3 token memorizzando il numero di versione in s1. Probabilmente puoi salvarne altri 2 catturandoli direttamente, supponendo che DatatypeConfigurationExceptionnon vengano lanciati.
Peter Taylor,

1
O meglio, continua int ama inizializzalo immediatamente, in modo che il ifblocco si svuoti. Annulla la condizione, rimuovi l'altro e usa --invece di assegnazione diretta a a.
Peter Taylor,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.