【技术】【小程序】MiniScrawler 工具原理解析

之前用到了文章A Measurement Study of Wechat Mini-Apps提出的微信小程序半自动化收集工具MiniCrawler。最近深入看一下工具实现细节。

工具介绍

工具有两个功能,一是小程序元数据收集,另一个是小程序包下载。

小程序元数据收集

具体原理不难,就是拿到用户的请求参数,然后替换 request 中的 keyword 字段,批量发起请求。

  • 用户手动在小程序搜索页面输入关键词,工具会帮助拿到用户向服务七搜索请求的一些参数,主要是认证参数
  • 用户把这些参数和待搜索词列表放在指定位置
  • 工具即可用这些拿到的参数批量发起小程序搜索请求,拿到服务器返回的多个小程序的元数据(小程序名、小程序 AppId 等)

小程序包下载

要求

  1. 微信 App 得是 7.0.20 版本
  2. 编译并安装 XposedPlugin
  3. 打开任意小程序页面
  4. 运行 adb shell am broadcast -a android.intent.myper --es appid "{待下载小程序的appid}",工具会自动下载链接及相关信息到 /sdcard/apps.txt

逆向 XposedPlugin

看了 XposedPlugin 源码,注册了个 android.intent.myper 的 receiver 用来接收待下载小程序的 appid。

Q:具体如何起到启动目标小程序的作用?
A:com.example.vsa.xposedutility.tests.WechatMiniAppsDownloader 中,工具接了 appid 之后 invoke 了小程序 API(代码如下)。通过调起 wx.navigateToMiniProgram,appid 作为参数传进去,达到从当前小程序(任意的,但需要是小程序的 context)跳转到目标小程序的目的。

1
2
Method method = next.getClass().getMethod("invokeHandler", String.class, String.class, Integer.TYPE);
method.invoke(next, "navigateToMiniProgram", "{\"appId\":\"" + stringExtra + "\",\"extraData\":\"\",\"envVersion\":\"release\",\"scene\":1037,\"sceneNote\":\"\"}", 9999);

Q:如何拿到下载链接及包的相关信息?
A:com.example.vsa.xposedutility.tests.Wechat7020 类中 hookAll 中可以看出(如下图),关注的是微信 App 里的两个类:com.tencent.mm.plugin.appbrand.appcache.bacom.tencent.mm.plugin.appbrand.jsapi.l

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void beforeHookedMethod(XC_MethodHook.MethodHookParam methodHookParam) throws Throwable {
Log.d(Wechat7020.TAG, "---->>>:" + methodHookParam.method.getName());
Utilities.printParameter(Wechat7020.TAG, methodHookParam);
if (methodHookParam.method.getName().equals("invokeHandler") && !Wechat7020.did) {
Wechat7020.did = true;
methodHookParam.thisObject.getClass().getMethod("invokeHandler", String.class, String.class, Integer.TYPE);
}
if (methodHookParam.method.getDeclaringClass().getName().equals("com.tencent.mm.plugin.appbrand.appcache.ba") && (methodHookParam.method instanceof Constructor) && ((Constructor) methodHookParam.method).getParameterTypes().length == 4) {
Utilities.writeToFile("/sdcard/apps.txt", (methodHookParam.args[0] + BuildConfig.FLAVOR) + " " + (methodHookParam.args[2] + BuildConfig.FLAVOR) + " " + (methodHookParam.args[3] + BuildConfig.FLAVOR) + "\n");
}
if (methodHookParam.method.getDeclaringClass().getName().equals("com.tencent.mm.plugin.appbrand.jsapi.l") && !WechatMiniAppsDownloader.wvs.contains(methodHookParam.thisObject)) {
WechatMiniAppsDownloader.wvs.add(methodHookParam.thisObject);
}
}

逆向微信

com.tencent.mm.plugin.appbrand.appcache.ba
根据 XposedPlugin 代码行为,应该在 hook 到该类之后写数据(包下载 url)到 /sdcard/apps.txt,但实测后没有该文件。只有在初始,需要下载微信小程序 jssdk 包的时候会调用一下,但下载普通小程序根本不走这。
自己又静态分析了一阵,找到了 com.tencent.mm.plugin.appbrand.appcache.bt.a(str,...) 方法,参数 str 就是下载链接。不知道为什么在我这边是 .bt 类,而作者那边是 .ba 类,apk 版本是一样的。

com.tencent.mm.plugin.appbrand.jsapi.l
里面 invokeHandler 是调起小程序 api 的方法

1
public final String invokeHandler(String str, String str2, int i)

里面 str 就是 invoke 的小程序方法名,本场景下是 navigateToMiniProgram,str2 是传给这个小程序方法的参数,本场景下是 JSON,包括 appId 等信息。
函数体内有一句String y = dVar.y(str, str2, i);,这里 trace 到后面是一个抽象类,该抽象类由多个子类实现。这些子类与 wx.{subAPI} 一一对应。navigateToMiniprogramcom.tencent.mm.plugin.appbrand.jsapi.miniprogram_navigator.g