Trivia Crack est un jeu très populaire pour les plates-formes Web et mobiles qui s'inspire quelque peu de Trivial Pursuit. Il s'agit du dernier engouement en matière de jeux sociaux, permettant aux utilisateurs de rivaliser avec leurs amis et des inconnus en répondant à des questions provenant d'un large éventail de catégories. Même si je n'ai jamais été très intéressé par les jeux, ma femme est récemment devenue une grande fan de Trivia Crack. Après l'avoir regardée jouer pendant un moment, j'ai décidé de le télécharger et d'examiner de plus près comment il était implémenté.
J'ai commencé par surveiller les requêtes API Web effectuées sur le réseau lors de l'utilisation de l'application Android. Très vite, j'ai remarqué quelque chose d'intéressant lors du fonctionnement du jeu. Il semblait que l'application recevait la catégorie, la question et la réponse des serveurs Trivia Crack avant même que l'utilisateur ne commence à faire tourner la roue des « catégories ».
Vous trouverez ci-dessous un exemple de réponse que l'application récupère avant d'afficher cet écran :
{"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
}
}
Notez que la catégorie, la question, les options de réponse et les clés de réponse correcte sont toutes incluses dans la réponse. Cela signifie qu'il serait simple d'identifier la réponse lorsqu'on lui demandera dans l'application de tricher dans le jeu. Bien que ce ne soit pas exactement éthique ou équitable pour une utilisation dans les jeux, j’ai pensé que ce serait une recherche intéressante.
Mon plan initial était de procéder à une ingénierie inverse de l'application Android et de fournir à l'utilisateur un Griller notification de la réponse. J'ai commencé par décompiler l'application et revoir le code source. J'ai utilisé grep pour rechercher dans la source certains mots-clés qui, je l'espérais, m'aideraient à retrouver l'activité questions/réponses. En parcourant certains des résultats potentiels, quelques lignes ont attiré mon attention.
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();
Après le code, "ANSWERS_CHEAT" faisait allusion à un mode de triche caché dans le jeu. Plutôt que de réinventer la roue, j'ai décidé de découvrir comment cela fonctionnait. En utilisant grep, j'ai trouvé toutes les références à "ANSWERS_CHEAT" chaîne et rapidement découvert un référence à un menu caché sur l'activité principale du tableau de bord.
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);
}
}
Ce code semblait gérer le réglage de l'option du mode de triche, mais je n'ai toujours pas pu accéder au menu lui-même. Dans la même activité, j'ai examiné la méthode OnCreateOptionsMenu ci-dessous :
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);
}
}
La plupart des fonctionnalités du mode de triche, y compris le menu caché, semblaient dépendre de la valeur renvoyée par com.etermax.tools.f.a.a(). Le code de cette classe est ci-dessous :
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;
}
}
Cela semblait être le point de décision que je recherchais. Modification de l'affectation a = faux ; à vrai j'aurais dû activer le menu caché. J'ai ouvert la petite représentation de la classe et trouvé l'affectation du membre booléen.
# 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
J'ai changé la ligne 29 (ligne d'extrait n°7 ci-dessus) en const/4 v0, 1, qui définit la valeur sur true. J'ai ensuite recompilé l'application et l'ai installée. Le bouton de menu a ensuite exposé avec succès les options cachées ci-dessous :
"Answer Cheat" semblait désormais activé par défaut, j'ai donc démarré un nouveau jeu pour tester. Comme prévu, les jeux ajoutaient désormais un nombre après les questions, indiquant l'index de base zéro de la bonne réponse.
Téléchargez l'APK corrigé ici. Notez que ceci est uniquement à des fins de recherche; Je ne suis responsable d'aucun gameplay immoral !
MODIFIER:Miroir APK
Cela devrait servir de bon exemple du fait que la confidentialité des applications clientes ne peut pas être garantie et que les développeurs doivent faire attention à ce qui est inclus dans leurs versions compilées.