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

朋友圈封面
二、實戰過程
微信好友的頭像是能夠直接儲存到相簿的,但是朋友圈的封面,卻沒有提供儲存的入口,下面我們來一步步找到朋友圈封面的地址。
說明一下:這裡是針對「微信 v6.6.7」 的原始碼分析,不同版本的程式碼因為混淆之後的緣故,不y一定適用。如果想下載微信的歷史版本安裝包,可以下載 「PP 助手」,就能找到微信的歷史版本。
1、TopActivity 分析
定位到朋友圈當前的 Activity
是 com.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 : 12944115522489626761olderBgId: 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; } }

到瀏覽器開啟