逆向破解Android版貪吃蛇

逆向就像破案,不斷找線索,等最后破案了才發(fā)現(xiàn)原來如此簡單。
先說下破解目標(biāo),在無盡模式下讓蛇不死。
首先神器apktool反編譯,順利得到混淆后的代碼和資源。


1.png

可以看到進(jìn)行dex分包了。
裝APK跑一下,蛇碰撞死后,出現(xiàn)游戲結(jié)束對話框。在這步我們?nèi)〉脽o盡模式的Activity是com.wepie.snake.app.activity.GameActivity,layout是activity_main.xml。布局如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent">
    <com.wepie.snake.module.game.GameViewContainer android:layout_width="match_parent" android:layout_height="match_parent">
        <com.wepie.snake.lib.plugin.z android:id="@+id/main_snake_surface_view" android:layout_width="match_parent" android:layout_height="match_parent"/>
        <com.wepie.snake.module.game.SpeedUpView android:id="@+id/main_speedup_bt" android:paddingLeft="80dp" android:paddingTop="80dp" android:paddingRight="40dp" android:paddingBottom="40dp" android:layout_width="200dp" android:layout_height="200dp" android:src="@drawable/shape_ffffff_corners_bl4_br4" android:scaleType="centerInside" android:layout_alignParentRight="true" android:layout_alignParentBottom="true"/>
    </com.wepie.snake.module.game.GameViewContainer>
    <com.wepie.snake.module.game.g android:id="@+id/main_snake_game_info_view" android:layout_width="match_parent" android:layout_height="match_parent"/>
    <com.wepie.snake.module.game.RouletteView android:id="@+id/main_roulette_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentBottom="true"/>
</RelativeLayout>

有興趣的可以研究下這些View,這里只談破解分析結(jié)果。按正常邏輯,如果我們寫代碼,就是蛇死的時(shí)候,在布局里彈框。破解就是要找到蛇死的時(shí)機(jī),然后hack掉。
對布局文件分析后,得知com.wepie.snake.lib.plugin.z,這個(gè)類才是游戲真正的核心,很有迷惑性。
z的定義如下:

public class z extends GLSurfaceView {
class c implements Renderer {
public void onDrawFrame(GL10 gl10) {
            GLES20.glClear(16640);
            if (this.d.q && this.d.t.b.a) {
                this.d.t.d();
            }
            if (this.a) {
                this.d.q = false;
                this.a = false;
                b();
            }
            this.d.m.a();
            this.d.l.a();
            this.d.t.a(this.d.k);
            z zVar = this.d;
            zVar.f += this.d.t.c.c * ((float) this.d.t.b.d);
            zVar = this.d;
            zVar.g += this.d.t.c.d * ((float) this.d.t.b.d);
            com.wepie.snake.module.game.i.b.a(this.d.f * e.k, this.d.g * e.k, 20.0f, this.d.f * e.k, this.d.g * e.k, 0.0f, 0.0f, 1.0f, 0.0f);
            this.d.e.c();
            this.d.j.b();
            this.d.k.c();
            this.d.l.c();
            this.d.w.a(this.d.v);
            this.d.u.b(this.d.t);
            this.d.u.a(this.d.m, this.d.k);
            this.d.t.a();
            this.d.j.a(this.d.m);
            this.d.k.a();
            this.d.l.a(this.d.m);
            this.d.t.d.a(this.d.m);
            this.d.d.a(this.d.t);
            this.d.u.a(this.d.d);
            this.b++;
            if (this.b >= 60) {
                this.b = 0;
                this.d.p.a(this.d.v);
            }
        }
}
}

代碼被混淆過,考驗(yàn)?zāi)托?,?xì)心和想象力的時(shí)候到了。
熟悉GLSurfaceView的都知道,游戲循環(huán)邏輯在Renderer的onDrawFrame,猜想就是在這里面每次調(diào)用時(shí)檢測蛇死了沒。
這里先說下一個(gè)誤導(dǎo),之前說蛇死了,彈框游戲結(jié)束,先找到彈框控件

public class g extends FrameLayout {
    public GameRankView a;
    private Context b;
    private TextView c;
    private TextView d;
    private TextView e;
    private c f;
    private GameOverView g;
    private int h;
    private int i;
    private int j;
     public void a(boolean z, OffGameScoreInfo offGameScoreInfo) {
        b.a();
        this.g.setVisibility(0);//View.visible
        this.g.a(z, this.i, this.j, this.h);
        this.g.a(offGameScoreInfo);
        this.f.a();
    }

}

控件有a這個(gè)方法,里面正好有setVisibility(0),蛇死,調(diào)用這個(gè)a方法,看起來邏輯正確。于是去z (GLSurfaceView)里找哪里調(diào)用了g的a方法。找到下面幾個(gè)地方

this.d.u.a(this.d.m, this.d.k);//AiManager 
this.d.j.a(this.d.m);//food
this.d.l.a(this.d.m);//KillFactory
this.d.t.d.a(this.d.m)//SnakeInfo

上面的m就是g類。研究后發(fā)現(xiàn)四個(gè)方法沒有想要的邏輯,四個(gè)方法作用后面注釋了。
仔細(xì)研究onDrawFrame里的方法,每次循環(huán)都在做什么?發(fā)現(xiàn)了這個(gè)方法

this.d.d.a(this.d.t);

第一個(gè)d是z,第二個(gè)d是com.wepie.snake.module.game.h.d,

public class d {
 public void a(m mVar) {
        if (mVar.b.a) {
            this.k = mVar;
            i a = mVar.d.a();
            this.i = a.a;
            this.j = a.b;
            if (a()) {//重點(diǎn)
                this.l = mVar.d.d + this.g;
                int a2 = g.a(this.i, this.j);
                this.t = 10000.0f;
                this.u = 10000.0f;
                e(a2);
                a(a2);
                b(a2);
                if (a2 % 16 != 0) {
                    c(a2);
                }
                if ((a2 + 1) % 16 != 0) {
                    d(a2);
                }
                b();
            }
        }
    }

  private boolean a() {
        float f = this.k.d.d * 0.6f;
        if (this.i >= a + f && this.i <= c - f && this.j <= b - f && this.j >= f + d) {
            return true;
        }
        a(this.k, null);
        return false;
    }

}

這里做碰撞檢測,如果a()為true,執(zhí)行下面的代碼,false呢?game over!
好,找到這個(gè)位置,然后怎么修改?找到反編譯后的smali代碼,

.line 61
invoke-direct {p0}, Lcom/wepie/snake/module/game/h/d;->a()Z
move-result v0

我們想要的效果是是if(true),這樣碰撞檢測始終返回true,就不會死了,所以把smali代碼這段判斷直接刪除。

最后,回編譯打包,跑跑看。


2.png

蛇撞墻后果然不會死了,但是蛇撞蛇還是會死,推測蛇撞蛇用了另外的碰撞檢測。
時(shí)間關(guān)系就不找了。

當(dāng)然最后只是刪了兩行代碼而已,看起來很簡單,過程其實(shí)沒有那么容易,比如最后回編譯打包,也許你會碰到問題,打不了包,仔細(xì)分析去解決吧。這里只是給個(gè)思路,僅供學(xué)習(xí)參考,勿做他用。
這次分析純粹是分析代碼找重點(diǎn),還有動態(tài)調(diào)試破解的方法,以后再來。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容