Android逆向之路—為什麼從後臺切換回app又顯示廣告了

MartinHan發表於2019-03-01

問題

最近發現自己的android機在開網易新聞,知乎等app的時候,明明自己沒有殺程式,從後臺返回回來的時候還是會再次顯示廣告,而且又正巧在知乎上看到了有人在提問,於是逆向分析。**就用知乎作為例子吧。**我用的5.4.1版本

先說結果:

不是因為殺程式、殺後臺的原因造成的,這是知乎的業務邏輯。

知乎就是為了顯示更多次數的廣告,故意設計成這個樣子的。

你啟動知乎的時候如果沒顯示廣告,那麼等你切到後臺再回來就顯示一次。

你啟動知乎的時候如果顯示廣告了,那麼你切到後臺了不殺死知乎,那麼他就不顯示。

開始逆向

討厭的廣告介面如下

Android逆向之路—為什麼從後臺切換回app又顯示廣告了

準備工作,利用apktool拆包,然後開始逆向分析。
首先通過搜尋字串方法,搜尋“發現更大的直接”然後找到了下面的字串

<string name="title_find_bigger_world">發現更大的世界</string>
複製程式碼

然後根據這個name的值發現了這個廣告介面佈局檔案是”fragment_launch_ad.xml”
繼續找,
根據id然後對應”public.xml”等方法,
可以最終找到其實這個頁面就是”LaunchAdFragment.java”
首先我們找到討厭的廣告頁面了

換一種思路開始尋找Activity

我從後臺切回來的時候肯定是當前Activity在作祟,他執行了一些程式才喚起的”LaunchAdFragment”

按照這個思路我就像看看知乎嘴上層的activity叫什麼名字,連上adb執行如下命令

adb shell dumpsys activity
複製程式碼

然後發現了,原來這個activity的名字叫”MainActivity”啊
activity棧如下

Running activities (most recent first):
      TaskRecord{b4d2961 #339 A=com.zhihu.android U=0 StackId=1 sz=1}
        Run #4: ActivityRecord{bc733c u0 com.zhihu.android/.app.ui.activity.MainActivity t339}
      TaskRecord{35c713e #340 A=com.android.gallery3d U=0 StackId=1 sz=1}
        Run #3: ActivityRecord{cd14b9f u0 com.android.gallery3d/com.huawei.gallery.app.GalleryMain t340}
      TaskRecord{1fb8a4b #333 A=com.android.settings U=0 StackId=1 sz=1}
        Run #2: ActivityRecord{d3eec28 u0 com.android.settings/.HWSettings t333}
      TaskRecord{69570b7 #335 A=com.tencent.mm U=0 StackId=1 sz=1}
        Run #1: ActivityRecord{a82f024 u0 com.tencent.mm/.ui.LauncherUI t335}
      TaskRecord{e43bab9 #295 I=com.android.settings/.deviceinfo.UsbConnModeChooserActivity U=0 StackId=1 sz=1}
        Run #0: ActivityRecord{9d10efe u0 com.android.settings/.deviceinfo.UsbConnModeChooserActivity t295}

複製程式碼

綜上所述,這個MainActivity和LaunchAdFragment肯定有關係,我就在MainActivity裡面,然後就在MainActivity裡面搜尋LaunchAdFragment,

發現了居然在onResume裡面呼叫了LaunchAdFragment
根據直覺來說,我覺得應該問題就在這裡了。我找到了知乎居然有下面這些邏輯。(看不懂沒關係,往下讀,我已經手動的轉成了Java)

.line 533
    :cond_3
    invoke-static {}, Lcom/zhihu/android/app/util/LaunchAdHelper;->getInstance()Lcom/zhihu/android/app/util/LaunchAdHelper;

    move-result-object v5

    invoke-virtual {v5}, Lcom/zhihu/android/app/util/LaunchAdHelper;->isShowLaunchAd()Z

    move-result v5

    if-eqz v5, :cond_4

    .line 535
    const-string v5, "input_method"

    invoke-virtual {p0, v5}, Lcom/zhihu/android/app/ui/activity/MainActivity;->getSystemService(Ljava/lang/String;)Ljava/lang/Object;

    move-result-object v0

    check-cast v0, Landroid/view/inputmethod/InputMethodManager;

    .line 536
    .local v0, "imm":Landroid/view/inputmethod/InputMethodManager;
    invoke-virtual {v0}, Landroid/view/inputmethod/InputMethodManager;->isActive()Z

    move-result v5

    if-nez v5, :cond_4

    .line 538
    invoke-static {}, Lcom/zhihu/android/app/ui/fragment/LaunchAdFragment;->buildIntent()Lcom/zhihu/android/app/util/ZHIntent;

    move-result-object v5

    invoke-virtual {p0, v5}, Lcom/zhihu/android/app/ui/activity/MainActivity;->startFragment(Lcom/zhihu/android/app/util/ZHIntent;)V
複製程式碼

對應的Java程式碼

void onResume() {
    // 判斷是否需要載入廣告,
    boolean showAd = LaunchAdHelper.getInstance().isShowLaunchAd();
    if(!showAd) return ;
    //判斷系統鍵盤是否是active的
    InputMethodManager imm = ((InputMethodManager)getSystemService("input_method"));
    if(imm.isActive()) return ;
    //開啟廣告頁面
    Intent intent = LaunchAdFragment.buildIntent();
    startFragment(intent);
}
複製程式碼

具體的邏輯已經在程式碼裡面加了註釋,所以說,到底顯示還是不顯示廣告主要看這個方法了

LaunchAdHelper.getInstance().isShowLaunchAd()
複製程式碼

繼續尋找LaunchAdHelper裡面的控制變數

然後找到這個類,跟進這個方法,具體看原始碼

.method public isShowLaunchAd()Z
    .locals 2

    .prologue
    const/4 v0, 0x0

    .line 105
    iget-boolean v1, p0, Lcom/zhihu/android/app/util/LaunchAdHelper;->mIsAllowShowLaunchAd:Z

    if-eqz v1, :cond_0

    .line 106
    iput-boolean v0, p0, Lcom/zhihu/android/app/util/LaunchAdHelper;->mIsAllowShowLaunchAd:Z

    .line 107
    const/4 v0, 0x1

    .line 110
    :cond_0
    return v0
.end method
複製程式碼

轉換成java程式碼如下

public boolean isShowLaunchAd() {
    if(!mIsAllowShowLaunchAd) {
        return false;
    }
    mIsAllowShowLaunchAd = false;
    return true;
}
複製程式碼

好了,至少到現在大家應該知道了,顯示不顯示廣告主要靠這個LaunchAdHelper裡面的成員變數mIsAllowShowLaunchAd

誰能夠改變控制這個變數,業務邏輯主要就是控制他了。
而且已經出現了業務邏輯就是,如果顯示了一次,那麼這個變數就會為false,然後就不顯示了。(至少現在是這樣)

然後搜尋LaunchAdHelper發現了他在onStart裡面初始了這個變數

.method public onStart(Landroid/content/Context;)V

    //------略------

    iput-boolean v0, p0,    Lcom/zhihu/android/app/util/LaunchAdHelper;->mIsAllowShowLaunchAd:Z

    .line 88
    :cond_0
    const/4 v0, 0x0

    iput-boolean v0, p0, Lcom/zhihu/android/app/util/LaunchAdHelper;->mIsFromInnerActivity:Z

    .line 89
    iget-boolean v0, p0, Lcom/zhihu/android/app/util/LaunchAdHelper;->mIsAllowShowLaunchAd:Z

    iput-boolean v0, p0, Lcom/zhihu/android/app/util/LaunchAdHelper;->mIsLaunchAdShow:Z

    //------略------
.end method
複製程式碼

其實這段程式碼的意思就是初始化了一下這個mIsAllowShowLaunchAd,設定成true,那麼緊接著問題就來了,誰呼叫了LaunchAdHelper的onStart方法呢,

他的父類是Object啊,只是一個普通的類,

繼續全域性搜尋

再次找回到了MainActivity

然後發現繞了一圈我們又繞回來了,在MainActivity裡面呼叫了這個方法,結果如下

.method protected onStart()V
    .locals 1

    .prologue
    .line 460
    invoke-super {p0}, Lcom/zhihu/android/app/ui/activity/BaseFragmentActivity;->onStart()V

    .line 461
    invoke-static {}, Lcom/zhihu/android/app/util/LaunchAdHelper;->getInstance()Lcom/zhihu/android/app/util/LaunchAdHelper;

    move-result-object v0
    
    #下面這兩句就是呼叫了初始化廣告的方法
    invoke-virtual {v0, p0}, Lcom/zhihu/android/app/util/LaunchAdHelper;->onStart(Landroid/content/Context;)V

    .line 462
    return-void
.end method
複製程式碼

好了,現在為止,大概明白了吧。

在MainActivity的onStart方法裡面呼叫了LaunchAdHelper的onStart方法,隨後在這裡面初始化將mIsAllowShowLaunchAd 設定成true的。

然後再onResume裡面再顯示廣告顯示一次了再次onResume就不顯示了
但是如果再次呼叫onStart那麼又初始化了,設定成了顯示廣告,
那麼具體什麼時候會呼叫Activity的onStart呢,
官方文件是這麼說的:

Android逆向之路—為什麼從後臺切換回app又顯示廣告了

具體意思就是顯示的時候就會呼叫onStart。

結果

其實結果已經在文章開始部分說過了,不記得的話往前翻翻吧

寫在結尾

這就是知乎的這個閃屏廣告顯示的邏輯,

有個以為接到了這種業務邏輯的時候程式設計師會不會心裡內心一萬隻羊駝奔騰飛過

還有,他們沒混淆app嗎,混淆過的方法不應該是

a.b.a1(p1,p3);
複製程式碼

這個樣子的嗎。

轉眼一看軟體版本5.4.1,恍然大悟。

關於我

個人部落格:MartinHan的小站

部落格網站:hanhan12312的專欄

知乎:MartinHan01

相關文章