Como eu quebrei o Trivia Crack

Trivia Crack é um jogo altamente popular para plataformas web e móveis, inspirado no Trivial Pursuit. É a última moda em jogos sociais, permitindo aos usuários competir contra amigos e estranhos respondendo perguntas de diversas categorias. Embora eu nunca tenha me interessado muito por jogos, minha esposa recentemente se tornou uma grande fã do Trivia Crack. Depois de observá-la tocar por um tempo, decidi fazer o download e ver mais de perto como foi implementado.

Comecei monitorando as solicitações de API da web feitas pela rede durante o uso do aplicativo Android. Rapidamente percebi algo interessante durante o funcionamento do jogo. Parecia que o aplicativo estava recebendo a categoria, a pergunta e a resposta dos servidores Trivia Crack antes mesmo de o usuário começar a girar a roda da “categoria”.

Abaixo está um exemplo de resposta que o aplicativo busca antes de mostrar esta tela:

{ 

"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

}

}

Observe que a categoria, a pergunta, as opções de resposta e as chaves de resposta corretas estão incluídas na resposta. Isso significa que seria fácil identificar a resposta quando solicitado no aplicativo para trapacear no jogo. Embora não seja exatamente ético ou justo para uso em jogos, achei que seria uma pesquisa interessante.

Meu plano inicial era fazer engenharia reversa no aplicativo Android e fornecer ao usuário uma Brinde notificação da resposta. Eu comecei por descompilando o aplicativo e revisando o código-fonte. Usei grep para pesquisar na fonte algumas palavras-chave que esperava que me ajudassem a rastrear a atividade de perguntas/respostas. Ao pesquisar alguns dos resultados potenciais, algumas linhas chamaram minha atenção.

 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();

Seguindo o código, “ANSWERS_CHEAT” aludiu a um modo de cheat oculto no jogo. Em vez de reinventar a roda, decidi descobrir como funcionava. Usando grep, encontrei todas as referências ao "ANSWERS_CHEAT" corda e rapidamente descoberto referência a um menu oculto na atividade principal do painel.

 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);

}

}

Este código parecia lidar com a configuração da opção do modo cheat, mas ainda não consegui acessar o menu em si. Dentro da mesma atividade, revisei o método OnCreateOptionsMenu abaixo:

 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);

}

}

A maior parte da funcionalidade do modo cheat, incluindo o menu oculto, parecia depender do valor retornado de com.etermax.tools.f.a.a(). O código dessa classe está abaixo:

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;

}

}

Este parecia ser o ponto de decisão que eu procurava. Alterando a atribuição a = falso; para verdadeiro deveria ter ativado o menu oculto. Abri a pequena representação da classe e encontrei a atribuição do membro booleano.

# 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

Alterei a linha 29 (linha de trecho 7 acima) para const/4 v0, 1, que define o valor como verdadeiro. Em seguida, recompilei o aplicativo e instalei-o. O botão de menu expôs com sucesso as opções ocultas abaixo:

"Answer Cheat" agora parecia habilitado por padrão, então comecei um novo jogo para testar. Como esperado, os jogos agora acrescentavam um número após as questões, indicando o índice baseado em zero da resposta correta.

Baixe o APK corrigido aqui. Observe que isso é apenas para fins de pesquisa; Não sou responsável por qualquer jogo imoral!

EDITAR:Espelho APK

Isso deve servir como um bom exemplo de que a privacidade do aplicativo cliente não pode ser garantida e os desenvolvedores devem ter cuidado com o que está incluído em suas versões compiladas.