逆向settings实现监控app通知

21 minute read

背景

通知是App促活用户的重要手段,推送的内容往往也是各家最时效和优质的内容。市面上的通知监控软件往往只能获取到通知的标题、内容和缩略图,无法获取到内容的地址。

总体思路

如何获取内容的地址以及通知中的其他信息?其实答案很简单,当用户点击推送的时候,会启动App跳转到对应的详情页面,那只要获取到跳转的信息,是不是大概率就能拿到内容的地址或ID了。

查阅资料发现,Android中点击通知能够跳转到对应App的Activity的关键在于PendingIntent。对于PendingIntent,其实可以从字面理解,等候着的Intent,即待触发的Intent,在通知中就是等待用户点击来触发跳转的Intent,Intent中包含了跳转Activity所需要的信息。

说完绕口令,那么Android有没有系统级的API可以获取到所有的通知呢。查阅资料发现,我的环境(Android 8.1 LineageOS),自带通知日志微件,调出的方式是通过长按桌面,选择微件,然后选择设置中的通知日志,界面如下。

其中每一条推送包含的信息如下。

可以猜想的是这个通知日志微件中调用了系统级的API,获取到了所有活跃的、历史的通知。

保持通知日志界面打开,执行如下的命令。

adb shell dumpsys activity activities | grep 'Hist #'

* Hist #0: ActivityRecord{510d5d1 u0 com.android.settings/.Settings$NotificationStationActivity t227}

可以发现,这个界面是Settings App中的NotificationStationActivity。查阅资料发现,NotificationStation类中包含了通知日志中的逻辑操作。那么就来反编译一下Settings App,来看看内部的实现逻辑吧。

Settings属于系统级应用,通常在/system/priv-app/目录,adb pull /system/priv-app/Settings/Settings.apk .拉取到本地,然后用Jadx打开,找到NotificationStation类。

这里边的loadNotifications方法,应该就是我们要找的加载所有通知的入口点。

里边主要调用了getActiveNotifications和getHistoricalNotifications方法获取到了所有的通知,并解析出packagename、title等字段。

回到这张图。

这里边的Intent的内容到底是什么?Notification类中能不能拿到我们想要的PendingIntent?接着我发现了formatPendingIntent,这是生产上图Intent字段值的方法,入参就是PendingIntent。

那么一切就引刃而解了,因为获取到PendingIntent,就意味着获取到了Intent,毕竟PendingIntent是Intent的包裹。

frida hook实践

Java.perform(function () {
    var NotificationStation = Java.use("com.android.settings.notification.NotificationStation");
    var Bundle = Java.use("android.os.Bundle");
    var Iterator = Java.use("java.util.Iterator");
    NotificationStation.formatPendingIntent.implementation = function(pi) {
        var intent = pi.getIntent();  // 隐藏方法,系统级应用才可以调用
        send(intent.getData());
        var bundle = intent.getExtras();
        if (bundle != null) {
            var keys = bundle.keySet();
            var it = keys.iterator();
            while (it.hasNext()) {
                var key = it.next();
                send(key + " " + bundle.get(key));
            }
        }
        return ""
    }
});

输出如下,发现我们拿到了Intent内部的所有信息。

{'type': 'send', 'payload': 'push_msg {"msgId":"2107407265394851840_10231046180696541845","tbMsgId":"","tbTaskId":"","cmd":"ntf","source":"push","channel":"accs","contributor":"","expired":"4h","bt":"1","pushRatio":"","pushDecline":"","pushLives":0,"der":0,"recv_time":"1595603951","bus":"PWAUP","isheadsup":false,"isPopped":false,"isDisplayed":false,"isDeleted":false,"isClicked":false,"isLocal":false,"notifyID":20005,"stats":{"st":1595567777,"rt":1},"title":"头条","text":"冠军杯两项纪录被打破,AG超玩会和DYG各占其一,一家欢喜一家愁! 更多>>","url":"https:\\/\\/t.uc.cn\\/7_8h2tH?uc_param_str=sndnfrpfbivesscpgimibtbmntnisieijblauputoggdnwds&zzd_from=ucpush&btifl=100&uc_biz_str=S%3Acustom%7CC%3Aiflow_wm2&siteType=0&page_show_type=airship","subUrl":"","style":"19","cid":"","ticker":"头条","openWith":"1","sound":"0","vibrate":"0","deletable":"1","forceShow":"0","showWhenLocked":"0","fg":"1","bg":"1","unactive":"1"}'}
{'type': 'send', 'payload': 'tp_business_type PWAUP'}
{'type': 'send', 'payload': 'key_event_id 10'}
{'type': 'send', 'payload': 'push_msg {"msgId":"2107407265394851840_10231046180696541845","tbMsgId":"","tbTaskId":"","cmd":"ntf","source":"push","channel":"accs","contributor":"","expired":"4h","bt":"1","pushRatio":"","pushDecline":"","pushLives":0,"der":0,"recv_time":"1595603951","bus":"PWAUP","isheadsup":false,"isPopped":false,"isDisplayed":false,"isDeleted":false,"isClicked":false,"isLocal":false,"notifyID":20005,"stats":{"st":1595567777,"rt":1},"title":"头条","text":"冠军杯两项纪录被打破,AG超玩会和DYG各占其一,一家欢喜一家愁! 更多>>","url":"https:\\/\\/t.uc.cn\\/7_8h2tH?uc_param_str=sndnfrpfbivesscpgimibtbmntnisieijblauputoggdnwds&zzd_from=ucpush&btifl=100&uc_biz_str=S%3Acustom%7CC%3Aiflow_wm2&siteType=0&page_show_type=airship","subUrl":"","style":"19","cid":"","ticker":"头条","openWith":"1","sound":"0","vibrate":"0","deletable":"1","forceShow":"0","showWhenLocked":"0","fg":"1","bg":"1","unactive":"1"}'}
{'type': 'send', 'payload': 'openurl https://t.uc.cn/7_8j3AK?uc_param_str=sndnfrpfbivesscpgimibtbmntnisieijblauputoggdnwds&zzd_from=ucpush&btifl=100&uc_biz_str=S%3Acustom%7CC%3Aiflow_wm2&siteType=0&page_show_type=airship'}
{'type': 'send', 'payload': 'policy UCM_NEW_WINDOW;UCM_NO_NEED_BACK'}
{'type': 'send', 'payload': 'tp_sub UCM_OPEN_PUSH_NOTIFICATION'}
{'type': 'send', 'payload': 'pd taobao_push'}
{'type': 'send', 'payload': 'tp UCM_OPEN_FROM_PUSH'}
{'type': 'send', 'payload': 'recv_time 1595603951'}
{'type': 'send', 'payload': 'push_msg {"msgId":"2107493893678694400","tbMsgId":"","tbTaskId":"","cmd":"ntf","source":"push","channel":"accs","contributor":"","expired":"3h","bt":"1","pushRatio":"1.0","pushDecline":"0.5","pushLives":0,"der":0,"recv_time":"1595603951","bus":"WAUP","isheadsup":false,"isPopped":false,"isDisplayed":false,"isDeleted":false,"isClicked":false,"isLocal":false,"notifyID":20004,"stats":{"st":1595569699},"title":"25篇论文被质疑造假 北大副校长回应","text":"少数标记错误,不涉及学术不端、不影响科学结论。详情>>","url":"https:\\/\\/t.uc.cn\\/7_8j3AK?uc_param_str=sndnfrpfbivesscpgimibtbmntnisieijblauputoggdnwds&zzd_from=ucpush&btifl=100&uc_biz_str=S%3Acustom%7CC%3Aiflow_wm2&siteType=0&page_show_type=airship","subUrl":"","style":"19","cid":"","ticker":"25篇论文被质疑造假 北大副校长回应","openWith":"1","sound":"1","vibrate":"1","deletable":"1","forceShow":"0","showWhenLocked":"0","fg":"1","bg":"1","unactive":"1"}'}
{'type': 'send', 'payload': 'tp_business_type WAUP'}
{'type': 'send', 'payload': 'key_event_id 10'}
{'type': 'send', 'payload': 'push_msg {"msgId":"2107493893678694400","tbMsgId":"","tbTaskId":"","cmd":"ntf","source":"push","channel":"accs","contributor":"","expired":"3h","bt":"1","pushRatio":"1.0","pushDecline":"0.5","pushLives":0,"der":0,"recv_time":"1595603951","bus":"WAUP","isheadsup":false,"isPopped":false,"isDisplayed":false,"isDeleted":false,"isClicked":false,"isLocal":false,"notifyID":20004,"stats":{"st":1595569699},"title":"25篇论文被质疑造假 北大副校长回应","text":"少数标记错误,不涉及学术不端、不影响科学结论。详情>>","url":"https:\\/\\/t.uc.cn\\/7_8j3AK?uc_param_str=sndnfrpfbivesscpgimibtbmntnisieijblauputoggdnwds&zzd_from=ucpush&btifl=100&uc_biz_str=S%3Acustom%7CC%3Aiflow_wm2&siteType=0&page_show_type=airship","subUrl":"","style":"19","cid":"","ticker":"25篇论文被质疑造假 北大副校长回应","openWith":"1","sound":"1","vibrate":"1","deletable":"1","forceShow":"0","showWhenLocked":"0","fg":"1","bg":"1","unactive":"1"}'}
{'type': 'send', 'payload': 'openurl https://t.uc.cn/7_8jwiw?uc_param_str=sndnfrpfbivesscpgimibtbmntnisieijblauputoggdnwds&zzd_from=ucpush&btifl=100&uc_biz_str=S%3Acustom%7CC%3Aiflow_site%7CK%3Atrue&siteType=0&page_show_type=airship'}
{'type': 'send', 'payload': 'policy UCM_NEW_WINDOW;UCM_NO_NEED_BACK'}
{'type': 'send', 'payload': 'tp_sub UCM_OPEN_PUSH_NOTIFICATION'}
{'type': 'send', 'payload': 'pd taobao_push'}
{'type': 'send', 'payload': 'tp UCM_OPEN_FROM_PUSH'}
{'type': 'send', 'payload': 'recv_time 1595603951'}
{'type': 'send', 'payload': 'push_msg {"msgId":"2107509862063276032","tbMsgId":"","tbTaskId":"","cmd":"ntf","source":"push","channel":"accs","contributor":"","expired":"3h","bt":"1","pushRatio":"1.0","pushDecline":"0.5","pushLives":0,"der":0,"recv_time":"1595603951","bus":"WAUP","isheadsup":false,"isPopped":false,"isDisplayed":false,"isDeleted":false,"isClicked":false,"isLocal":false,"notifyID":20003,"stats":{"st":1595573506},"title":"北京目前局地气温已超过40℃","text":"从温度分布图来看,最高温出现在通州。","url":"https:\\/\\/t.uc.cn\\/7_8jwiw?uc_param_str=sndnfrpfbivesscpgimibtbmntnisieijblauputoggdnwds&zzd_from=ucpush&btifl=100&uc_biz_str=S%3Acustom%7CC%3Aiflow_site%7CK%3Atrue&siteType=0&page_show_type=airship","subUrl":"","style":"19","cid":"","ticker":"北京目前局地气温已超过40℃","openWith":"1","sound":"1","vibrate":"1","deletable":"1","forceShow":"0","showWhenLocked":"0","fg":"1","bg":"1","unactive":"1"}'}
{'type': 'send', 'payload': 'tp_business_type WAUP'}
{'type': 'send', 'payload': 'key_event_id 10'}
{'type': 'send', 'payload': 'push_msg {"msgId":"2107509862063276032","tbMsgId":"","tbTaskId":"","cmd":"ntf","source":"push","channel":"accs","contributor":"","expired":"3h","bt":"1","pushRatio":"1.0","pushDecline":"0.5","pushLives":0,"der":0,"recv_time":"1595603951","bus":"WAUP","isheadsup":false,"isPopped":false,"isDisplayed":false,"isDeleted":false,"isClicked":false,"isLocal":false,"notifyID":20003,"stats":{"st":1595573506},"title":"北京目前局地气温已超过40℃","text":"从温度分布图来看,最高温出现在通州。","url":"https:\\/\\/t.uc.cn\\/7_8jwiw?uc_param_str=sndnfrpfbivesscpgimibtbmntnisieijblauputoggdnwds&zzd_from=ucpush&btifl=100&uc_biz_str=S%3Acustom%7CC%3Aiflow_site%7CK%3Atrue&siteType=0&page_show_type=airship","subUrl":"","style":"19","cid":"","ticker":"北京目前局地气温已超过40℃","openWith":"1","sound":"1","vibrate":"1","deletable":"1","forceShow":"0","showWhenLocked":"0","fg":"1","bg":"1","unactive":"1"}'}
{'type': 'send', 'payload': 'from push'}
{'type': 'send', 'payload': 'SELFPUSH {"s":"news_news_top","i":"20200723A0WZFF00","pid":"337190000","ty":"article","scheme":"","at":-1}'}
{'type': 'send', 'payload': 'newsSeqid 200009155'}
{'type': 'send', 'payload': 'com.sina.weibo.intent.extra.LOCALPUSH_NOTIFICATION_ID 1595584863'}
{'type': 'send', 'payload': 'com.sina.weibo.intent.extra.LOCALPUSH_NOTIFICATION_ID 1595584863'}
{'type': 'send', 'payload': 'from push'}
{'type': 'send', 'payload': 'SELFPUSH {"s":"news_news_top","i":"20200723A09NGP00","pid":"335120000","ty":"article","scheme":"","at":-1}'}
{'type': 'send', 'payload': 'newsSeqid 200009155'}
{'type': 'send', 'payload': 'from push'}
{'type': 'send', 'payload': 'SELFPUSH {"s":"news_news_top","i":"20200724V0CBUJ00","pid":"200009276","ty":"article","scheme":"","at":0}'}
{'type': 'send', 'payload': 'newsSeqid 200009155'}
{'type': 'send', 'payload': 'from push'}
{'type': 'send', 'payload': 'SELFPUSH {"s":"news_news_top","i":"DSG2020072400818700","pid":"200009163","ty":"article","scheme":"","at":0}'}
{'type': 'send', 'payload': 'newsSeqid 200009155'}

改进点

本以为到此为止大部分App的通知我们都可以获取到了,但在运行过程中发现,用该方法获取通知Intent信息,有时候会导致Settings崩溃退出。

回溯日志发现,每次崩溃都是在解析某资讯App的通知时出现的,具体崩溃日志如下。

07-24 17:44:31.467  4851  4851 D AndroidRuntime: Shutting down VM
07-24 17:44:31.468  4851  4851 E AndroidRuntime: FATAL EXCEPTION: main
07-24 17:44:31.468  4851  4851 E AndroidRuntime: Process: com.android.settings, PID: 4851
07-24 17:44:31.468  4851  4851 E AndroidRuntime: java.lang.RuntimeException: Unable to resume activity {com.android.settings/com.android.settings.Settings$NotificationStationActivity}: java.lang.RuntimeException: Parcelable encountered ClassNotFoundException reading a Serializable object (name = com.***.news.data.PushData)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3582)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3622)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2863)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.app.ActivityThread.-wrap11(Unknown Source:0)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1590)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:106)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.os.Looper.loop(Looper.java:164)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.app.ActivityThread.main(ActivityThread.java:6499)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:440)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: Caused by: java.lang.RuntimeException: Parcelable encountered ClassNotFoundException reading a Serializable object (name = com.***.news.data.PushData)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.os.Parcel.readSerializable(Parcel.java:3010)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.os.Parcel.readValue(Parcel.java:2796)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.os.Parcel.readArrayMapInternal(Parcel.java:3114)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:273)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.os.BaseBundle.unparcel(BaseBundle.java:226)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.os.BaseBundle.keySet(BaseBundle.java:520)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at com.android.settings.notification.NotificationStation.formatPendingIntent(Native Method)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at com.android.settings.notification.NotificationStation.generateExtraText(NotificationStation.java:432)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at com.android.settings.notification.NotificationStation.loadNotifications(NotificationStation.java:301)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at com.android.settings.notification.NotificationStation.refreshList(NotificationStation.java:209)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at com.android.settings.notification.NotificationStation.onResume(NotificationStation.java:205)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.app.Fragment.performResume(Fragment.java:2554)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1322)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1557)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1618)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.app.FragmentManagerImpl.dispatchMoveToState(FragmentManager.java:3027)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.app.FragmentManagerImpl.dispatchResume(FragmentManager.java:2989)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.app.FragmentController.dispatchResume(FragmentController.java:200)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.app.Activity.performResume(Activity.java:7173)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3557)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	... 10 more
07-24 17:44:31.468  4851  4851 E AndroidRuntime: Caused by: java.lang.ClassNotFoundException: com.***.news.data.PushData
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at java.lang.Class.classForName(Native Method)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at java.lang.Class.forName(Class.java:453)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:629)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.os.Parcel$2.resolveClass(Parcel.java:3001)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1616)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1777)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1354)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:374)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at android.os.Parcel.readSerializable(Parcel.java:3004)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	... 29 more
07-24 17:44:31.468  4851  4851 E AndroidRuntime: Caused by: java.lang.ClassNotFoundException: Didn't find class "com.***.news.data.PushData" on path: DexPathList[[zip file "/system/priv-app/Settings/Settings.apk"],nativeLibraryDirectories=[/system/priv-app/Settings/lib/arm64, /system/priv-app/Settings/Settings.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64, /system/lib64, /vendor/lib64]]
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:125)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
07-24 17:44:31.468  4851  4851 E AndroidRuntime: 	... 39 more
07-24 17:44:31.471  1413  1428 W ActivityManager:   Force finishing activity com.android.settings/.Settings$NotificationStationActivity
07-24 17:44:31.480  1413  1450 I ActivityManager: Showing crash dialog for package com.android.settings u0

可以发现,是在unparcel的时候报错。很明显的是,Intent中被放入了该App特有的类,因为我们hook的是Settings App,肯定无法找到com.***.news.data.PushData类。

有一种方法是将var keys = bundle.keySet();用try catch包裹,报错时直接跳过这条通知。

但如果就是想获取到这条通知中的内容呢?frida API中有Java.openClassFile(filePath)这个方法,可以加载dex或者apk文件中的类。所以执行Java.openClassFile(filePath).load()后,再用var classLoader = Java.use("com.***.news.data.PushData").class.getClassLoader(),获取该类的classLoader,并调用bundle.setClassLoader(classLoader)即可。

mipush逆向

运行了一段时间后,发现通过mipush推送的通知,没有办法获取到通知中的内容,mipush_payload中全部都是密文。逆向SDK发现,手机从mipush服务端接收到是经过格式化且aes加密的消息,mipush_payload会在App侧的SDK解密,逻辑搞清楚之后,逆向也很简单。

不过在逆向的过程中发现,mipush SDK的混淆做的很彻底,同一个类中经常出现同名但类型不同的成员变量,也有方法名且参数类型相同的方法,对frida hook有很大的干扰。这里分享一下解决的代码。

var Java_lang_Object = Java.use('java.lang.Object');
var Java_lang_String = Java.use('java.lang.String');
var Type = Java.use('java.lang.reflect.Type');
    //This function get method reference by reflect
    function dynamic_search_method(io_object, iv_name, iv_ret_type, it_par){ 
      var lt_methods = io_object.getMethods()  ;
      var lv_found;
      for(var  lv_i=0;lv_i < lt_methods.length;lv_i++){
         if(lt_methods[lv_i].getName().toString()  == iv_name && lt_methods[lv_i].getGenericReturnType().getTypeName() == iv_ret_type){
            var lt_par_type = lt_methods[lv_i].getParameterTypes();
            if(lt_par_type.length == it_par.length){
              lv_found = true; 
              for(var  lv_j=0;lv_j < lt_par_type.length && lv_found == true;lv_j++){
                if(lt_par_type[lv_j].getName().toString() != it_par[lv_j]) lv_found = false  ; 
              }                   
              if(lv_found == true) return lt_methods[lv_i];
            }
         }
      }
     return null;
    }
    //This function call method dynamically 
    function dynamic_invoke(io_object,io_method, it_par){
      if(io_object===null || io_method ===null ) return null;
      try{
        var lo_cast_obj = Java.cast( io_object ,Java_lang_Object);
      }catch(e){
        return null;
      }
      var lt_par = Java.array('java.lang.Object',it_par);
      return io_method.invoke(lo_cast_obj,lt_par);
    }
var lo_fld_eaxe;
var lv_found = false;
var lt_fields = this.getClass().getDeclaredFields();
for (var i = 0; i < lt_fields.length && lv_found == false; i++) {
    if(lt_fields[i].getName().toString() == 'a' &&  lt_fields[i].getType().getName().toString() == 'e.a.x.e' ){
       lo_fld_eaxe = lt_fields[i];
       lv_found = true; 
  }
}
if(lv_found == true) {
   lo_fld_eaxe.setAccessible(true);
   try{ 
          var lv_e_a_x_e = lo_fld_eaxe.get(this);   
   }
   catch(err){
          console.log("Error:"+err);
   }
 }

另外附上frida cast byte[]的方法,Java.array('byte', obj);

if (key.toString() === "mipush_payload") {
    if (pkg === "com.jifen.qukan") {
        var secKey = "IS7hXXi0RDSkX+CnfTAAAA=="  // 可以在app的sharedpreferences中找到
    }

    var mipushPayloadBytes = bundle.getByteArray(key);
    var IK = Java.use("com.xiaomi.push.ik");
    var ik = IK.$new();
    var IY = Java.use("com.xiaomi.push.iy");
    IY.a(ik, mipushPayloadBytes)
    var DecryptClass = Java.use("com.xiaomi.push.h");
    var Base64Class = Java.use("com.xiaomi.push.bc");
    var keyBytes = Base64Class.a(JavaString.$new(secKey).toCharArray())
    var lo_meth = dynamic_search_method(ik.getClass(),"a","byte[]",[]);
    var lv_var= dynamic_invoke(ik,lo_meth,[]);
    var buffer = Java.array('byte', lv_var);
    var decryptBytes = DecryptClass.a(keyBytes, buffer); 
    var apClass = Java.use("com.xiaomi.mipush.sdk.ap")
    var hoClass = Java.use("com.xiaomi.push.ho")
    var iz = apClass.a(hoClass.a(5), true)
    IY.a(iz, decryptBytes)
    iz = Java.cast(iz, Java.use("com.xiaomi.push.ir"))
    send(iz.toString())
}

全文完~

Updated: