How I Cracked Trivia Crack

How I Cracked Trivia Crack

We may earn a commission for purchases made using our links.

Trivia Crack is a highly popular game for both web and mobile platforms which is somewhat modeled after Trivial Pursuit. It’s the latest craze in social gaming, allowing users to compete against their friends and strangers in answering questions from an array of categories. Though I’ve never been very interested in gaming, my wife has recently become a huge fan of Trivia Crack. After watching her play for a while, I decided to download it and take a closer look into how it was implemented.

I began by monitoring the web API requests made over the network while using the Android app. Very quickly, I noticed something interesting during the game’s operation. It seemed that the app was receiving the category, question, and answer from the Trivia Crack servers before the user even began spinning the “category” wheel.

Below is an example response that the app fetches prior to showing this screen:

{
    "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
    }
}

Note the category, question, answer options, and correct answer keys are all included in the response. This means it would be straightforward to identify the answer when asked within the app to cheat the game. While not exactly ethical or fair for gaming use, I thought it would be interesting research.

My initial plan was to reverse engineer the Android app and provide the user with a Toast notification of the answer. I started by decompiling the app and reviewing the source code. I used grep to search the source for some keywords that I hoped would help me track down the questions/answers activity. While searching through some of the potential results, a few lines caught my 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();

Following the code, “ANSWERS_CHEAT” alluded to a hidden cheat mode in the game. Rather than reinvent the wheel, I decided on finding out how it worked. Using grep, I found all references to the “ANSWERS_CHEAT” string and quickly discovered reference to a hidden menu on the main dashboard activity.

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

This code appeared to handle setting the cheat mode option, but I still wasn’t able to access the menu itself. Within the same activity, I reviewed the OnCreateOptionsMenu method below:

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

Most of the cheat mode functionality, including the hidden menu, looked like it depended on the returned value of com.etermax.tools.f.a.a() . The code for that class is below:

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

This seemed to be the decision point that I was looking for. Changing the assignment a = false;  to true  should’ve enabled the hidden menu. I opened the smali representation of the class and found the assignment of the boolean member.

# 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

I changed line 29 (snippet line #7 above) to const/4 v0, 1 , which set the value to true. I then recompiled the app and installed it. The menu button then successfully exposed the hidden options below:

“Answer Cheat” now seemed enabled by default, so I started up a new game to test. As expected, the games now appended a number after the questions, indicating the zero-based index of the correct answer.

Download the patched APK here. Note this is for research purposes only; I am not responsible for any immoral gameplay!

EDIT: APK Mirror

This should serve as a good example that client application privacy cannot be guaranteed and developers should be careful about what’s included in their compiled releases.