VirtualApp實戰之拿到女神朋友圈封面

幕後眼光發表於2019-03-03

前言

之前講到得 VirtualAppRoot 可能大家還沒實際體會到它的好處,這篇文章教大家如何拿到女神微信「朋友圈」的封面(邪惡臉)

朋友圈封面

實戰過程

微信好友的頭像是能夠直接儲存到相簿的,但是朋友圈的封面,卻沒有提供儲存的入口,下面我們來一步步找到朋友圈封面的地址。

說明一下:這裡是針對「微信 v6.6.7」 的原始碼分析,不同版本的程式碼因為混淆之後的緣故,不一定適用。如果想下載微信的歷史版本安裝包,可以下載 「PP 助手」,就能找到微信的歷史版本。

1、TopActivity 分析

定位到朋友圈當前的 Activitycom.tencent.mm.plugin.sns.ui.SnsUserUI

TopActivity分析

2、Jadx 反編譯原始碼

用 Jadx 將反編譯的原始碼,另存為 Gradle 專案,這樣可以直接在 Android Studio 看原始碼了,不過這樣匯出來的,是不能夠執行的。我個人習慣在 Android Studio 檢視原始碼,因為可以方便地使用查詢、跳轉和類結構等功能。

另存為Gradle專案

  • 看看 SnsUserUI 原始碼,發現並沒有設定封面相關的地方,不過裡面有個類似乎把很多操作隱藏起來,並且傳入幾個關鍵欄位資訊進去,值得懷疑:

SnsUserUI

  • 點進去看 bb 類,在它的 onCreate() 方法裡面,找到了封面的封裝類 SnsHeader

SnsHeader

  • 繼續看 SnsHeader 的原始碼,找到封面設定的 ImageView ,應該就是圖片紅框的控制元件,為啥這麼確定?因為點選封面會彈出更換封面的彈窗,裡面的 log 提示的也很明顯

change backGround

  • 跟蹤變數 this.nWh.nWt 是在哪裡設定值,找到以下程式碼,可以看到給封面設定 bitmap 值以及預設封面的資源名稱,其中 bitmap 對應的變數名稱是 a

設定bitmap

  • 找到這個 a 賦值的地方,這方法有個可疑引數:accSnsPath ,跟一下

a

  • 可以看到 accSnsPath 有兩處賦值的地方,猜測封面圖片的處理應該是用了「三級快取」機制的,先讀記憶體,記憶體沒有,就讀本地,本地沒有,再網路請求獲取。所以對應的第一個賦值的地方是:本地儲存路徑;第二個賦值的地方是:網路的 url

accSnsPath

accSnsPath2

3、Hook

根據上面的分析過程,對以下 3 個類,進行 hook ,這裡有個 「小技巧」,是「尼古拉斯·趙四」分享的,如果在進行程式碼分析過程沒有頭緒的時候,可以對 Log 類進行 hook,在列印出來的日誌,找尋蛛絲馬跡。

再次強調,hook 針對微信 v6.6.7 原始碼,採用 YAHFA hook

public class HookWxG {
    public static String className = "com.tencent.mm.plugin.sns.model.g";
    public static String methodName = "a";
    public static String methodSig = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLcom/tencent/mm/storage/av;)Landroid/graphics/Bitmap;";

    public static Bitmap hook(String str, String str2, String str3, boolean z, Object avVar) {
        //圖片 url 
        Log.w("@@@", "url:" + str2);
        return backup();
    }

    public static Bitmap backup() {
        return null;
    }
}
複製程式碼
public class HookWxAf {
    public static String className = "com.tencent.mm.plugin.sns.model.af";
    public static String methodName = "getAccSnsPath";
    public static String methodSig = "()Ljava/lang/String;";

    public static String hook() {
        String result = backup();
        //圖片儲存路徑(不含id)
        Log.w("@@@", "path:"+result);
        return result;
    }

    public static String backup() {
        return"";
    }
}
複製程式碼
public class HookWxLog {
    public static String className = "com.tencent.mm.sdk.platformtools.x";
    public static String methodName = "d";
    public static String methodSig = "(Ljava/lang/String;Ljava/lang/String;)V";

    public static void hook(String tag,String msg) {
        if ("MicroMsg.SnsHeader".equals(tag)) {
            Log.w("@@@", tag + ":" + msg);
        }
    }

    public static void backup(String tag,String msg) {
        return;
    }
}
複製程式碼
  • 看一下列印出來的結果:

log

  • 將這個 url 的值複製到瀏覽器看看:

封面

  • 驗證一下本地路徑對不對,找到對應的資料夾看看 ,其實裡面還有很多子資料夾,這裡面我們 log 有列印出 bgId ,找到字首是 snsb_ + bgId 的檔案/storage/emulated/0/tencent/MicroMsg/c3d467aeabb4fae4b1bbf3a7a6839f5d/sns/b/c/snsb_12944115522489626761),以圖片方式-開啟即可。
path: /storage/emulated/0/tencent/MicroMsg/c3d467aeabb4fae4b1bbf3a7a6839f5d/sns/
複製程式碼
MicroMsg.SnsHeader:showName x452460984 get bgId : 12944115522489626761  olderBgId: null
複製程式碼

檔案管理器-開啟

  • 給介面加個按鈕,複製url然後跳轉到系統瀏覽器檢視,程式碼如下:
public class HookWxSnsUserUI {
    public static String className = "com.tencent.mm.plugin.sns.ui.SnsUserUI";
    public static String methodName = "onCreate";
    public static String methodSig = "(Landroid/os/Bundle;)V";

    public static Activity SnsUserUI;

    public static void hook(Object thiz, Bundle b) {
        Log.w("@@@", "SnsUserUI oncreate");
        SnsUserUI = (Activity) thiz;
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                View decorView = SnsUserUI.getWindow().getDecorView();
                if (decorView != null && decorView instanceof ViewGroup) {
                    LinearLayout llContainer = new LinearLayout(SnsUserUI);
                    FrameLayout.LayoutParams containerLp = new FrameLayout.LayoutParams(-2, -2);
                    containerLp.gravity = Gravity.CENTER_VERTICAL | Gravity.RIGHT;
                    llContainer.setOrientation(LinearLayout.VERTICAL);
                    llContainer.setLayoutParams(containerLp);
                    llContainer.setGravity(Gravity.CENTER);
                    llContainer.setBackgroundColor(Color.parseColor("#ececec"));

                    Button btnCopy = new Button(SnsUserUI);
                    int wrapContent = LinearLayout.LayoutParams.WRAP_CONTENT;
                    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(wrapContent, wrapContent);
                    params.topMargin = SizeUtils.dp2px(8);
                    btnCopy.setLayoutParams(params);
                    btnCopy.setText("到瀏覽器開啟");
                    btnCopy.setTextSize(12);
                    btnCopy.setIncludeFontPadding(false);
                    btnCopy.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if (!HookWxG.URL.equals("empty")) {
                                ToastUtils.showShort("複製成功`");
                                // 複製
                                ClipboardManager clip = (ClipboardManager) SnsUserUI.getSystemService(Context.CLIPBOARD_SERVICE);
                                clip.setText(HookWxG.URL);

                                Intent intent = new Intent();
                                intent.setAction("android.intent.action.VIEW");
                                Uri content_url = Uri.parse(HookWxG.URL);
                                intent.setData(content_url);
                                SnsUserUI.startActivity(intent);
                            } else {
                                ToastUtils.showShort("哎呀,地址沒有賦值成功~");
                            }
                        }
                    });

                    llContainer.addView(btnCopy);
                    ((ViewGroup) decorView).addView(llContainer);
                }
            }
        }, 2000);
        backup(thiz, b);
    }

    public static void backup(Object thiz, Bundle b) {
        return;
    }
}
複製程式碼

到瀏覽器開啟

相關文章