目录
第一章: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);
}
}
执行效果: