T getResultOrThrowable() throws Throwable {
return (T) methodHookParam.getResultOrThrowable();
}
+ // --------- 观察调用 --------------
+
+ /**
+ * 观察方法是否被调用,如果被调用则打印一些日志。
+ */
+ final public void observeCall() {
+ if (logExpand == null) {
+ logExpand = new LogExpand(methodHookParam, mTag);
+ }
+ logExpand.detailedLogs();
+ }
+
// --------- 调用方法 --------------
/**
* 方法具体介绍请看实现类。
@@ -128,6 +151,7 @@ final public boolean setThisField(String name, Object value) {
}
// ---------- 设置自定义字段 --------------
+
final public boolean setThisAdditionalInstanceField(String key, Object value) {
return iDynamic.setAdditionalInstanceField(methodHookParam.thisObject, key, value);
}
diff --git a/app/src/main/java/com/hchen/hooktool/utils/BitmapUtils.java b/app/src/main/java/com/hchen/hooktool/utils/BitmapUtils.java
new file mode 100644
index 0000000..030951d
--- /dev/null
+++ b/app/src/main/java/com/hchen/hooktool/utils/BitmapUtils.java
@@ -0,0 +1,84 @@
+/*
+ * This file is part of HookTool.
+
+ * HookTool is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+
+ * Copyright (C) 2023-2024 HookTool Contributions
+ */
+package com.hchen.hooktool.utils;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.NinePatchDrawable;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Drawable 转 Bitmap
+ */
+public class BitmapUtils {
+ public static Bitmap drawableToBitmap(Drawable drawable) {
+ // 取 drawable 的长宽
+ int w = drawable.getIntrinsicWidth();
+ int h = drawable.getIntrinsicHeight();
+ // 取 drawable 的颜色格式
+ Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
+ // 建立对应 bitmap
+ Bitmap bitmap = Bitmap.createBitmap(w, h, config);
+ // 建立对应 bitmap 的画布
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, w, h);
+ // 把 drawable 内容画到画布中
+ drawable.draw(canvas);
+ return bitmap;
+ }
+
+ Bitmap drawable2Bitmap(Drawable drawable) {
+ if (drawable instanceof BitmapDrawable) {
+ return ((BitmapDrawable) drawable).getBitmap();
+ } else if (drawable instanceof NinePatchDrawable) {
+ Bitmap bitmap = Bitmap
+ .createBitmap(
+ drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight(),
+ drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
+ : Bitmap.Config.RGB_565);
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight());
+ drawable.draw(canvas);
+ return bitmap;
+ } else {
+ return null;
+ }
+ }
+
+ public static byte[] Bitmap2Bytes(Bitmap bm) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
+ return baos.toByteArray();
+ }
+
+ public static Bitmap Bytes2Bimap(byte[] b) {
+ if (b.length != 0) {
+ return BitmapFactory.decodeByteArray(b, 0, b.length);
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/app/src/main/java/com/hchen/hooktool/utils/ConvertHelper.java b/app/src/main/java/com/hchen/hooktool/utils/ConvertHelper.java
index 69de30b..06ad91d 100644
--- a/app/src/main/java/com/hchen/hooktool/utils/ConvertHelper.java
+++ b/app/src/main/java/com/hchen/hooktool/utils/ConvertHelper.java
@@ -28,6 +28,9 @@
* 本工具的快捷转换
*/
public class ConvertHelper {
+ /**
+ * @hide
+ */
protected ToolData data;
public ConvertHelper(ToolData data) {
diff --git a/app/src/main/java/com/hchen/hooktool/utils/FieldObserver.java b/app/src/main/java/com/hchen/hooktool/utils/FieldObserver.java
deleted file mode 100644
index 474e61f..0000000
--- a/app/src/main/java/com/hchen/hooktool/utils/FieldObserver.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * This file is part of HookTool.
-
- * HookTool is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
-
- * Copyright (C) 2023-2024 HookTool Contributions
- */
-package com.hchen.hooktool.utils;
-
-import static com.hchen.hooktool.log.AndroidLog.logE;
-import static com.hchen.hooktool.log.AndroidLog.logI;
-import static com.hchen.hooktool.log.AndroidLog.logW;
-
-import java.lang.reflect.Field;
-
-import de.robv.android.xposed.XposedHelpers;
-
-/**
- * 字段设置结果观察
- */
-public class FieldObserver {
- private final ToolData data;
-
- public FieldObserver(ToolData data) {
- this.data = data;
- }
-
- /**
- * 观察字段设置结果。
- */
- public void dynamicObserver(Field field, Object instance, Object value) {
- Object o = null;
- field.setAccessible(true);
- try {
- o = field.get(instance);
- } catch (IllegalAccessException e) {
- logE(data.getTAG(), e);
- return;
- }
- doInspect("", field.getName(), value, o);
- }
-
- public void dynamicObserver(Object instance, String name, Object value) {
- Object o = null;
- try {
- o = XposedHelpers.getObjectField(instance, name);
- } catch (Throwable e) {
- logE(data.getTAG(), e);
- return;
- }
- doInspect("", name, value, o);
- }
-
- /**
- * 观察静态字段设置结果。
- */
- public void staticObserver(Field field, Object value) {
- Object o = null;
- field.setAccessible(true);
- try {
- o = field.get(null);
- } catch (IllegalAccessException e) {
- logE(data.getTAG(), e);
- return;
- }
- doInspect("static", field.getName(), value, o);
- }
-
- public void staticObserver(Class> clazz, String name, Object value) {
- Object o = null;
- try {
- o = XposedHelpers.getStaticObjectField(clazz, name);
- } catch (Throwable e) {
- logE(data.getTAG(), e);
- return;
- }
- doInspect("static", name, value, o);
- }
-
- private void doInspect(String call, String name, Object value, Object o) {
- if (o == null && value == null) {
- logI(data.getTAG(), "set field: [" + name + "], value to null");
- return;
- }
- if ((o == null && value != null) || (o != null && value == null)) {
- logW(data.getTAG(), "failed set " + call + " field: [" + name + "]," +
- " value to: [" + value + "], now is: " + o);
- return;
- }
- if (o == value || o.equals(value)) {
- logI(data.getTAG(), "success set " + call + " field: [" + name + "]," +
- " value to: [" + value + "], now is: " + o);
- } else {
- logW(data.getTAG(), "failed set " + call + " field: [" + name + "]," +
- " value to: [" + value + "], now is: " + o);
- }
- }
-}
diff --git a/app/src/main/java/com/hchen/hooktool/utils/LogExpand.java b/app/src/main/java/com/hchen/hooktool/utils/LogExpand.java
index 1dac9b4..4f52d6d 100644
--- a/app/src/main/java/com/hchen/hooktool/utils/LogExpand.java
+++ b/app/src/main/java/com/hchen/hooktool/utils/LogExpand.java
@@ -33,16 +33,15 @@
* 日志增强
*/
public class LogExpand {
+ private final XC_MethodHook.MethodHookParam param;
+ private final String TAG;
private String methodName;
private String className;
- private XC_MethodHook.MethodHookParam param;
- private String[] filter = null;
- private final String TAG;
- public LogExpand(Member member, String TAG) {
+ public LogExpand(XC_MethodHook.MethodHookParam param, String TAG) {
this.TAG = TAG;
- this.filter = ToolData.filter;
- getName(member);
+ this.param = param;
+ getName(param.method);
}
public static String printStackTrace(Throwable t) {
@@ -57,17 +56,13 @@ private void getName(Member member) {
methodName = method.getName();
className = method.getDeclaringClass().getSimpleName();
} else if (member instanceof Constructor> constructor) {
- methodName = constructor.getDeclaringClass().getSimpleName();
+ methodName = "Constructor";
className = constructor.getDeclaringClass().getSimpleName();
} else {
logE(TAG, "unknown type! member: " + member);
}
}
- public void setParam(XC_MethodHook.MethodHookParam param) {
- this.param = param;
- }
-
public void detailedLogs() {
if (param.args == null || param.args.length == 0) {
logI(TAG, "class: [" + className + "], method: [" + methodName + "], param: { }");
@@ -76,22 +71,12 @@ public void detailedLogs() {
StringBuilder log = new StringBuilder();
for (int i = 0; i < param.args.length; i++) {
- log.append("(").append(i).append(")->").append("[").append(param.args[i]).append("]");
+ log.append("(").append(param.args[i].getClass().getSimpleName())
+ .append(")->").append("[").append(param.args[i]).append("]");
if (i < param.args.length - 1) {
log.append(", ");
}
}
- if (!isFilter())
- logI(TAG, "class: [" + className + "], method: [" + methodName + "], param: {" + log + "}");
- }
-
- private boolean isFilter() {
- if (filter == null) return false;
- for (String s : filter) {
- if (s.equals(TAG)) {
- return true;
- }
- }
- return false;
+ logI(TAG, "class: [" + className + "], method: [" + methodName + "], param: {" + log + "}");
}
}
diff --git a/app/src/main/java/com/hchen/hooktool/utils/PackagesUtils.java b/app/src/main/java/com/hchen/hooktool/utils/PackagesUtils.java
new file mode 100644
index 0000000..f57fd54
--- /dev/null
+++ b/app/src/main/java/com/hchen/hooktool/utils/PackagesUtils.java
@@ -0,0 +1,280 @@
+/*
+ * This file is part of HookTool.
+
+ * HookTool is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+
+ * Copyright (C) 2023-2024 HookTool Contributions
+ */
+package com.hchen.hooktool.utils;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+import com.hchen.hooktool.data.AppData;
+import com.hchen.hooktool.log.AndroidLog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 软件包实用程序
+ */
+public class PackagesUtils {
+ private static final String TAG = "PackagesUtils";
+
+ public static boolean isUninstall(String pkg) {
+ return isUninstall(context(), pkg);
+ }
+
+ /**
+ * 判断目标包名应用是否已经被卸载。
+ */
+ public static boolean isUninstall(Context context, String pkg) {
+ if (context == null) return false;
+ PackageManager packageManager = context.getPackageManager();
+ try {
+ packageManager.getPackageInfo(pkg, PackageManager.MATCH_ALL);
+ return false;
+ } catch (PackageManager.NameNotFoundException e) {
+ AndroidLog.logW(TAG,
+ "didn't find this app on the machine, it may have been uninstalled! pkg: " + pkg, e);
+ return true;
+ }
+ }
+
+ public static boolean isDisable(String pkg) {
+ return isDisable(context(), pkg);
+ }
+
+ /**
+ * 获取包名应用是否被禁用。
+ */
+ public static boolean isDisable(Context context, String pkg) {
+ if (context == null) return false;
+ PackageManager packageManager = context.getPackageManager();
+ try {
+ ApplicationInfo result = packageManager.getApplicationInfo(pkg, 0);
+ if (!result.enabled) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ return false;
+ }
+
+ public static boolean isHidden(String pkg) {
+ return isHidden(context(), pkg);
+ }
+
+ /**
+ * 获取包名应用是否被 Hidden,一般来说被隐藏视为未安装,可以使用 isUninstall() 来判断。
+ */
+ public static boolean isHidden(Context context, String pkg) {
+ try {
+ if (context == null) return false;
+ PackageManager packageManager = context.getPackageManager();
+ packageManager.getApplicationInfo(pkg, 0);
+ return false;
+ } catch (PackageManager.NameNotFoundException e) {
+ return true;
+ }
+ }
+
+ /**
+ * 通过自定义代码获取 Package 信息,
+ * 支持
+ *
+ * PackageInfo
+ *
+ * ResolveInfo
+ *
+ * ActivityInfo
+ *
+ * ApplicationInfo
+ *
+ * ProviderInfo
+ *
+ * 类型的返回值.
+ * 返回使用 return new ArrayList<>(XX); 包裹。
+ *
+ * @param iCode 需要执行的代码
+ * @return ListAppData 包含各种应用详细信息
+ * @see #addAppData(Parcelable, PackageManager)
+ */
+ public static List getPackagesByCode(ICode iCode) {
+ List appDataList = new ArrayList<>();
+ Context context = context();
+ if (context == null) return appDataList;
+ PackageManager packageManager = context.getPackageManager();
+ Parcelable parcelable = iCode.getPackageCode(packageManager);
+ List packageCodeList = iCode.getPackageCodeList(packageManager);
+ try {
+ if (parcelable != null) {
+ appDataList.add(addAppData(parcelable, packageManager));
+ } else {
+ if (packageCodeList != null) {
+ for (Parcelable get : packageCodeList) {
+ appDataList.add(addAppData(get, packageManager));
+ }
+ }
+ }
+ } catch (Throwable e) {
+ AndroidLog.logE(TAG, "failed to get package via code!", e);
+ }
+ return appDataList;
+ }
+
+ @SuppressLint("QueryPermissionsNeeded")
+ public static List getInstalledPackages(int flag) {
+ List appDataList = new ArrayList<>();
+ Context context = context();
+ if (context == null) return appDataList;
+ try {
+ PackageManager packageManager = context.getPackageManager();
+ List packageInfos = packageManager.getInstalledPackages(flag);
+ for (PackageInfo packageInfo : packageInfos) {
+ appDataList.add(addAppData(packageInfo, packageManager));
+ }
+ return appDataList;
+ } catch (Throwable e) {
+ AndroidLog.logE(TAG, "failed to get the list of installed apps via flag!", e);
+ }
+ return appDataList;
+ }
+
+ private static AppData addAppData(Parcelable parcelable, PackageManager pm) throws Throwable {
+ AppData appData = new AppData();
+ try {
+ if (parcelable instanceof PackageInfo) {
+ appData.icon = BitmapUtils.drawableToBitmap(((PackageInfo) parcelable).applicationInfo.loadIcon(pm));
+ appData.label = ((PackageInfo) parcelable).applicationInfo.loadLabel(pm).toString();
+ appData.packageName = ((PackageInfo) parcelable).applicationInfo.packageName;
+ appData.versionName = ((PackageInfo) parcelable).versionName;
+ appData.versionCode = Long.toString(((PackageInfo) parcelable).getLongVersionCode());
+ appData.isSystemApp = isSystem(((PackageInfo) parcelable).applicationInfo);
+ appData.enabled = ((PackageInfo) parcelable).applicationInfo.enabled;
+ appData.user = getUserId(((PackageInfo) parcelable).applicationInfo.uid);
+ appData.uid = ((PackageInfo) parcelable).applicationInfo.uid;
+ } else if (parcelable instanceof ResolveInfo) {
+ appData.icon = BitmapUtils.drawableToBitmap(aboutResolveInfo((ResolveInfo) parcelable).applicationInfo.loadIcon(pm));
+ appData.label = aboutResolveInfo((ResolveInfo) parcelable).applicationInfo.loadLabel(pm).toString();
+ appData.packageName = aboutResolveInfo((ResolveInfo) parcelable).applicationInfo.packageName;
+ appData.isSystemApp = isSystem(aboutResolveInfo((ResolveInfo) parcelable).applicationInfo);
+ appData.enabled = aboutResolveInfo((ResolveInfo) parcelable).applicationInfo.enabled;
+ appData.user = getUserId(aboutResolveInfo((ResolveInfo) parcelable).applicationInfo.uid);
+ appData.uid = aboutResolveInfo((ResolveInfo) parcelable).applicationInfo.uid;
+ } else if (parcelable instanceof ActivityInfo) {
+ appData.icon = BitmapUtils.drawableToBitmap(((ActivityInfo) parcelable).applicationInfo.loadIcon(pm));
+ appData.label = ((ActivityInfo) parcelable).applicationInfo.loadLabel(pm).toString();
+ appData.packageName = ((ActivityInfo) parcelable).applicationInfo.packageName;
+ appData.isSystemApp = isSystem(((ActivityInfo) parcelable).applicationInfo);
+ appData.activityName = ((ActivityInfo) parcelable).name;
+ appData.enabled = ((ActivityInfo) parcelable).applicationInfo.enabled;
+ appData.user = getUserId(((ActivityInfo) parcelable).applicationInfo.uid);
+ appData.uid = ((ActivityInfo) parcelable).applicationInfo.uid;
+ } else if (parcelable instanceof ApplicationInfo) {
+ appData.icon = BitmapUtils.drawableToBitmap(((ApplicationInfo) parcelable).loadIcon(pm));
+ appData.label = ((ApplicationInfo) parcelable).loadLabel(pm).toString();
+ appData.packageName = ((ApplicationInfo) parcelable).packageName;
+ appData.isSystemApp = isSystem(((ApplicationInfo) parcelable));
+ appData.enabled = ((ApplicationInfo) parcelable).enabled;
+ appData.user = getUserId(((ApplicationInfo) parcelable).uid);
+ appData.uid = ((ApplicationInfo) parcelable).uid;
+ } else if (parcelable instanceof ProviderInfo) {
+ appData.icon = BitmapUtils.drawableToBitmap(((ProviderInfo) parcelable).applicationInfo.loadIcon(pm));
+ appData.label = ((ProviderInfo) parcelable).applicationInfo.loadLabel(pm).toString();
+ appData.packageName = ((ProviderInfo) parcelable).applicationInfo.packageName;
+ appData.isSystemApp = isSystem(((ProviderInfo) parcelable).applicationInfo);
+ appData.enabled = ((ProviderInfo) parcelable).applicationInfo.enabled;
+ appData.user = getUserId(((ProviderInfo) parcelable).applicationInfo.uid);
+ appData.uid = ((ProviderInfo) parcelable).applicationInfo.uid;
+ }
+ } catch (Throwable e) {
+ throw new Throwable("error in obtaining application information: " + parcelable, e);
+ }
+ return appData;
+ }
+
+ private static ComponentInfo aboutResolveInfo(ResolveInfo resolveInfo) {
+ if (resolveInfo.activityInfo != null) return resolveInfo.activityInfo;
+ if (resolveInfo.serviceInfo != null) return resolveInfo.serviceInfo;
+ if (resolveInfo.providerInfo != null) return resolveInfo.providerInfo;
+ return null;
+ }
+
+ /**
+ * 根据 uid 获取 user id。
+ */
+ public static int getUserId(int uid) {
+ return InvokeUtils.callStaticMethod(UserHandle.class, "getUserId", new Class[]{int.class}, uid);
+ }
+
+ /**
+ * 可用于判断是否是系统应用。
+ * 如果 app 为 null 则固定返回 false,请注意检查 app 是否为 null。
+ *
+ * @return 返回 true 代表是系统应用
+ */
+ public static boolean isSystem(ApplicationInfo app) {
+ if (Objects.isNull(app)) {
+ AndroidLog.logE(TAG, "isSystem app is null, will return false");
+ return false;
+ }
+ if (app.uid < 10000) {
+ return true;
+ }
+ return (app.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0;
+ }
+
+ private static Context context() {
+ try {
+ return getContext();
+ } catch (Throwable e) {
+ AndroidLog.logE(TAG, e);
+ return null;
+ }
+ }
+
+ private static Context getContext() throws Throwable {
+ Context context = ContextUtils.getContext(ContextUtils.FLAG_CURRENT_APP);
+ if (context == null) {
+ context = ContextUtils.getContext(ContextUtils.FlAG_ONLY_ANDROID);
+ }
+ if (context == null) {
+ throw new Throwable("context is null");
+ }
+ return context;
+ }
+
+ public interface ICode {
+ default Parcelable getPackageCode(PackageManager pm) {
+ return null;
+ }
+
+ default List getPackageCodeList(PackageManager pm) {
+ return null;
+ }
+ }
+}
diff --git a/app/src/main/java/com/hchen/hooktool/utils/SystemSDK.java b/app/src/main/java/com/hchen/hooktool/utils/SystemSDK.java
index 6ed3c14..92fb05d 100644
--- a/app/src/main/java/com/hchen/hooktool/utils/SystemSDK.java
+++ b/app/src/main/java/com/hchen/hooktool/utils/SystemSDK.java
@@ -33,16 +33,6 @@
* 此类用于获取设备基本信息
*/
public class SystemSDK {
- private static String TAG;
-
- public SystemSDK() {
-
- }
-
- public SystemSDK(String tag) {
- TAG = tag;
- }
-
public static String getSystemVersionIncremental() {
return getProp("ro.system.build.version.incremental");
}
diff --git a/app/src/main/java/com/hchen/hooktool/utils/ToolData.java b/app/src/main/java/com/hchen/hooktool/utils/ToolData.java
index 3088e4b..5084567 100644
--- a/app/src/main/java/com/hchen/hooktool/utils/ToolData.java
+++ b/app/src/main/java/com/hchen/hooktool/utils/ToolData.java
@@ -30,6 +30,7 @@
/**
* 这是本工具的读写数据类,请不要继承重写。
+ * @hide
*/
public class ToolData {
// HCInit
@@ -37,9 +38,7 @@ public class ToolData {
public static int mInitLogLevel = HCInit.LOG_I;
public static String spareTag = "Unknown";
public static String modulePackageName = null;
- public static boolean useLogExpand = false;
- public static String[] filter = new String[]{};
- public static boolean useFieldObserver = false;
+ public static boolean autoObserveCall = false;
public static IXposedHookZygoteInit.StartupParam startupParam = null;
// HCHook
public static XC_LoadPackage.LoadPackageParam lpparam = null;