behindeye 发表于 2019-1-25 10:30

VirtualApp实战之拿到女神朋友圈封面

本帖最后由 behindeye 于 2020-1-20 14:09 编辑

## 一、前言
之前讲到得 `VirtualApp` 免 `Root` 可能大家还没实际体会到它的好处,这篇文章教大家**如何拿到女神微信「朋友圈」的封面**(邪恶脸)

![朋友圈封面](https://i.loli.net/2019/01/24/5c49c90bba192.png)

## 二、实战过程
微信好友的头像是能够直接保存到相册的,但是朋友圈的封面,却没有提供保存的入口,下面我们来一步步找到朋友圈封面的地址。

**说明一下**:这里是针对「微信 v6.6.7」 的源码分析,不同版本的代码因为混淆之后的缘故,不y一定适用。如果想下载微信的历史版本安装包,可以下载 「PP 助手」,就能找到微信的历史版本。

### 1、TopActivity 分析
定位到朋友圈当前的 `Activity` 是 `com.tencent.mm.plugin.sns.ui.SnsUserUI`

!(https://i.loli.net/2019/01/24/5c49ce3956b7e.png)

### 2、Jadx 反编译源码
用 Jadx 将反编译的源码,另存为 `Gradle` 项目,这样可以直接在 `Android Studio` 看源码了,不过这样导出来的,是不能够运行的。我个人习惯在 `Android Studio` 查看源码,因为可以方便地使用**查找、跳转和类结构**等功能。

![另存为Gradle项目](https://i.loli.net/2019/01/24/5c49cffcc95fa.png)

- 看看 `SnsUserUI` 源码,发现并没有设置封面相关的地方,不过里面有个类似乎把很多操作隐藏起来,并且传入几个关键字段信息进去,值得怀疑:

!(https://i.loli.net/2019/01/24/5c49d2d95f9b4.png)

- 点进去看 bb 类,在它的 `onCreate()` 方法里面,找到了封面的封装类 `SnsHeader`

!(https://i.loli.net/2019/01/24/5c49d3dd2f060.png)

- 继续看 `SnsHeader` 的源码,找到封面设置的 `ImageView` ,应该就是图片红框的控件,为啥这么确定?因为点击封面会弹出更换封面的弹窗,里面的 log 提示的也很明显

!(https://i.loli.net/2019/01/24/5c49d46e64279.png)

- 跟踪变量 `this.nWh.nWt` 是在哪里设置值,找到以下代码,可以看到给封面设置 `bitmap` 值以及默认封面的资源名称,其中 `bitmap` 对应的变量名称是 a

![设置bitmap](https://i.loli.net/2019/01/24/5c49d598be2c2.png)

- 找到这个 a 赋值的地方,这方法有个可疑参数:`accSnsPath` ,跟一下

!(https://i.loli.net/2019/01/24/5c49d6983beb3.png)

- 可以看到 `accSnsPath` 有两处赋值的地方,猜测封面图片的处理应该是用了「三级缓存」机制的,先读内存,内存没有,就读本地,本地没有,再网络请求获取。所以对应的第一个赋值的地方是:本地存储路径;第二个赋值的地方是:网络的 `url`。

!(https://i.loli.net/2019/01/25/5c4a6457ba535.png)

!(https://i.loli.net/2019/01/24/5c49d6fe5d231.png)



### 3、Hook
根据上面的分析过程,对以下 3 个类,进行 hook ,**这里有个 「小技巧」,是「尼古拉斯·赵四」分享的,如果在进行代码分析过程没有头绪的时候,可以对 Log 类进行 hook**,在打印出来的日志,找寻蛛丝马迹。

**再次强调,hook 针对微信 v6.6.7 源码**,采用 YAHFAhook

```
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;
    }
}
```

- 看一下打印出来的结果:

!(https://upload-images.jianshu.io/upload_images/2578395-2d4495d879947bcf.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp)

- 将这个 url 的值复制到浏览器看看:

![封面](https://upload-images.jianshu.io/upload_images/2578395-ab432715e4af67b4.png?imageMogr2/auto-orient/strip|imageView2/2/w/1050/format/webp)

- 验证一下本地路径对不对,找到对应的文件夹看看 ,其实里面还有很多子文件夹,这里面我们 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
```

![文件管理器-打开](https://upload-images.jianshu.io/upload_images/2578395-2aa4487af002185a.png?imageMogr2/auto-orient/strip|imageView2/2/w/256/format/webp)

- 给界面加个按钮,**复制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("czc", "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) {
      Log.w("YAHFA", "LauncherUI backup");
      return;
    }
}
```

![到浏览器打开](https://i.loli.net/2019/01/25/5c4a7d7f4aebd.png)

behindeye 发表于 2019-1-25 11:18

这一次贴了附件了,大家可以下载玩玩

behindeye 发表于 2019-1-25 15:44

behindeye 发表于 2019-1-25 11:18
这一次贴了附件了,大家可以下载玩玩

感谢网友分享,用 HttpCanary 抓包也是可以抓到封面图片的地址的~{:1_893:}{:1_921:}

773815358 发表于 2019-1-25 14:10

xiaohan1 发表于 2019-1-25 10:37

学习一下

lightningfight 发表于 2019-1-25 10:42

向楼主学习{:17_1087:}

wbkasd 发表于 2019-1-25 10:43

干的漂亮,我选择截图{:301_971:}

jfy168 发表于 2019-1-25 10:45

理论实践俱佳。

c0okie5 发表于 2019-1-25 10:48

膜拜大佬{:17_1068:}

zhaolisheng 发表于 2019-1-25 10:51

向大神学习{:300_950:}

lpdswing 发表于 2019-1-25 11:03

我选择截图加抠图

slam 发表于 2019-1-25 11:06

顶 支持技术贴

西元世纪爱 发表于 2019-1-25 11:16

弱鸡的我只能截图。感谢楼主
页: [1] 2 3 4 5 6 7
查看完整版本: VirtualApp实战之拿到女神朋友圈封面