Android逆向工程-破解 哈皮妹-蘿莉
前言
新的一年新的開始,除了繼續我的原有課題之外,我還打算研究下Android逆向工程的一些東西,主要包括反編譯、Smali、APK打包、簽名、反逆向和移動安全等。這篇就是新課題的第一篇文章,不過要犧牲下哈皮妹-蘿莉這款應用了。通過對哈皮妹-蘿莉的破解,讓我更加深刻直觀地認識到,Android的安全性是一個多麼大的問題,如果我們的應用沒有采用特殊手段去阻止破解,那麼一個個的山寨版就會出來,一個個原本收費的應用被修改成不用錢也能玩,做為開發者,辛苦的勞動成果得不到應有的回報,是不是覺得特別受打擊呢?所以,反逆向是廣大開發者所需要關注的。在Android逆向工程中的一系列文章中,我將會介紹Android逆向和反逆向,請大家期待。
為什麼選擇破解哈皮妹-蘿莉
這是一個意外,是前幾天無意中發現了這款應用,想想就裝上看看吧,結果發現一些功能需要充錢才能用,所以,就萌生了破解它的想法,破解進行的很順利。另外,我需要說明的是,我反對通過非法手段破解他人應用,因為這是不道德且有違行業規則的,那麼為什麼我還要寫文章來介紹破解的方法呢?因為,儘管破解他人應用是不好的,但是破解這種行為已經廣泛地流傳開了,即使我不破解,也有其他人破解。所以,我要做的是,介紹破解的流程和危害從而引起廣大開發者的注意,如果大家在開發應用的時候都能注意到防破解的問題,那麼破解就不會那麼猖狂了。其實破解是很難防的,但是通過一些特定方法可以阻止一些簡單常規的破解手段,這樣就能減小應用被破解的概率。本著遵守行業規範,我只會給出破解思想,但是不會給出破解後的apk,希望大家理解。
破解準備工作
由於我在linux下工作,所以本文介紹的破解工具以及命令都是linux下的,但是windows以及mac上的工具、命令是和linux類似的,大家可以自己瞭解下。
準備破解所需工具
原始碼反編譯工具:dex2jar、jd-gui
apk反編譯為smali、打包、簽名工具:apktool、signapk.jar以及簽名所需的key檔案
其中dex2jar將dex檔案轉成jar包,jd-gui完成jar包到java檔案的轉換,這樣就可以看到原始碼了,但是原始碼往往是混淆過的,看起來很像亂碼;apktool完成apk的解包、打包,signapk.jar採用key檔案對打包後的apk進行簽名,ok,簽名後就可以直接安裝了,到此為止,一個山寨應用就誕生了。需要注意的是,apk打包以後必須要進行簽名,否則apk是無法安裝的,不信大家可以試試,但是由於是採用你的key檔案進行簽名的,所以簽名後的apk和官方版的簽名是不一樣的,也就是說,我們生成的apk是山寨版,不過山寨版也是可以用的。
破解命令
反編譯命令:
第一步 ./dex2jar.sh classes.dex
第二步 採用jd-gui開啟jar包檢視原始碼
說明:上面第一步中的classes.dex來自原始apk中,將原始apk解壓,copy出裡面的classes.dex即可
解包、打包/簽名命令:
第一步 ./apktool d -f com.qxshikong.mm.lolita.apk com.lolita
第二步 修改smali
第三步 apk打包 ./apktool b com.lolita com.lolita.apk
第四步 apk簽名 java -jar signapk.jar testkey.x509.pem testkey.pk8 com.lolita.apk com.lolita.signed.apk
ok,com.lolita.signed.apk就是我們最終的apk,安裝試試吧。
說明:上述第一步中的com.qxshikong.mm.lolita.apk 、com.lolita分別為原始apk的檔名和解包後的目錄名;第三步是將解壓後的目錄重新打包成apk。
破解流程
上面已經介紹了破解所需的工具以及命令,但是還差最後一步,就是修改smali這一步,這一步是破解的關鍵所在,對任何apk的破解,其核心工作均在這一步。首先描述下哈皮妹-蘿莉的玩法,然後再描述破解思想,最後給出破解後的smali程式碼。
哈皮妹-蘿莉玩法
就是一個漂亮妹子,可以和你互動,其實就是播放不同的mp4而已。普通的互動可以隨便玩,但是深層次的互動,比如跳舞、換裝等,需要你有一定數量的金幣和鑽石,金幣可以通過遊戲賺取,鑽石必須充值,但是想看妹子跳舞啥的,不充值只靠遊戲賺是很難的,幾乎不可能啊。
破解思想
如果能修改遊戲中的金幣數和鑽石數,問題不就搞定了麼?是的,事實證明,的確是這樣。也許有人會說,直接找記憶體修改工具(比如八門神器)把錢給改了不就行了麼,注意,記憶體修改工具改錢需要其有root許可權,沒有root許可權的手機是搞不定的,另外,修改記憶體不屬於逆向工程,它屬於外掛。按照我們這種思想,程式中應該會有一個地方專門儲存當前的金幣和鑽石的數量,很可能是一個物件,因為,整個遊戲都會直接對金錢進行存取,所以,我覺得開發者應該會設定一個類,並全域性持有這個類的物件。我也試圖通過log來得到有用資訊,但是沒發現啥有價值的log。
破解方法
我開啟jd-gui,檢視反編譯的程式碼,程式碼被混淆了,雖然很難,但是還是被我找到了關鍵的地方,我發現瞭如下的一個類,通過對混淆程式碼邏輯的分析,我大致覺得這個類就是代表金幣和鑽石的類,事實證明我是對的。
package com.qxshikong.mm.lolita.c;
public final class c
{
private int a;
private int b;
public final int a()
{
return this.a; //我們在這裡返回 999999999
}
public final void a(int paramInt)
{
this.a = paramInt;
}
public final int b()
{
return this.b; //我們在這裡返回 999999999
}
public final void b(int paramInt)
{
this.b = paramInt;
}
}
有了這個類,就很簡單了,修改方法看上面程式碼的註釋,直接在兩個get方法中強制返回999999999,這下金幣和鑽石都夠花了吧。下面對其smali進行修改,我們是無法直接修改其反編譯後的原始碼的,只能修改smali,關於smali的詳細語法,以後我會寫文章專門介紹,這裡就先有個大致感覺就行了。修改前的smali:
.class public final Lcom/qxshikong/mm/lolita/c/c;
.super Ljava/lang/Object;
# instance fields
.field private a:I
.field private b:I
# direct methods
.method public constructor <init>()V
.locals 0
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
# virtual methods
.method public final a()I
.locals 1
iget v0, p0, Lcom/qxshikong/mm/lolita/c/c;->a:I
return v0 #修改這裡的返回值為999999999
.end method
.method public final a(I)V
.locals 0
iput p1, p0, Lcom/qxshikong/mm/lolita/c/c;->a:I
return-void
.end method
.method public final b()I
.locals 1
iget v0, p0, Lcom/qxshikong/mm/lolita/c/c;->b:I
return v0 #修改這裡的返回值為999999999
.end method
.method public final b(I)V
.locals 0
iput p1, p0, Lcom/qxshikong/mm/lolita/c/c;->b:I
return-void
.end method
修改後的smali:.class public final Lcom/qxshikong/mm/lolita/c/c;
.super Ljava/lang/Object;
# instance fields
.field private a:I
.field private b:I
# direct methods
.method public constructor <init>()V
.locals 0
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
# virtual methods
.method public final a()I
.locals 1
const v0, 0x3b9ac9ff #這裡定義一個常量 v0 = 999999999
return v0 #返回v0
.end method
.method public final a(I)V
.locals 0
iput p1, p0, Lcom/qxshikong/mm/lolita/c/c;->a:I
return-void
.end method
.method public final b()I
.locals 1
const v0, 0x3b9ac9ff #這裡定義一個常量 v0 = 999999999
return v0 #返回v0
.end method
.method public final b(I)V
.locals 0
iput p1, p0, Lcom/qxshikong/mm/lolita/c/c;->b:I
return-void
.end method