From 89503739fa37e6fbf055a7c34c9499d0769d8742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=8E=E6=9C=88=E4=BE=9D=E5=B8=8C?= <1093286982@qq.com> Date: Tue, 13 Feb 2024 00:23:07 +0800 Subject: [PATCH] =?UTF-8?q?[add]=20=E6=9A=B4=E5=8A=9B=E9=9A=90=E8=97=8F-de?= =?UTF-8?q?v?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Conflicts: # README.md --- README.md | 10 ++ .../main/java/com/lu/wxmask/bean/PointBean.kt | 20 +++ .../java/com/lu/wxmask/plugin/CommonPlugin.kt | 16 +- .../java/com/lu/wxmask/plugin/WXMaskPlugin.kt | 4 +- .../plugin/part/HideSearchListUIPluginPart.kt | 4 +- .../plugin/point/HideSearchListPoint.java | 51 ++++++ .../java/com/lu/wxmask/util/AppVersionUtil.kt | 6 +- .../java/com/lu/wxmask/util/CodeUtil.java | 162 ++++++++++++++++++ .../com/lu/wxmask/util/HookPointManager.kt | 97 +++++++++++ 9 files changed, 351 insertions(+), 19 deletions(-) create mode 100644 app/src/main/java/com/lu/wxmask/bean/PointBean.kt create mode 100644 app/src/main/java/com/lu/wxmask/plugin/point/HideSearchListPoint.java create mode 100644 app/src/main/java/com/lu/wxmask/util/CodeUtil.java create mode 100644 app/src/main/java/com/lu/wxmask/util/HookPointManager.kt diff --git a/README.md b/README.md index 3d1e494..8ff1927 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,9 @@ v1.13版本开始,默认隐藏App在桌面的图标。隐藏以后,打开模 8.0.41 (2441) 2023-09-06 8.0.42 (2460) 2023-09-22 8.0.43 (2480) 2023-11-06 +8.0.44 (2502) 2023-12-04 +8.0.45 (2521) 2024-01-02 + **PS.** - 仅支持上述版本,所有其他版本号以及32位版本未经测试,预计百分之九十九不可用 @@ -131,6 +134,13 @@ SHA1: 227E395C67A2C0B0BCC750E1A3C52F642B433441 8.0.43(2480):[https://dldir1.qq.com/weixin/android/weixin8043android2480_0x28002b35_arm64.apk](https://dldir1.qq.com/weixin/android/weixin8043android2480_0x28002b35_arm64.apk) SHA1: C46C85AF05130EDABCDBA8D487A5373ECE4AE6D0 +8.0.44(2502):[https://dldir1.qq.com/weixin/android/weixin8044android2502_0x28002c36_arm64.apk](https://dldir1.qq.com/weixin/android/weixin8044android2502_0x28002c36_arm64.apk) +SHA1: 38525994D6D69106CDB3D6F9F62B045CFF9CC4D5 + +8.0.45(2521):[https://dldir1.qq.com/weixin/android/weixin8045android2521_0x28002d34_arm64_1.apk](https://dldir1.qq.com/weixin/android/weixin8045android2521_0x28002d34_arm64_1.apk) +SHA1: F44F35663E2A2C3BF9EA671270D65902AB5727DA + + 推荐适配的最后两个版本,因为其他版本,作者自己不再使用 diff --git a/app/src/main/java/com/lu/wxmask/bean/PointBean.kt b/app/src/main/java/com/lu/wxmask/bean/PointBean.kt new file mode 100644 index 0000000..7b00adb --- /dev/null +++ b/app/src/main/java/com/lu/wxmask/bean/PointBean.kt @@ -0,0 +1,20 @@ +package com.lu.wxmask.bean + +import androidx.annotation.Keep +import org.json.JSONObject + +@Keep +class PointBean( + var featId: String, + var clazz: String, + var method: String +) { + companion object { + fun fromJson(value: JSONObject): PointBean { + val featId = value.optString("featId", null) + val clazz = value.optString("clazz", null) + val method = value.optString("method", null) + return PointBean(featId, clazz, method) + } + } +} diff --git a/app/src/main/java/com/lu/wxmask/plugin/CommonPlugin.kt b/app/src/main/java/com/lu/wxmask/plugin/CommonPlugin.kt index 5144561..4921c40 100644 --- a/app/src/main/java/com/lu/wxmask/plugin/CommonPlugin.kt +++ b/app/src/main/java/com/lu/wxmask/plugin/CommonPlugin.kt @@ -1,20 +1,8 @@ package com.lu.wxmask.plugin import android.content.Context -import android.view.MotionEvent -import android.view.View -import android.view.ViewGroup -import android.widget.ListAdapter -import android.widget.ListView -import com.lu.lposed.api2.XC_MethodHook2 -import com.lu.lposed.api2.XposedHelpers2 import com.lu.lposed.plugin.IPlugin -import com.lu.magic.util.log.LogUtil -import com.lu.magic.util.view.ChildDeepCheck -import com.lu.magic.util.view.SelfDeepCheck -import com.lu.magic.util.view.ViewUtil -import com.lu.wxmask.BuildConfig -import com.lu.wxmask.util.AppVersionUtil +import com.lu.wxmask.util.HookPointManager import de.robv.android.xposed.callbacks.XC_LoadPackage class CommonPlugin : IPlugin { @@ -42,5 +30,7 @@ class CommonPlugin : IPlugin { // } // } // ) + + HookPointManager.INSTANCE.init(context, lpparam) } } \ No newline at end of file diff --git a/app/src/main/java/com/lu/wxmask/plugin/WXMaskPlugin.kt b/app/src/main/java/com/lu/wxmask/plugin/WXMaskPlugin.kt index 104cf95..cc9d401 100644 --- a/app/src/main/java/com/lu/wxmask/plugin/WXMaskPlugin.kt +++ b/app/src/main/java/com/lu/wxmask/plugin/WXMaskPlugin.kt @@ -15,7 +15,7 @@ import java.util.concurrent.CopyOnWriteArraySet class WXMaskPlugin : IPlugin, ConfigSetObserver { lateinit var maskIdList: Array - private val hideSearchListPluginPart = HideSearchListUIPluginPart() + val hideSearchListPluginPart = HideSearchListUIPluginPart() private val enterChattingUIPluginPart = EnterChattingUIPluginPart() private val hideMainUIListPluginPart = HideMainUIListPluginPart() private val emptySingChatHistoryGalleryPluginPart = EmptySingChatHistoryGalleryPluginPart() @@ -55,7 +55,7 @@ class WXMaskPlugin : IPlugin, ConfigSetObserver { maskIdList = loadMaskIdList() hideMainUIListPluginPart.handleHook(context, lpparam) enterChattingUIPluginPart.handleHook(context, lpparam) - hideSearchListPluginPart.handleHook(context, lpparam) +// hideSearchListPluginPart.handleHook(context, lpparam) emptySingChatHistoryGalleryPluginPart.handleHook(context, lpparam) } diff --git a/app/src/main/java/com/lu/wxmask/plugin/part/HideSearchListUIPluginPart.kt b/app/src/main/java/com/lu/wxmask/plugin/part/HideSearchListUIPluginPart.kt index c2e6e45..8628738 100644 --- a/app/src/main/java/com/lu/wxmask/plugin/part/HideSearchListUIPluginPart.kt +++ b/app/src/main/java/com/lu/wxmask/plugin/part/HideSearchListUIPluginPart.kt @@ -42,7 +42,7 @@ class HideSearchListUIPluginPart : IPlugin { //hook getItem --> rename to h XposedHelpers2.findAndHookMethod("com.tencent.mm.plugin.fts.ui.a0", context.classLoader, - getItemMethod , + getItemMethod, java.lang.Integer.TYPE, object : XC_MethodHook2() { override fun afterHookedMethod(param: MethodHookParam) { @@ -265,7 +265,7 @@ class HideSearchListUIPluginPart : IPlugin { } - private fun needHideUserName2(param: XC_MethodHook.MethodHookParam, itemData: Any?): Boolean { + fun needHideUserName2(param: XC_MethodHook.MethodHookParam, itemData: Any?): Boolean { if (itemData == null) { return false } diff --git a/app/src/main/java/com/lu/wxmask/plugin/point/HideSearchListPoint.java b/app/src/main/java/com/lu/wxmask/plugin/point/HideSearchListPoint.java new file mode 100644 index 0000000..7f69181 --- /dev/null +++ b/app/src/main/java/com/lu/wxmask/plugin/point/HideSearchListPoint.java @@ -0,0 +1,51 @@ +package com.lu.wxmask.plugin.point; + +import android.content.Context; + +import com.lu.lposed.api2.XC_MethodHook2; +import com.lu.lposed.api2.XposedHelpers2; +import com.lu.lposed.plugin.IPlugin; +import com.lu.lposed.plugin.PluginProviders; +import com.lu.magic.util.log.LogUtil; +import com.lu.wxmask.ClazzN; +import com.lu.wxmask.bean.PointBean; +import com.lu.wxmask.plugin.WXMaskPlugin; +import com.lu.wxmask.plugin.part.HideSearchListUIPluginPart; + +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Method; + +import de.robv.android.xposed.callbacks.XC_LoadPackage; + +public class HideSearchListPoint implements IPlugin { + private PointBean mPointBean; + + public HideSearchListPoint(@NotNull PointBean it) { + mPointBean = it; + } + + @Override + public void handleHook(Context context, XC_LoadPackage.LoadPackageParam lpparam) { + if (mPointBean == null) { + return; + } + Method method = XposedHelpers2.findMethodExactIfExists(ClazzN.from(mPointBean.getClazz()), mPointBean.getMethod(), Integer.TYPE); + if (method == null) { + return; + } + XposedHelpers2.hookMethod(method, new XC_MethodHook2() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + WXMaskPlugin wxMaskPlugin = PluginProviders.from(WXMaskPlugin.class); + HideSearchListUIPluginPart pluginPart = wxMaskPlugin.getHideSearchListPluginPart(); + + if (pluginPart.needHideUserName2(param, param.getResult())) { + LogUtil.d("hide hahah", param.getResult()); + param.setResult(null); + } + } + + }); + } +} diff --git a/app/src/main/java/com/lu/wxmask/util/AppVersionUtil.kt b/app/src/main/java/com/lu/wxmask/util/AppVersionUtil.kt index 7500a91..30ab6d1 100644 --- a/app/src/main/java/com/lu/wxmask/util/AppVersionUtil.kt +++ b/app/src/main/java/com/lu/wxmask/util/AppVersionUtil.kt @@ -56,8 +56,10 @@ class AppVersionUtil { Constrant.WX_CODE_8_0_38, Constrant.WX_CODE_8_0_40, Constrant.WX_CODE_8_0_41, - Constrant.WX_CODE_8_0_42 -> true - + Constrant.WX_CODE_8_0_42, + Constrant.WX_CODE_8_0_43, + Constrant.WX_CODE_8_0_44, + Constrant.WX_CODE_8_0_45 -> true else -> false } } diff --git a/app/src/main/java/com/lu/wxmask/util/CodeUtil.java b/app/src/main/java/com/lu/wxmask/util/CodeUtil.java new file mode 100644 index 0000000..7f0b1f5 --- /dev/null +++ b/app/src/main/java/com/lu/wxmask/util/CodeUtil.java @@ -0,0 +1,162 @@ +package com.lu.wxmask.util; + +import android.content.Context; + +import com.lu.magic.util.ReflectUtil; +import com.lu.magic.util.function.Consumer; +import com.lu.magic.util.function.Predicate; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +import dalvik.system.DexFile; + +public class CodeUtil { + /** + * 获取应用程序下的所有Dex文件 + * + * @param packageCodePath 包路径 + * @return Set + */ + private static List getDexFileList(String packageCodePath) { + List dexFiles = new ArrayList<>(); + File dir = new File(packageCodePath).getParentFile(); + File[] files = dir.listFiles(); + for (File file : files) { + try { + String absolutePath = file.getAbsolutePath(); + if (!absolutePath.contains(".")) continue; + String suffix = absolutePath.substring(absolutePath.lastIndexOf(".")); + if (!suffix.equals(".apk")) continue; + DexFile dexFile = getDexFile(file.getAbsolutePath()); + if (dexFile == null) continue; + dexFiles.add(dexFile); + } catch (Exception e) { + e.printStackTrace(); + } + } + return dexFiles; + } + + /** + * 获取DexFile文件 + * + * @param path 路径 + * @return DexFile + */ + public static DexFile getDexFile(String path) { + try { + return new DexFile(path); + } catch (IOException e) { + return null; + } + } + + + public static List filterClass(Context context, Predicate func) { + List dexFiles = getDexFileListNewApi(context.getClassLoader()); + if (dexFiles.isEmpty()) { + //老方法需要重新读取,耗时大 + dexFiles = getDexFileList(context.getApplicationContext().getPackageCodePath()); + } + return filterClassInternal(dexFiles.iterator(), func); + } + + public static void eachClass(Context context, Consumer consumer) { + List dexFiles = getDexFileListNewApi(context.getClassLoader()); + if (dexFiles.isEmpty()) { + //老方法需要重新读取,耗时大 + dexFiles = getDexFileList(context.getApplicationContext().getPackageCodePath()); + } + eachClassInternal(dexFiles.iterator(), consumer); + } + + /** + * 读取类路径下的所有类 + * + * @param context 上下文 + * @param pkgName 包名 + * @return List + */ + public static List filterClass(Context context, String pkgName, boolean includeChildDir) { + List dexFiles = getDexFileListNewApi(context.getClassLoader()); + if (dexFiles.isEmpty()) { + //老方法需要重新读取,耗时大 + dexFiles = getDexFileList(context.getApplicationContext().getPackageCodePath()); + } + + return filterClassInternal(dexFiles.iterator(), currentClassPath -> { + if (!currentClassPath.startsWith(pkgName)) { + return false; + } + if (includeChildDir) { + return true; + } else { + return '.' == currentClassPath.charAt(pkgName.length()); + } + }); + } + + + public static List getDexFileListNewApi(ClassLoader classLoader) { + List result = new ArrayList<>(); + try { + Object pathList = ReflectUtil.getFieldValue(classLoader, classLoader.getClass().getSuperclass(), "pathList"); + Object[] dexElements = (Object[]) ReflectUtil.getFieldValue(pathList, "dexElements"); + for (Object dexElement : dexElements) { + DexFile dexFile = (DexFile) ReflectUtil.getFieldValue(dexElement, "dexFile"); + result.add(dexFile); + } + } catch (Exception e) { + e.printStackTrace(); + } + return result; + } + + + private static void eachClassInternal(Iterator dexFiles, Consumer func) { + + while (dexFiles.hasNext()) { + DexFile dexFile = dexFiles.next(); + if (dexFile == null) continue; + + Enumeration entries = dexFile.entries(); + while (entries.hasMoreElements()) { + try { + String currentClassPath = entries.nextElement(); + if (currentClassPath == null || currentClassPath.isEmpty()) { + continue; + } + func.accept(currentClassPath); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + } + + private static List filterClassInternal(Iterator dexFiles, Predicate predicateFunc) { + HashSet result = new HashSet<>(); + eachClassInternal(dexFiles, s -> { + if (predicateFunc.test(s)) { + result.add(s); + } + }); + return new ArrayList<>(result); + } + + + public static String getPackageName(String typeName) { + int index = typeName.lastIndexOf("."); + if (index == -1) { + return null; + } + return typeName.substring(0, index); + } +} diff --git a/app/src/main/java/com/lu/wxmask/util/HookPointManager.kt b/app/src/main/java/com/lu/wxmask/util/HookPointManager.kt new file mode 100644 index 0000000..5a681f9 --- /dev/null +++ b/app/src/main/java/com/lu/wxmask/util/HookPointManager.kt @@ -0,0 +1,97 @@ +package com.lu.wxmask.util + +import android.content.Context +import com.lu.lposed.api2.XposedHelpers2 +import com.lu.magic.util.GsonUtil +import com.lu.wxmask.bean.PointBean +import com.lu.wxmask.plugin.point.HideSearchListPoint +import com.lu.wxmask.util.ext.toJson +import de.robv.android.xposed.callbacks.XC_LoadPackage +import org.json.JSONObject +import java.lang.reflect.Modifier + +class HookPointManager { + companion object { + @JvmStatic + val INSTANCE = HookPointManager() + const val key_search_pkg_adapter = "com.tencent.mm.plugin.fts.ui:BaseAdapter" + val KEY_SEARCH_ADAPTER = "search_adapter" + } + + fun init(context: Context, lpparam: XC_LoadPackage.LoadPackageParam) { + val keyLocal = getCookPointKey() + + val hookList: HashSet = try { + GsonUtil.fromJson( + ConfigUtil.sp.getString(keyLocal, "{}"), + GsonUtil.getType(HashSet::class.java, PointBean::class.java) + ) + } catch (e: Exception) { + HashSet() + } + + if (hookList.isEmpty()) { + hookList.addAll(getHookPointByScanner(context)) + ConfigUtil.sp.edit().putString(keyLocal, hookList.toJson()).apply() + } + hookList.forEach { + when (it.featId) { + KEY_SEARCH_ADAPTER -> { + HideSearchListPoint(it).handleHook(context, lpparam) + } + } + } + + } + + private fun getCookPointKey(): String { + return "cookPoint-" + AppVersionUtil.getVersionCode() + } + +// private fun getHookPointByLocal(): HashSet { +// val pointJson = JSONObject(ConfigUtil.sp.getString(getCookPointKey(), "{}") ?: "{}") +// val result = HashSet() +// pointJson.keys().forEach { +// val value = pointJson[it] +// if (value is JSONObject) { +// result.add(PointBean.fromJson(value)) +// } +// } +// return result +// } + + + fun getHookPointByScanner(context: Context): HashSet { + val classLoader = context.classLoader + val result = HashSet() + CodeUtil.eachClass(context) { + val pkgName = CodeUtil.getPackageName(it) + when (pkgName) { + "com.tencent.mm.plugin.fts.ui" -> { + val clazz = XposedHelpers2.findClassIfExists(it, classLoader) + if (android.widget.BaseAdapter::class.java.isAssignableFrom(clazz)) { + XposedHelpers2.findMethodsByExactPredicate(clazz) { m -> + val ret = !arrayOf( + String::class.java, + Integer::class.java, + Byte::class.java, + Short::class.java, + Long::class.java, + Float::class.java, + Double::class.java, + ).contains(m.returnType) + if (ret && Modifier.isPublic(m.modifiers) && !Modifier.isAbstract(m.modifiers)) { + result.add(PointBean(KEY_SEARCH_ADAPTER, clazz.name, m.name)) + } + return@findMethodsByExactPredicate ret + } + + } + } + } + } + return result + } + + +}