老样子,上一篇MonkeyLei:Java-Hook技术-入门实践+反射、动态代理、热修复再看看 我们Hook学习了一下,一个是Java本地Main的实践练习。 一个是Android的监听事件的Hook的练习。 Now,我们Hook拦截下通知。流程如出一辙,都是先去跟踪通知流程,然后找到我们需要以及能够Hook的接口对象,然后替换它。就是之前有篇文章说到的Hook四板斧:

1. 根据需求确定 要hook的对象
2. 寻找要hook的对象的持有者,拿到要hook的对象
3. 定义“要hook的对象”的代理类,并且创建该类的对象
4. 使用上一步创建出来的对象,替换掉要hook的对象

我们要做什么?首先我们通知的方式是这样,参考之前一篇文章MonkeyLei:Android-发送通知/含Android8.0+耐心照着官方教程实现,不是很难:

        // 4. Show the notification - 展示通知// https://developer.android.google.cn/training/notify-user/build-notification.html#notifyNotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);// notificationId is a unique int for each notification that you must definenotificationManager.notify((int) System.currentTimeMillis(), builder.build());

重点就是notify方法,然后我们一路跟踪过去有如下流程:

        //        1.//        public void notify(int id, @NonNull Notification notification) {//            this.notify((String)null, id, notification);//        }////        2.//        public void notify(@Nullable String tag, int id, @NonNull Notification notification) {//            ....//            this.mNotificationManager.notify(tag, id, notification);//        }////        3.//        public void notify(String tag, int id, Notification notification)//        {//            notifyAsUser(tag, id, notification, mContext.getUser());//        }////        4.//        /**//         * @hide//         *///        public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)//        {//            INotificationManager service = getService();//            ....//            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,//                    copy, user.getIdentifier());//        }////        5.//        /** @hide *///        static public INotificationManager getService()//        {//            if (sService != null) {//                return sService;//            }//            IBinder b = ServiceManager.getService("notification");//            sService = INotificationManager.Stub.asInterface(b);//            return sService;//        }

你会发现最终我们调用的是INotificationManagerenqueueNotificationWithTag方法,这是一个接口对象,是通过Binder转换获取的一个接口对象,是程序与服务进行通信的方式。在内部的我们就暂不跟踪了。。到此,我们只需要替换掉这个sService为我们定义的动态代理对象即可!

接下来我们需要做:

1. 由于我们用的是NotificationManagerCompat来管理的通知(这个是NotificationManager的一个包装,你可以直接用老版的获取NotificationManager的方法,一样),所以我们需要通过NotificationManagerCompat获取NotificationManager - 这是一个变量mNotificationManager,所以好获取:

  // 1. 通过反射获取NotificationManager对象,新版本可以这样。其实也可以直接如下获取:// -- (NotificationManager)this.mContext.getSystemService("notification");Field mNotificationManager = NotificationManagerCompat.class.getDeclaredField("mNotificationManager");mNotificationManager.setAccessible(true);final NotificationManager notificationManager1 = (NotificationManager) mNotificationManager.get(notificationManagerCompat);

2. 然后获取NotificationManager的sService

   // 2. 获取将要被代理对象替换的INotificationManager变量- 实际是一个IxxxxService实例对象(该对象可以调用服务对应的方法)Field sService = NotificationManager.class.getDeclaredField("sService");sService.setAccessible(true);

3. 创建一个代理对象 - 需要稍微注意下Class.forName的使用

  // 3. 创建一个代理接口对象 - 针对INotificationManager接口做代理Class iNotiMngClz = Class.forName("android.app.INotificationManager");final Object iNotiMngClzLast = sService.get(notificationManager1);Object proxyNotificationManager = Proxy.newProxyInstance(getClassLoader(), new Class[]{iNotiMngClz}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Log.e("test", "通知被拦截了!");// 为了正常运行,我们还是执行下人家的通知吧!//return method.invoke(iNotiMngClzLast, args);return null;}});

4. 然后替换掉sService - 注意是之前获取的当前App的NotificationManager对象的,所以参数1需要设置该对象。参数2设置新的代理对象

  // 4. 替换NotificationManager的sServicesService.set(notificationManager1, proxyNotificationManager);

5. 到此就完事了。。就达到了Hook当前App通知的目的,你可以拦截掉通知,也可以增加处理,不拦截通知 - 之前的通知执行的方法对象iNotiMngClzLast需要你先获取:

  final Object iNotiMngClzLast = sService.get(notificationManager1);

测试了下,基本没啥问题。。。算是又熟悉了下反射,代理的使用。。同时如何下手去Hook一个东西。。相信对于你要去实践不注册manifest启动一个页面有所帮助。。。

完整代码:

NotifyHookActivity.java

package com.skl.hooktest;import android.app.NotificationManager;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class NotifyHookActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_notify_hook);}/*** Hook通知* @param view*/public void hook(View view) {// 跟踪下notificationManager.notify的调用流程;// 最终调用的是INotificationManager的enqueueNotificationWithTag方法,// --这就涉及到Android Service通信(Binder机制)的相关知识了。//        1.//        public void notify(int id, @NonNull Notification notification) {//            this.notify((String)null, id, notification);//        }////        2.//        public void notify(@Nullable String tag, int id, @NonNull Notification notification) {//            ....//            this.mNotificationManager.notify(tag, id, notification);//        }////        3.//        public void notify(String tag, int id, Notification notification)//        {//            notifyAsUser(tag, id, notification, mContext.getUser());//        }////        4.//        /**//         * @hide//         *///        public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)//        {//            INotificationManager service = getService();//            ....//            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,//                    copy, user.getIdentifier());//        }////        5.//        /** @hide *///        static public INotificationManager getService()//        {//            if (sService != null) {//                return sService;//            }//            IBinder b = ServiceManager.getService("notification");//            sService = INotificationManager.Stub.asInterface(b);//            return sService;//        }/**sService只创建一次,INotificationManager是接口,到这里我们就可以Hook替换掉这个sService*/// 老版本api可以用这个方法获取NotificationManager// NotificationManager notificationManager2 = getSystemService(NotificationManager.class);// 内部最终还是会获取NotificationManagerNotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(this);try {// 1. 通过反射获取NotificationManager对象,新版本可以这样。其实也可以直接如下获取:// -- (NotificationManager)this.mContext.getSystemService("notification");Field mNotificationManager = NotificationManagerCompat.class.getDeclaredField("mNotificationManager");mNotificationManager.setAccessible(true);final NotificationManager notificationManager1 = (NotificationManager) mNotificationManager.get(notificationManagerCompat);// 2. 获取将要被代理对象替换的INotificationManager变量- 实际是一个IxxxxService实例对象(该对象可以调用服务对应的方法)Field sService = NotificationManager.class.getDeclaredField("sService");sService.setAccessible(true);// 3. 创建一个代理接口对象 - 针对INotificationManager接口做代理Class iNotiMngClz = Class.forName("android.app.INotificationManager");final Object iNotiMngClzLast = sService.get(notificationManager1);Object proxyNotificationManager = Proxy.newProxyInstance(getClassLoader(), new Class[]{iNotiMngClz}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Log.e("test", "通知被拦截了!");// 为了正常运行,我们还是执行下人家的通知吧!return method.invoke(iNotiMngClzLast, args);// return null;}});// 4. 替换NotificationManager的sServicesService.set(notificationManager1, proxyNotificationManager);} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}/*** 发送通知(支持8.0+)* @param view*/public void nofify(View view) {NotifyUtil.notifyUtil(this);}/*** 跳转到其他页面,发送通知; 验证下Hook是不是App全局有效;* 答案:是的* 因为:我们的context获取的通知服务时针对当前App的,不会干扰到其他App,系统的更不可能了。* TODO 下一步:我们去跟踪下获取通知服务的源码流程,应该就大概知道原因了....* @param view*/public void goNextPage(View view) {// startActivity(new Intent(this, MainActivity.class));}
}

NotifyUtil.java

  package com.skl.hooktest;import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;public class NotifyUtil {private static String CHANNEL_ID = "111";public static void notifyUtil(Context context){// 1. Set the notification content - 创建通知基本内容// https://developer.android.google.cn/training/notify-user/build-notification.html#builderNotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID).setSmallIcon(R.drawable.ic_launcher_background).setContentTitle("My notification")// 这是单行//.setContentText("Much longer text that cannot fit one line...")// 这是多行.setStyle(new NotificationCompat.BigTextStyle().bigText("Much longer text that cannot fit one line..." +"Much longer text that cannot fit one line..." +"Much longer text that cannot fit one line...")).setPriority(NotificationCompat.PRIORITY_HIGH).setAutoCancel(true);// 2. Create a channel and set the importance - 8.0后需要设置Channel// https://developer.android.google.cn/training/notify-user/build-notification.html#buildercreateNotificationChannel(context);// 3. Set the notification's tap action - 创建一些点击事件,比如点击跳转页面// https://developer.android.google.cn/training/notify-user/build-notification.html#click// 4. Show the notification - 展示通知// https://developer.android.google.cn/training/notify-user/build-notification.html#notifyNotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);// notificationId is a unique int for each notification that you must definenotificationManager.notify((int) System.currentTimeMillis(), builder.build());}private static void createNotificationChannel(Context context) {// Create the NotificationChannel, but only on API 26+ because// the NotificationChannel class is new and not in the support libraryif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {CharSequence name = context.getString(R.string.app_name);String description = context.getString(R.string.app_name);int importance = NotificationManager.IMPORTANCE_HIGH;NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);channel.setDescription(description);// Register the channel with the system; you can't change the importance// or other notification behaviors after thisNotificationManager notificationManager = context.getSystemService(NotificationManager.class);notificationManager.createNotificationChannel(channel);}}
}

ok。先这样吧。肚子饿了,先去恰饭。。。后面忙完了继续抓紧学习实践。。插件化入门还都还需时间。更别说热修复了。。。不过后面我想热修复可能会先快速使用框架上手,然后再去深入。。。

Hook测试实践工程地址: NetNut/DocPro

HOOK注意事项

  • Hook 静态变量和单例,因为一旦创建对象,它们不容易变化,非常容易定位。
  • 考虑 Android API 的变化,注意做好兼容

加油,下个月初要开始加强锻炼和调整心态了。。。。 哎。太懒了,真的是。。。

android hook底层代码_Java-Hook技术-入门实践(反射、动态代理)-Hook拦截通知(当前App/Context)...相关推荐

  1. hook控制浏览器的方法_Java-Hook技术-入门实践+反射、动态代理、热修复再看看

    延续之前的MonkeyLei:Android-模块化.组件化.插件化.热修复-插件化-起个头,我们复习下里面的关于反射和动态代理点的知识.然后尝试简单了解下Hook... 看之前文章,记得多复习下反射 ...

  2. 【VirtualAPP 双开系列03】动态代理-hook系统服务(Java层)

    目录: 1. Hook 简述 2. VirtualAPP Java Hook 类图 3. VirtualAPP Java Hook 实例 (ActivityManagerService) 1. Hoo ...

  3. 【Java从入门到头秃专栏 8】语法篇(七) :反射 动态代理 注解

    目录 1 反射机制 2 反射的应用:动态代理 3 注解 1 反射机制 反射机制(Reflect Machanism),是指在程序运行期间借助Reflect API获取任何类的内部信息,并能直接操作对象 ...

  4. Android面试题目之(11) Class结构,动态代理和方法监控

    1.动态代理,这里有篇博文. http://blog.csdn.net/self_study/article/details/55050627 http://www.cnblogs.com/flyou ...

  5. 【Android架构师java原理详解】二;反射原理及动态代理模式

    前言: 本篇为Android架构师java原理专题二:反射原理及动态代理模式 大公司面试都要求我们有扎实的Java语言基础.而很多Android开发朋友这一块并不是很熟练,甚至半路初级底子很薄,这给我 ...

  6. 【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入具体的操作细节 | 创建 事件监听器 对应的 动态代理 | 动态代理的数据准备 | 创建调用处理程序 | 创建动态代理实例对象 )

    文章目录 前言 一.创建 事件监听器 对应的 动态代理 二.动态代理 数据准备 三.动态代理 调用处理程序 四.动态代理 实例对象创建 前言 Android 依赖注入的核心就是通过反射获取 类 / 方 ...

  7. java动态代理_Java 动态代理和依赖注入

    [注]本文译自:https://psamsotha.github.io/jersey/2015/12/16/dynamic-proxies-dependency-injection.html 本文将讨 ...

  8. 代理模式【介绍、静态代理、动态代理、入门、应用】

    代理介绍 代理(Proxy)是一种设计模式, 提供了对目标对象另外的访问方式:即通过代理访问目标对象. 这样好处: 可以在目标对象实现的基础上,增强额外的功能操作.(扩展目标对象的功能). 可以做到在 ...

  9. 动态代理 需求:一个超级明星技能跳舞、唱歌、拍电影、睡觉。明星经纪人公司接活动,赚的钱37分。请用写出代码。

    需求:一个超级明星技能跳舞.唱歌.拍电影.睡觉.明星经纪人公司接活动,赚的钱37分.请用写出代码. 这是一个典型的动态代理的问题,代码实现如下: 准备工作 我们需要建立2个类和一个接口. SuperS ...

最新文章

  1. DevExpress —— dxDataGrid
  2. Linux开启和关闭防火墙的方法
  3. leetcode题解8-盛最多水的容器
  4. Python群机器人发送城市天气情况
  5. 微软警告:警惕新型勒索软件 PonyFinal,已现身印度、伊朗和美国
  6. linux发布微软消息队列,消息队列RabbitMQ入门与5种模式详解
  7. 模拟Post登陆带验证码的网站
  8. 【Android】自定义倒计时弹框(Handler/Timer/RxJava/持续更新)
  9. 七夕前夜,地球和月亮搭起“鹊桥”
  10. 23岁的Python,这些年在编程语言排行榜上直线上升的原因是什么?很多人都不解
  11. 初中计算机室教学计划,初中信息技术教学计划
  12. java c/s网络聊天室,基于c-s网络聊天室报告.doc
  13. 什么是高质量的代码--整理的网上博文
  14. 直播报名|美团技术沙龙56期:美团计算机视觉与多媒体技术实践--ACM MM2020专场...
  15. Windows10与Ubuntu双系统安装记录
  16. android 筛选电影,Movie - 该看什么电影呢?这几个小程序或许能给你一个选择 - Android 应用 - 【最美应用】...
  17. HCI实验spss数据分析
  18. MQTT协议之连接和心跳
  19. C# 对JS解析AJX请求JSON并绑定到html页面的一些心得
  20. mysql 事务原理详解

热门文章

  1. 剑指offer.删除链表中重复的节点
  2. Thinkphp3.2.3的主从分离事务问题(坑!!!)
  3. Linux操作系统:文件和目录命令
  4. Dreamweaver里文档工具栏位置
  5. cjson源代码解读 (一)介绍
  6. Windows服务创建及安装
  7. 在dotnetnuke中去掉显示姓名中的空格
  8. python实时处理log文件脚本
  9. 旅游规划(双权连通图)
  10. 20155216 2016-2017-2 《Java程序设计》第三周学习总结