Trivia Crack is een zeer populair spel voor zowel web- als mobiele platforms, dat enigszins is gemodelleerd naar Trivial Pursuit. Het is de nieuwste rage in sociaal gamen, waardoor gebruikers kunnen strijden tegen hun vrienden en vreemden bij het beantwoorden van vragen uit een reeks categorieën. Hoewel ik nooit erg geïnteresseerd ben geweest in gamen, is mijn vrouw onlangs een grote fan van Trivia Crack geworden. Nadat ik haar een tijdje had zien spelen, besloot ik het te downloaden en nader te bekijken hoe het werd geïmplementeerd.
Ik begon met het monitoren van de web-API-verzoeken die via het netwerk werden gedaan terwijl ik de Android-app gebruikte. Al snel merkte ik iets interessants op tijdens de werking van het spel. Het leek erop dat de app de categorie, de vraag en het antwoord ontving van de Trivia Crack-servers voordat de gebruiker zelfs maar aan het 'categoriewiel' begon te draaien.
Hieronder ziet u een voorbeeldreactie die de app ophaalt voordat dit scherm wordt weergegeven:
{"id": 2747994099,
"opponent": {
"id": 0,
"alerts_count": 0,
"username": "smartplay(tm)"
},
"game_status": "PENDING_APPROVAL",
"language": "EN",
"created": "03/23/2015 08:58:29 EST",
"last_turn": "03/23/2015 08:58:29 EST",
"type": "NORMAL",
"expiration_date": "03/26/2015 08:58:29 EST",
"my_turn": true,
"statistics": {
"player_one_statistics": {
"category_questions": [
{
"category": "GEOGRAPHY",
"correct": 1,
"incorrect": 0,
"worst": false
}
],
"correct_answers": 1,
"incorrect_answers": 0,
"challenges_won": 0,
"questions_answered": 1,
"crowns_won": 0
},
"player_two_statistics": {
"correct_answers": 0,
"incorrect_answers": 0,
"challenges_won": 0,
"questions_answered": 0,
"crowns_won": 0
}
},
"duelGameType": false,
"normalType": true,
"spins_data": {
"spins": [
{
"type": "NORMAL",
"questions": [
{
"question": {
"id": 14996887,
"category": "SPORTS",
"text": "Who was the first woman gymnast to score a perfect ten at the Olympics?",
"answers": [
"Nadia Comaneci",
"Mo Huilan",
"Tatiana Gutsu",
"Agnes Keleti"
],
"author": {
"id": 71534267,
"name": "Florentina Ionela Gagliano",
"username": "florentina.gagliano",
"facebook_id": "100000030456122",
"facebook_name": "Florentina Ionela Gagliano",
"fb_show_picture": true,
"fb_show_name": true
},
"correct_answer": 0,
"media_type": "NORMAL"
},
"powerup_question": {
"id": 8534934,
"category": "SPORTS",
"text": "In basketball, what does it mean to \"kiss it off the glass\"?",
"answers": [
"Make both free throws",
"Pass off someone's back",
"Dribble past two people",
"Hit a shot off the backboard"
],
"author": {
"id": 41439403,
"name": "tsan.819",
"username": "tsan.819",
"fb_show_picture": false,
"fb_show_name": false
},
"correct_answer": 3,
"media_type": "NORMAL"
}
}
]
}
]
},
"available_crowns": [
"SCIENCE",
"ARTS",
"HISTORY",
"ENTERTAINMENT",
"SPORTS",
"GEOGRAPHY"
],
"my_player_number": 1,
"available_extra_shots": 1,
"player_one": {
"charges": 1
},
"player_two": {
"charges": 0
},
"round_number": 1,
"sub_status": "P1_PLAYING_FIRST_TURN",
"previous_sub_status": "P1_WAITING_FIRST_TURN",
"is_random": true,
"unread_messages": 0,
"status_version": 1,
"new_achievements": false,
"my_level_data": {
"level": 1,
"points": 1,
"progress": 33,
"goal_points": 3,
"level_up": false
}
}
Let op: de categorie, vraag, antwoordopties en correcte antwoordsleutels zijn allemaal opgenomen in het antwoord. Dit betekent dat het eenvoudig is om het antwoord te identificeren wanneer er in de app wordt gevraagd om het spel te bedriegen. Hoewel het niet bepaald ethisch of eerlijk is voor gaminggebruik, dacht ik dat het interessant onderzoek zou zijn.
Mijn oorspronkelijke plan was om de Android-app te reverse-engineeren en de gebruiker te voorzien van een Geroosterd brood mededeling van het antwoord. Ik begon door het decompileren van de app en het beoordelen van de broncode. Ik gebruikte grep om in de bron te zoeken naar enkele trefwoorden waarvan ik hoopte dat ze me zouden helpen de vraag/antwoord-activiteit op te sporen. Terwijl ik door enkele van de mogelijke resultaten zocht, trokken een paar regels mijn aandacht.
v.setText(p);String s1 = "";
if (com.etermax.tools.f.a.a() && h.a("ANSWERS_CHEAT", true))
{
s1 = (new StringBuilder()).append(" (").append(r.getCorrectAnswer()).append(")").toString();
}
B.setText((new StringBuilder()).append(r.getText()).append(s1).toString());
A.setContentDescription(r.getText());
a(B);
u.setVisibility(0);
C.startAnimation(com.etermax.preguntados.ui.a.c.b());
x.setImageResource(com.etermax.preguntados.ui.game.duelmode.h.a(m).a(g, r.getCategory()));
LayoutInflater layoutinflater;
List list;
if (l != null && l == GameType.DUEL_GAME)
{
y.setVisibility(0);
y.setText(c(c.x().h()));
} else
{
y.setVisibility(8);
}
H.setEnabled(false);
layoutinflater = getLayoutInflater(getArguments());
d.a(e.d);
list = r.getAnswers();
Na de code zinspeelde "ANSWERS_CHEAT" op een verborgen cheat-modus in het spel. In plaats van het wiel opnieuw uit te vinden, besloot ik uit te zoeken hoe het werkte. Met behulp van grep heb ik alle verwijzingen naar de "ANSWERS_CHEAT" gevonden snaar en snel ontdekt A verwijzing naar een verborgen menu op de hoofddashboardactiviteit.
public boolean onOptionsItemSelected(MenuItem menuitem){
if (com.etermax.tools.f.a.a() && menuitem.getItemId() == com.etermax.i.cheat)
{
if (j.a("ANSWERS_CHEAT", true))
{
j.b("ANSWERS_CHEAT", false);
menuitem.setTitle("Enable Answer Cheat");
return true;
} else
{
j.b("ANSWERS_CHEAT", true);
menuitem.setTitle("Disable Answer Cheat");
return true;
}
} else
{
return super.onOptionsItemSelected(menuitem);
}
}
Deze code leek het instellen van de cheatmodusoptie te regelen, maar ik had nog steeds geen toegang tot het menu zelf. Binnen dezelfde activiteit heb ik de OnCreateOptionsMenu-methode hieronder beoordeeld:
public boolean onCreateOptionsMenu(Menu menu){
if (com.etermax.tools.f.a.a())
{
getMenuInflater().inflate(com.etermax.l.preguntados_debug_menu, menu);
return true;
} else
{
return super.onCreateOptionsMenu(menu);
}
}
Het grootste deel van de functionaliteit van de cheatmodus, inclusief het verborgen menu, leek afhankelijk te zijn van de geretourneerde waarde van com.etermax.tools.f.a.a(). De code voor die klasse staat hieronder:
public class a.{
private static boolean a;
private static String b;
public static void a(ApplicationInfo applicationinfo)
{
a = false;
}
public static void a(String s)
{
b = s;
}
public static boolean a()
{
return a;
}
public static String b()
{
return b;
}
public static boolean c()
{
return b != null;
}
}
Dit leek het beslissingspunt te zijn waarnaar ik op zoek was. Het wijzigen van de opdracht a = onwaar; naar WAAR had het verborgen menu moeten inschakelen. Ik opende de kleine representatie van de klas en vond de opdracht van het Booleaanse lid.
# direct methods..method public static a(Landroid/content/pm/ApplicationInfo;)V
.locals 1
.prologue
.line 29
const/4 v0, 0x0
sput-boolean v0, Lcom/etermax/tools/f/a;->a: Z
.line 30
return-void
.end method
Ik heb regel 29 (fragmentregel #7 hierboven) gewijzigd in const/4 v0, 1, waarmee de waarde op true wordt ingesteld. Vervolgens heb ik de app opnieuw gecompileerd en geïnstalleerd. De menuknop heeft vervolgens met succes de onderstaande verborgen opties zichtbaar gemaakt:
"Answer Cheat" leek nu standaard ingeschakeld, dus startte ik een nieuw spel om te testen. Zoals verwacht hebben de spellen nu een nummer achter de vragen toegevoegd, dat de op nul gebaseerde index van het juiste antwoord aangeeft.
Download de gepatchte APK hier. Let op: dit is alleen voor onderzoeksdoeleinden; Ik ben niet verantwoordelijk voor immoreel spel!
BEWERKING:APK-spiegel
Dit zou als goed voorbeeld moeten dienen dat de privacy van clientapplicaties niet kan worden gegarandeerd en dat ontwikkelaars voorzichtig moeten zijn met wat er in hun gecompileerde releases staat.