前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >HooK之hook Notification

HooK之hook Notification

作者头像
李小白是一只喵
发布2021-02-05 11:19:10
1.3K0
发布2021-02-05 11:19:10
举报
文章被收录于专栏:算法微时光

image.png

目录

第一章:android hook介绍

第二章:hook之替换View.OnClickListener

第三章:HooK之hook Notification

HooK Notification

发送消息到通知栏的核心代码:

代码语言:javascript
复制
String channelId = createNotificationChannel("my_channel_ID", "my_channel_NAME", NotificationManager.IMPORTANCE_HIGH);
NotificationCompat.Builder notification = new NotificationCompat.Builder(MainActivity.this, channelId)
                        .setContentTitle("通知")
                        .setContentText("你好,世界!")
                        .setContentIntent(pendingIntent)
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setAutoCancel(true)
                        .setWhen(System.currentTimeMillis());


notificationManager.notify(16657, notification.build());

最后是使用了notificationManager的notify方法。

继续跟踪notify方法:

代码语言:javascript
复制
public void notify(int id, Notification notification)
{
    notify(null, id, notification);
}

public void notify(String tag, int id, Notification notification)
{
    notifyAsUser(tag, id, notification, mContext.getUser());
}

notify 方法最终调用 notifyAsUser 方法:

代码语言:javascript
复制
    /**
     * @hide
     */
    @UnsupportedAppUsage
    public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
    {
        INotificationManager service = getService();
        String pkg = mContext.getPackageName();

        try {
            if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
                    fixNotification(notification), user.getIdentifier());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

notifyAsUser 方法中,service 是一个单例,因此,可以想方法 hook 住这个 service。

而且notifyAsUser 最终会调用到 service 的 enqueueNotificationWithTag 方法。

因此 hook 住 service 的 enqueueNotificationWithTag 方法即可.

具体思路

Hook Notification,大概需要三步:

  • 第一步:得到 NotificationManager 的 service
  • 第二步:因为 service 是接口,所以我们可以使用动态代理,获取动态代理对象
  • 第三步:偷梁换柱,使用动态代理对象 proxyNotiMng 替换系统的 service
代码展示

全部源码参考:android之NotificationManager服务

代码语言:javascript
复制
package com.exmple.hooknotify;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.time.format.TextStyle;

public class MainActivity extends AppCompatActivity {
    public  static String TAG = "Hello Hook";
    Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        try {
            hookNotificationManager(MainActivity.this);
        } catch (Exception e) {
            e.printStackTrace();
        }

        button = (Button)findViewById(R.id.buttonOne);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                NotificationManager notificationManager = (NotificationManager) MainActivity.this.getSystemService(Context.NOTIFICATION_SERVICE);

                Intent intent = new Intent(MainActivity.this, MainActivity2.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                PendingIntent pendingIntent = PendingIntent.getActivity(MainActivity.this, 0, intent, 0);

                String channelId = createNotificationChannel("my_channel_ID", "my_channel_NAME", NotificationManager.IMPORTANCE_HIGH);
                NotificationCompat.Builder notification = new NotificationCompat.Builder(MainActivity.this, channelId)
                        .setContentTitle("通知")
                        .setContentText("你好,世界!")
                        .setContentIntent(pendingIntent)
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setAutoCancel(true)
                        .setWhen(System.currentTimeMillis());


                notificationManager.notify(16657, notification.build());
            }
        });


    }

    private String createNotificationChannel(String channelID, String channelNAME, int level) {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            NotificationChannel channel = new NotificationChannel(channelID, channelNAME, level);
            manager.createNotificationChannel(channel);
            return channelID;
        } else {
            return null;
        }
    }

    public static void hookNotificationManager(final Context context) throws Exception {
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        // 第一步:得到系统的 sService
        Method getService = NotificationManager.class.getDeclaredMethod("getService");
        getService.setAccessible(true);
        final Object sOriginService = getService.invoke(notificationManager);

        // 第二步:设置动态代理对象
        Class iNotiMngClz = Class.forName("android.app.INotificationManager");
        Object proxyNotiMng = Proxy.newProxyInstance(context.getClass().getClassLoader(), new
                Class[]{iNotiMngClz}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Log.d(TAG, "invoke(). method:" + method);
                String name = method.getName();
                Log.d(TAG, "invoke: name=" + name);
                if (args != null && args.length > 0) {
                    for (Object arg : args) {
                        Log.d(TAG, "invoke: arg=" + arg);
                    }
                }
                Toast.makeText(context.getApplicationContext(), "检测到有人发通知了", Toast.LENGTH_SHORT).show();
                // 操作交由 sOriginService 处理,不拦截通知
                return method.invoke(sOriginService, args);
            }
        });

        // 第三步:偷梁换柱,使用 proxyNotiMng 替换系统的 sService
        Field sServiceField = NotificationManager.class.getDeclaredField("sService");
        sServiceField.setAccessible(true);
        sServiceField.set(notificationManager, proxyNotiMng);
    }

}

执行效果:

image.png

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • HooK Notification
    • 具体思路
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档