Un velocissimo RE in Java

Ancora un caso di reversing, ancora su Java. Ovviamente non posso esplicitare il nome del prodotto, dato che la nuova versione ancora non è stata rilasciata.

Il lavoro è stato particolarmente semplice, tuttavia mi è sembrato un caso talmente eclatante di pessima (o voglio sperare completamente assente) code auditing che merita di essere pubblicato.

Inserisco il cd e installo il pacchetto su una VM creata allo scopo con VMWare. Il setup tenta una installazione di un JRE piuttosto vecchiotto, quindi estrae nella solita directory  “C:\Program Files (x86)\NomeProdotto” i seguenti file:

ced01

Nella cartella “bin” troviamo solo una manciata di risorse grafiche, una (seppur interessante) dll “ALMGRTMM” non firmata e priva di informazioni di versione, un launcher exe per il pacchetto jar principale e un ulteriore directory “lib” piena zeppa di librerie jar.

ced02

DJ Java Decompiler è in grado di decomprimere e decompilare in modo accettabile la maggior parte dgli archivi jar.

Navigando a vista tra le librerie, ce n’è una che balza alla mia attenzione, se non altro per il nome: componentsLcsSrv (license services?). Al suo interno, troviamo la classe ACTKeyLicenseMgr.

Il metodo main di detta classe presenta subito qualcosa di interessante:

public static void main(String args[])
{
   ACTKeyLicenseMgr oAK = new ACTKeyLicenseMgr();
   String sWK = oAK.getWEBKey("1A2B3456");
   System.out.println(sWK);
   System.out.println(oAK.isValidActKey("12345A67890-1B2-C"));
}

Ovviamente ho sostituito entrambe le stringhe alfanumeriche nello snippet. E sapete perché? Perché sono chiavi valide. Valide ed universali, ovvero permettono al pacchetto di essere attivato su qualunque postazione.

Faccio uno snapshot della virtual machine e tento l’attivazione con le chiavi estratte: successo.

Continuando l’analisi del codice della classe, balza subito agli occhi questo metodo:

public int isValidActKey(String actKey)
{
	boolean bValid = false;
	long lPCKey = getPCKey();
	if(actKey.indexOf("$") > 0)
		actKey = actKey.substring(0, actKey.indexOf("$"));
	int iIdx1 = actKey.indexOf("-");
	int iIdx2 = actKey.indexOf("-", iIdx1 + 1);
	String sCryptProdId = actKey.substring(iIdx1 + 1, iIdx2);
	String sCod = actKey.substring(0, actKey.length() - 2);
	if(!actKey.substring(actKey.length() - 1).equals(chkSum(sCod)))
		return -1;
	sCod = sCod.substring(0, sCod.indexOf("-"));
	String sModVal = sCod.substring(sCod.length() - 3);
	int iIdProd = (int)(Long.parseLong(sCryptProdId, 16) / (Long.parseLong(sModVal) % 11L + 1L));
	sCod = sCod.substring(0, sCod.length() - 3);
	if(Long.parseLong(sCod, 16) % 325L != Long.parseLong(sModVal))
		return -1;
	for(int i = 1; i < 5034; i++)
	{
		long lCurr = (lPCKey * (long)i) % (long)(Math.pow(2D, 31D) - 1.0D);
		if(Long.parseLong(sCod, 16) == lCurr)
			bValid = true;
	}

	if(!bValid)
            iIdProd = -1;

        return iIdProd;
 }

Il nome non lascia spazio a dubbi, l’algoritmo è relativamente semplice e si presta alla realizzazione di un piccolo keygen; infine è di fatto possibile alterare le ultime tre istruzioni per costringere il software a restituire una chiave sempre valida: una rapida analisi dell’unico punto in cui isValidActKey viene richiamata ci rivela infatti che il software si aspetta di ricevere il valore costante 419 per autorizzare l’attivazione.

Una volta ricompilato il sorgente java in un file .class e ricreato il pacchetto jar, il software è completamente sprotetto ed è possibile registrarlo con le chiavi colpevolmente contenute nel sorgente.