HooK之hook Notification

image.png

目录

第一章:android hook介绍
第二章:hook之替换View.OnClickListener
第三章:HooK之hook Notification

HooK Notification

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

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方法:

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 方法:

    /**
     * @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服务

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

你可能感兴趣的:(HooK之hook Notification)