diff --git a/app/src/main/java/me/firesun/wechat/enhancement/SettingsActivity.java b/app/src/main/java/me/firesun/wechat/enhancement/SettingsActivity.java index 066caec..475d1b0 100644 --- a/app/src/main/java/me/firesun/wechat/enhancement/SettingsActivity.java +++ b/app/src/main/java/me/firesun/wechat/enhancement/SettingsActivity.java @@ -4,23 +4,31 @@ import android.annotation.TargetApi; import android.app.Application; import android.app.FragmentManager; +import android.app.ProgressDialog; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceFragment; import android.support.v7.app.AppCompatActivity; import android.widget.Toast; +import com.google.gson.Gson; + import java.lang.reflect.Method; +import dalvik.system.PathClassLoader; import me.firesun.wechat.enhancement.util.HookParams; +import me.firesun.wechat.enhancement.util.SearchClasses; public class SettingsActivity extends AppCompatActivity { @@ -93,6 +101,61 @@ public boolean onPreferenceClick(Preference pref) { return true; } }); + + Preference generate = findPreference("generate"); + generate.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference pref) { + final Context context = getApplication(); + if (context == null) { + return false; + } + final PackageManager packageManager = context.getPackageManager(); + if (packageManager == null) { + return false; + } + + final ProgressDialog dialog = new ProgressDialog(getActivity()); + dialog.setCancelable(false); + dialog.setMessage(getResources().getString(R.string.generating)); + dialog.show(); + + new Thread(new Runnable() { + @Override + public void run() { + boolean success = false; + try { + PackageInfo packageInfo = packageManager.getPackageInfo(HookParams.WECHAT_PACKAGE_NAME, 0); + String wechatApk = packageInfo.applicationInfo.sourceDir; + PathClassLoader wxClassLoader = new PathClassLoader(wechatApk, ClassLoader.getSystemClassLoader()); + SearchClasses.generateConfig(wechatApk, wxClassLoader, packageInfo.versionName); + + String config = new Gson().toJson(HookParams.getInstance()); + SharedPreferences.Editor editor = context.getSharedPreferences(HookParams.WECHAT_ENHANCEMENT_CONFIG_NAME, Context.MODE_WORLD_READABLE).edit(); + editor.clear(); + editor.putString("params", config); + editor.commit(); + + success = true; + + } catch (Throwable e) { + e.printStackTrace(); + } + + final String msg = getResources().getString(success ? R.string.generate_success : R.string.generate_failed); + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + dialog.dismiss(); + Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show(); + } + }); + } + }, "generate-config").start(); + + return true; + } + }); } private Application getApplication() { diff --git a/app/src/main/java/me/firesun/wechat/enhancement/util/ReflectionUtil.java b/app/src/main/java/me/firesun/wechat/enhancement/util/ReflectionUtil.java index 2daab53..d9f5c3a 100644 --- a/app/src/main/java/me/firesun/wechat/enhancement/util/ReflectionUtil.java +++ b/app/src/main/java/me/firesun/wechat/enhancement/util/ReflectionUtil.java @@ -1,6 +1,8 @@ package me.firesun.wechat.enhancement.util; +import android.util.Log; + import net.dongliu.apk.parser.bean.DexClass; import java.lang.reflect.Field; @@ -8,23 +10,46 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; -import de.robv.android.xposed.XposedHelpers; - -import static de.robv.android.xposed.XposedBridge.log; +import de.robv.android.xposed.XposedBridge; public final class ReflectionUtil { private static final Map classesCache = new HashMap(); + private static boolean xposedExist; + + static { + try { + Class.forName("de.robv.android.xposed.XposedBridge"); + xposedExist = true; + } catch (ClassNotFoundException e) { + xposedExist = false; + } + } + + public static void log(String msg) { + if (msg == null) { + return; + } + + if (xposedExist) { + XposedBridge.log(msg); + } else { + Log.i("Xposed", "[WechatEnhancement] " + msg); + } + } + public static Method findMethodsByExactParameters(Class clazz, Class returnType, Class... parameterTypes) { if (clazz == null) { return null; } - List list = Arrays.asList(XposedHelpers.findMethodsByExactParameters(clazz, returnType, (Class[]) Arrays.copyOf(parameterTypes, parameterTypes.length))); + // List list = Arrays.asList(XposedHelpers.findMethodsByExactParameters(clazz, returnType, (Class[]) Arrays.copyOf(parameterTypes, parameterTypes.length))); + List list = Arrays.asList(findMethodsByExactParametersInner(clazz, returnType, (Class[]) Arrays.copyOf(parameterTypes, parameterTypes.length))); if (list.isEmpty()) return null; else if (list.size() > 1) { @@ -74,10 +99,79 @@ public static Classes findClassesFromPackage(ClassLoader loader, List cl return cs; } + private static Map methodCache = new HashMap<>(); + + private static String getParametersString(Class... clazzes) { + StringBuilder sb = new StringBuilder("("); + boolean first = true; + for (Class clazz : clazzes) { + if (first) + first = false; + else + sb.append(","); + + if (clazz != null) + sb.append(clazz.getCanonicalName()); + else + sb.append("null"); + } + sb.append(")"); + return sb.toString(); + } + + public static Method findMethodExact(Class clazz, String methodName, Class... parameterTypes) { + String fullMethodName = clazz.getName() + '#' + methodName + getParametersString(parameterTypes) + "#exact"; + + if (methodCache.containsKey(fullMethodName)) { + Method method = methodCache.get(fullMethodName); + if (method == null) + throw new NoSuchMethodError(fullMethodName); + return method; + } + + try { + Method method = clazz.getDeclaredMethod(methodName, parameterTypes); + method.setAccessible(true); + methodCache.put(fullMethodName, method); + return method; + } catch (NoSuchMethodException e) { + methodCache.put(fullMethodName, null); + throw new NoSuchMethodError(fullMethodName); + } + } + + public static Method[] findMethodsByExactParametersInner(Class clazz, Class returnType, Class... parameterTypes) { + List result = new LinkedList<>(); + for (Method method : clazz.getDeclaredMethods()) { + if (returnType != null && returnType != method.getReturnType()) + continue; + + Class[] methodParameterTypes = method.getParameterTypes(); + if (parameterTypes.length != methodParameterTypes.length) + continue; + + boolean match = true; + for (int i = 0; i < parameterTypes.length; i++) { + if (parameterTypes[i] != methodParameterTypes[i]) { + match = false; + break; + } + } + + if (!match) + continue; + + method.setAccessible(true); + result.add(method); + } + return result.toArray(new Method[result.size()]); + } + public static final Method findMethodExactIfExists(Class clazz, String methodName, Class... parameterTypes) { Method method = null; try { - method = XposedHelpers.findMethodExact(clazz, methodName, (Class[]) Arrays.copyOf(parameterTypes, parameterTypes.length)); + // method = XposedHelpers.findMethodExact(clazz, methodName, (Class[]) Arrays.copyOf(parameterTypes, parameterTypes.length)); + method = findMethodExact(clazz, methodName, (Class[]) Arrays.copyOf(parameterTypes, parameterTypes.length)); } catch (Error | Exception e) { } return method; @@ -86,7 +180,7 @@ public static final Method findMethodExactIfExists(Class clazz, String methodNam public static final Class findClassIfExists(String className, ClassLoader classLoader) { Class c = null; try { - c = XposedHelpers.findClass(className, classLoader); + c = Class.forName(className, false, classLoader); } catch (Error | Exception e) { } return c; diff --git a/app/src/main/java/me/firesun/wechat/enhancement/util/SearchClasses.java b/app/src/main/java/me/firesun/wechat/enhancement/util/SearchClasses.java index 44fe8f3..9d18d58 100644 --- a/app/src/main/java/me/firesun/wechat/enhancement/util/SearchClasses.java +++ b/app/src/main/java/me/firesun/wechat/enhancement/util/SearchClasses.java @@ -20,18 +20,26 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage; import me.firesun.wechat.enhancement.Main; -import static de.robv.android.xposed.XposedBridge.log; +import static me.firesun.wechat.enhancement.util.ReflectionUtil.log; public class SearchClasses { private static List wxClasses = new ArrayList<>(); private static XSharedPreferences preferencesInstance = null; - public static void init(Context context, XC_LoadPackage.LoadPackageParam lpparam, String versionName) { + public static void init(Context context, XC_LoadPackage.LoadPackageParam lparam, String versionName) { - if (loadConfig(lpparam, versionName)) + if (loadConfig(lparam, versionName)) return; log("failed to load config, start finding..."); + + generateConfig(lparam.appInfo.sourceDir, lparam.classLoader, versionName); + + saveConfig(context); + } + + public static void generateConfig(String wechatApk, ClassLoader classLoader, String versionName) { + HookParams hp = HookParams.getInstance(); hp.versionName = versionName; hp.versionCode = HookParams.VERSION_CODE; @@ -49,8 +57,11 @@ public static void init(Context context, XC_LoadPackage.LoadPackageParam lpparam ApkFile apkFile = null; try { - apkFile = new ApkFile(lpparam.appInfo.sourceDir); + apkFile = new ApkFile(wechatApk); DexClass[] dexClasses = apkFile.getDexClasses(); + + wxClasses.clear(); + for (int i = 0; i < dexClasses.length; i++) { wxClasses.add(ReflectionUtil.getClassName(dexClasses[i])); } @@ -66,14 +77,14 @@ public static void init(Context context, XC_LoadPackage.LoadPackageParam lpparam //LuckMoney try { - Class ReceiveUIParamNameClass = ReflectionUtil.findClassesFromPackage(lpparam.classLoader, wxClasses, "com.tencent.mm", 1) + Class ReceiveUIParamNameClass = ReflectionUtil.findClassesFromPackage(classLoader, wxClasses, "com.tencent.mm", 1) .filterByMethod(String.class, "getInfo") .filterByMethod(int.class, "getType") .filterByMethod(void.class, "reset") .firstOrNull(); hp.ReceiveUIParamNameClassName = ReceiveUIParamNameClass.getName(); - Class RequestCallerClass = ReflectionUtil.findClassesFromPackage(lpparam.classLoader, wxClasses, "com.tencent.mm", 1) + Class RequestCallerClass = ReflectionUtil.findClassesFromPackage(classLoader, wxClasses, "com.tencent.mm", 1) .filterByField("foreground", "boolean") .filterByMethod(void.class, int.class, String.class, int.class, boolean.class) .filterByMethod(void.class, "cancel", int.class) @@ -85,7 +96,7 @@ public static void init(Context context, XC_LoadPackage.LoadPackageParam lpparam void.class, RequestCallerClass, int.class) .getName(); - Class NetworkRequestClass = ReflectionUtil.findClassesFromPackage(lpparam.classLoader, wxClasses, "com.tencent.mm", 1) + Class NetworkRequestClass = ReflectionUtil.findClassesFromPackage(classLoader, wxClasses, "com.tencent.mm", 1) .filterByMethod(void.class, "unhold") .filterByMethod(RequestCallerClass) .firstOrNull(); @@ -95,7 +106,7 @@ public static void init(Context context, XC_LoadPackage.LoadPackageParam lpparam RequestCallerClass) .getName(); - Class ReceiveLuckyMoneyRequestClass = ReflectionUtil.findClassesFromPackage(lpparam.classLoader, wxClasses, "com.tencent.mm.plugin.luckymoney", 1) + Class ReceiveLuckyMoneyRequestClass = ReflectionUtil.findClassesFromPackage(classLoader, wxClasses, "com.tencent.mm.plugin.luckymoney", 1) .filterByField("msgType", "int") .filterByMethod(void.class, int.class, String.class, JSONObject.class) .firstOrNull(); @@ -105,7 +116,7 @@ public static void init(Context context, XC_LoadPackage.LoadPackageParam lpparam void.class, int.class, String.class, JSONObject.class) .getName(); - hp.LuckyMoneyRequestClassName = ReflectionUtil.findClassesFromPackage(lpparam.classLoader, wxClasses, "com.tencent.mm.plugin.luckymoney", 1) + hp.LuckyMoneyRequestClassName = ReflectionUtil.findClassesFromPackage(classLoader, wxClasses, "com.tencent.mm.plugin.luckymoney", 1) .filterByField("talker", "java.lang.String") .filterByMethod(void.class, int.class, String.class, JSONObject.class) .filterByMethod(int.class, "getType") @@ -113,7 +124,7 @@ public static void init(Context context, XC_LoadPackage.LoadPackageParam lpparam .firstOrNull() .getName(); - hp.GetTransferRequestClassName = ReflectionUtil.findClassesFromPackage(lpparam.classLoader, wxClasses, "com.tencent.mm.plugin.remittance", 1) + hp.GetTransferRequestClassName = ReflectionUtil.findClassesFromPackage(classLoader, wxClasses, "com.tencent.mm.plugin.remittance", 1) .filterByField("java.lang.String") .filterByNoField("int") @@ -122,7 +133,7 @@ public static void init(Context context, XC_LoadPackage.LoadPackageParam lpparam .firstOrNull() .getName(); - Class LuckyMoneyReceiveUIClass = XposedHelpers.findClass(hp.LuckyMoneyReceiveUIClassName, lpparam.classLoader); + Class LuckyMoneyReceiveUIClass = ReflectionUtil.findClassIfExists(hp.LuckyMoneyReceiveUIClassName, classLoader); hp.ReceiveUIMethod = ReflectionUtil.findMethodsByExactParameters(LuckyMoneyReceiveUIClass, boolean.class, int.class, int.class, String.class, ReceiveUIParamNameClass) .getName(); @@ -134,7 +145,7 @@ public static void init(Context context, XC_LoadPackage.LoadPackageParam lpparam //ADBlock try { - Class XMLParserClass = ReflectionUtil.findClassesFromPackage(lpparam.classLoader, wxClasses, "com.tencent.mm.sdk.platformtools", 0) + Class XMLParserClass = ReflectionUtil.findClassesFromPackage(classLoader, wxClasses, "com.tencent.mm.sdk.platformtools", 0) .filterByMethod(Map.class, String.class, String.class) .firstOrNull(); hp.XMLParserClassName = XMLParserClass.getName(); @@ -148,7 +159,7 @@ public static void init(Context context, XC_LoadPackage.LoadPackageParam lpparam //AntiRevoke try { - ReflectionUtil.Classes storageClasses = ReflectionUtil.findClassesFromPackage(lpparam.classLoader, wxClasses, "com.tencent.mm.storage", 0); + ReflectionUtil.Classes storageClasses = ReflectionUtil.findClassesFromPackage(classLoader, wxClasses, "com.tencent.mm.storage", 0); Class MsgInfoClass = storageClasses .filterByMethod(boolean.class, "isSystem") .firstOrNull(); @@ -175,12 +186,12 @@ public static void init(Context context, XC_LoadPackage.LoadPackageParam lpparam //Photo Limits try { - Class SelectConversationUIClass = XposedHelpers.findClass(hp.SelectConversationUIClassName, lpparam.classLoader); + Class SelectConversationUIClass = XposedHelpers.findClass(hp.SelectConversationUIClassName, classLoader); hp.SelectConversationUICheckLimitMethod = ReflectionUtil.findMethodsByExactParameters(SelectConversationUIClass, boolean.class, boolean.class) .getName(); - hp.ContactInfoClassName = ReflectionUtil.findClassesFromPackage(lpparam.classLoader, wxClasses, "com.tencent.mm.storage", 0) + hp.ContactInfoClassName = ReflectionUtil.findClassesFromPackage(classLoader, wxClasses, "com.tencent.mm.storage", 0) .filterByMethod(String.class, "getCityCode") .filterByMethod(String.class, "getCountryCode") .firstOrNull() @@ -189,8 +200,6 @@ public static void init(Context context, XC_LoadPackage.LoadPackageParam lpparam } catch (Error | Exception e) { log("Search Photo Limits Classes Failed!"); } - - saveConfig(context); } private static int getVersionNum(String version) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index da64b66..a830cee 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,7 +12,7 @@ 延迟0毫秒拆开红包 设置 红包设置 - 其他设置 + 设置 防撤回设置 增强功能 过滤朋友圈广告 @@ -22,6 +22,7 @@ 朋友圈防删除 显示图标 尝试修复插件 + 生成本机配置 修复完成,请重启微信 关于 作者 @@ -32,4 +33,7 @@ 请输入关键字, 以,分隔 包含微信ID不抢 请输入微信ID, 以,分隔 + 生成配置成功 + 生成配置失败 + 正在生成配置… diff --git a/app/src/main/res/xml/pref_setting.xml b/app/src/main/res/xml/pref_setting.xml index a59aaac..a163f6e 100644 --- a/app/src/main/res/xml/pref_setting.xml +++ b/app/src/main/res/xml/pref_setting.xml @@ -1,4 +1,18 @@ + + + + + + + - - - - +