Introduzione:

L'AppStore e' diventato di recente meta di molti programmatori che come il sottoscritto, cercano di creare qualcosa che possa piacere agli utenti dei melafonini e di ricavarne guadagno. Durante il recente sviluppo della mia applicazione 3D Tunnel mi sono trovato di fronte al problema comune dei crackers e dei vari siti come appulo.us che prendono le ultime releases dall'appstore ufficiale, tolgono le misure di sicurezza e autenticazione delle apps e le rimettono online gratuitamente, a disposizione di chiunque abbia un melafonino jailbreakato.

Il problema:

Ogni persona che scarica l'applicazione gratuitamente e' ovviamente una potenziale perdita di guadagno per noi programmatori, oltre che ad essere la seccatura personale di vedere i propri mesi di lavoro disponibili gratuitamente per colpa di qualche ragazzino 13enne (crackare le apps e' un'operazione che richiede approssimativamente 5 minuti, con gli strumenti necessari). Apple da parte sua non sembra sforzarsi piu' di troppo per impedire tutto questo, quindi dobbiamo trovare il modo di arrangiarci.

La soluzione:

Fortunatamente ci sono persone come Oliver Drobnik che si sono gia' messe all'opera tempo fa per trovare un modo per proteggersi da questo inconveniente. Ho implementato l'idea di Oliver (che spieghero' tra un secondo) nel mio 3D Tunnel perche' l'ho ritenuta favolosa e meritevole di nota su pierotofy.it. 

Partiamo dal presupposto che una protezione software sicura al 100% non esiste (perche' lo sappiamo bene che un reverser abbastanza bravo, con il tempo necessario, riuscira' sempre a trovare un modo per sbloccare le nostre protezioni) e che quando un'applicazione viene crackata e inserita nei vari siti per il download essa riceve un gran numero di downloads (il mio 3D Tunnel penso sia stato scaricato un migliaio di volte in meno di una settimana). La domanda che dobbiamo porgi e' questa: come possiamo sfruttare il fatto che la nostra protezione verra' crackata in un modo o nell'altro e che l'applicazione ricevera' molta visibilita'?

Oliver risponde alla domanda con il semplice concetto di Silent Lite (demo silenziosa). Esistono vari modi per verificare se l'applicazione e' stata crackata o meno. Un breve esempio lo trovate qui.

 

 

#if HEARTBEAT_CHECK_PIRACY
+ (BOOL)isCracked {

#if TARGET_IPHONE_SIMULATOR
	return NO;
#else
	static BOOL isCracked = NO;
	static BOOL didCheck = NO;
	if(didCheck) return isCracked;

#if HEARTBEAT_PIRACY_THRESHOLD >= 1
	if([[[NSBundle mainBundle] infoDictionary] objectForKey:@"SignerIdentity"] != nil) {
		#if HEARTBEAT_PIRACY_THRESHOLD >= 2
		NSString* infoPath = [[NSBundle mainBundle] pathForResource:@"Info" ofType:@"plist"];
		if([[NSString stringWithContentsOfFile:infoPath encoding:NSUTF8StringEncoding error:NULL] rangeOfString:@""].location != NSNotFound) 
                {
			#if HEARTBEAT_PIRACY_THRESHOLD >= 3
			NSDate* infoModifiedDate = [[[NSFileManager defaultManager] fileAttributesAtPath:infoPath traverseLink:YES] fileModificationDate];
			NSDate* pkgInfoModifiedDate = [[[NSFileManager defaultManager] fileAttributesAtPath:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"PkgInfo"] traverseLink:YES] fileModificationDate];
			if([infoModifiedDate timeIntervalSinceReferenceDate] > [pkgInfoModifiedDate timeIntervalSinceReferenceDate]) {		
                        #endif
                #endif
		isCracked = YES;
		#if HEARTBEAT_PIRACY_THRESHOLD >= 2
			#if HEARTBEAT_PIRACY_THRESHOLD >= 3
			}
			#endif
		}
		#endif
	}	
        #endif

	didCheck = YES;

	return isCracked;
#endif
}
#endif

 

Oppure potete fare una donazione ad Oliver tra i 30 e i 100 euro e lui vi inviera' i sorgenti del suo AntiCrack, che oltre ad includere un modo per controllare se il codice e' crackato o meno rendera' la vita molto difficile ai crackers grazie ad una serie di protezioni aggiuntive (piu' informazioni sono sul suo sito in lingua inglese).

Una volta che avete a disposizione un modo per vedere se la vostra applicazione e' stata compromessa, cosa si fa? I meno esperti sicuramente penseranno di visualizzare un messaggio del tipo "Applicazione non valida perche' crackata" e di chiudere l'app. SBAGLIATO!

Cosi' facendo il cracker si accorgera' che state usando un metodo di protezione e impieghera' ulteriore tempo per rompere la vostra protezione! E noi sappiamo che con un po' di tempo a disposizione, probabilmente ci riuscira'.

Invece noi siamo piu' furbi. Noi vogliamo che la nostra applicazione venga inserita senza difficolta' nei canali illegali della pirateria, ma vogliamo che essa resti vigile di chi ha realmente acquistato l'applicazione e chi no. Perche' al momento giusto, possiamo decidere di far diventare tutte le copie illegalmente scaricate delle versioni demo o addirittura obbligare gli utenti che hanno scaricato illegalmente la nostra app ad acquistare la versione ufficiale!

E come?

Ho scritto una piccola classe nel mio 3D Tunnel che voglio condividere con voi. Questa classe si occupa di creare un file nella directory del gioco che tiene traccia di quanti giorni sono passati dalla prima esecuzione dell'app. E se sono passati piu' di 5 giorni, il metodo timeToSetLiteVersionOn ritorna true, consentendoci di cambiare il comportamento dell'applicazione. Perche' 5 giorni? Perche' il cracker non deve accorgersi che sta inviando ai vari siti un'applicazione Lite, e trovo che 5 giorni siano un tempo sufficiente per raggiungere questo obiettivo. Se infatti si dovesse accorgere del trucco, non invierebbe l'applicazione e non potremmo mai avere la nostra enorme pubblicita' gratuita.

Ma ecco il codice. Usatelo liberamente per le vostre applicazioni commerciali e non, e se trovate un modo per salvare le informazioni in maniera persistente nell'iPhone (ora eliminando il gioco e ricaricandolo dara' all'utente altri 5 giorni di versione completa, anche se non e' un gran problema visto che la percentuale di persone che capira' questo trucco e' probabilmente molto bassa) per favore fatemelo sapere. Scusate se non ho curato molto il design:

TimeChecker.h

//
//  TimeChecker.h
//  3DTunnel
//
//  Created by Piero Toffanin on 10/13/09.
//

#import 


@interface TimeChecker : NSObject {
	NSDate *firstExecutionDate;
}
-(BOOL)timeToSetLiteVersionOn;
+(NSString *)getWorkingTimeCheckerPath;
- (BOOL)dateGreaterThan:(NSDate *)aDate Date:(NSDate *)bDate;
@end

 

TimeChecker.m

//
//  TimeChecker.m
//  3DTunnel
//
//  Created by Piero Toffanin on 10/13/09.
//

#import "TimeChecker.h"

@implementation TimeChecker

-(id)init{
	if (self = [super init]) {
		firstExecutionDate = nil;

		NSData *data = [NSData dataWithContentsOfFile:[TimeChecker getWorkingTimeCheckerPath]];
		if (data != nil){
			//We have a countdown going on! haha
			firstExecutionDate = [NSKeyedUnarchiver unarchiveObjectWithData:data];
			//NSLog(@"Restore: %@",firstExecutionDate);
		}else{
			//First timer, let's start countdown
			firstExecutionDate = [NSDate date];
			data = [NSKeyedArchiver archivedDataWithRootObject: firstExecutionDate];
			[data writeToFile:[TimeChecker getWorkingTimeCheckerPath] atomically:YES];
			//NSLog(@"Wrote today's date");
		}

	}
	return self;
}
- (BOOL)dateGreaterThan:(NSDate *)aDate Date:(NSDate *)bDate {
	BOOL result = NO;
	if([aDate compare:bDate] == NSOrderedDescending) result = YES;
	return result;
}
-(BOOL)timeToSetLiteVersionOn{
	int daysToAdd = 5; //After 5 days... DEMO!
	NSDate *expirationDate = [firstExecutionDate addTimeInterval:60*60*24*daysToAdd];
	NSDate *today = [NSDate date];
	if ([self dateGreaterThan:today Date:expirationDate]){

		//Makes sure that they don't pull back the clock and still make it work.. :)
		firstExecutionDate = [NSDate dateWithTimeIntervalSince1970:0];
		NSData *data = [NSKeyedArchiver archivedDataWithRootObject: firstExecutionDate];
		[data writeToFile:[TimeChecker getWorkingTimeCheckerPath] atomically:YES];

		//Return
		return YES;
	}
	return NO;
}	
+(NSString *)getWorkingTimeCheckerPath{
	NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);

	return [[paths objectAtIndex:0] stringByAppendingPathComponent:@"highscores.plist"]; //LOL in fact we save the expiration date for crackers
}

@end

 

Vostro modulo principale con il codice di inizializzazione:

if (isCracked()){
	NSLog(@"Loaded :)");

	TimeChecker *tc = [[TimeChecker alloc] init];
	if ([tc timeToSetLiteVersionOn]){
		LITE = YES;
	}
	[tc release];
}else {
	NSLog(@"Loaded");
}

 

 

 

Conclusione

La pirateria delle applicazioni per l'iPhone non dev'essere per forza un male. Usando un po' di furbizia, noi sviluppatori possiamo usufruire di essa per farci un po' di pubblicita' gratuita a basso costo. Nelle domande frequenti di Appulo.us dicono che il sito esiste perche' manca un sistema che permetta agli utenti di provare le applicazioni prima di comprarle dall'iTunes store. Ebbene, anche se i gestori di appulo mancano proprio di etica perche' sono presenti cracks per applicazioni dove esistono versioni Lite, con quest'articolo spero di aver dato agli sviluppatori iPhone come me un buon modo per far rispettare questa premessa: volete provare la mia app? Liberi di farlo. Ma dopo 5 giorni... pagate o niente. :)