Trivia Crack は、Web プラットフォームとモバイル プラットフォームの両方で非常に人気のあるゲームで、Trivial Pursuit をある程度モデル化しています。 これはソーシャル ゲームの最新の流行であり、ユーザーはさまざまなカテゴリからの質問に答えて友人や見知らぬ人と競うことができます。 私はゲームにはあまり興味がありませんでしたが、妻は最近 Trivia Crack の大ファンになりました。 彼女のプレイをしばらく観察した後、ダウンロードして実装方法を詳しく調べてみることにしました。
まず、Android アプリの使用中にネットワーク経由で行われる Web API リクエストを監視しました。 ゲームの操作中にすぐに、興味深いことに気づきました。 ユーザーが「カテゴリ」ホイールを回し始める前に、アプリはトリビア クラック サーバーからカテゴリ、質問、回答を受信していたようです。
以下は、この画面を表示する前にアプリが取得する応答の例です。
{"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
}
}
カテゴリ、質問、回答オプション、および正解キーがすべて応答に含まれていることに注意してください。 これは、アプリ内でゲームをチートするように求められた場合、答えを簡単に特定できることを意味します。 ゲーム用途としては厳密には倫理的でも公正でもありませんが、興味深い研究になると思いました。
私の当初の計画は、Android アプリをリバース エンジニアリングしてユーザーに提供することでした。 トースト 回答の通知。 私が始めたのは アプリを逆コンパイルする そしてソースコードをレビューします。 grep を使用して、質問と回答のアクティビティを追跡するのに役立ついくつかのキーワードをソース内で検索しました。 いくつかの潜在的な結果を検索しているときに、いくつかの行が私の注意を引きました。
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();
コードに続く「ANSWERS_CHEAT」は、ゲーム内の隠れたチート モードを示唆しています。 私は車輪を再発明するのではなく、それがどのように機能するかを調べることにしました。 grep を使用して、「ANSWERS_CHEAT」へのすべての参照を見つけました。 弦 そしてすぐに 発見した ある メイン ダッシュボード アクティビティの非表示メニューへの参照。
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);
}
}
このコードはチート モード オプションの設定を処理しているように見えましたが、それでもメニュー自体にはアクセスできませんでした。 同じアクティビティ内で、以下の OnCreateOptionsMenu メソッドを確認しました。
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);
}
}
隠しメニューを含むチート モードの機能のほとんどは、次の戻り値に依存しているように見えました。 com.etermax.tools.f.a.a(). そのクラスのコードは以下のとおりです。
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;
}
}
これが私が探していた決定点のようでした。 割り当ての変更 a = 偽; に 真実 隠しメニューを有効にするべきでした。 クラスの smari 表現を開いて、ブール型メンバーの割り当てを見つけました。
# 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
29 行目 (上記の抜粋行 #7) を次のように変更しました。 const/4 v0、1、値を true に設定します。 次に、アプリを再コンパイルしてインストールしました。 メニュー ボタンは、以下の隠しオプションを正常に公開しました。
「Answer Cheat」がデフォルトで有効になっているようだったので、テストするために新しいゲームを起動しました。 予想通り、ゲームでは質問の後に数字が追加され、正解の 0 から始まるインデックスが示されました。
パッチを当てたAPKをダウンロードする ここ. これは研究目的のみであることに注意してください。 私はいかなる不道徳なゲームプレイについても責任を負いません。
編集:APKミラー
これは、クライアント アプリケーションのプライバシーは保証されず、開発者はコンパイルされたリリースに何が含まれているかに注意する必要があることを示す良い例となるはずです。