/* QNotified - An Xposed module for QQ/TIM * Copyright (C) 2019-2020 [email protected] * https://github.com/cinit/QNotified * * This software is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * <https://www.gnu.org/licenses/>. */ package nil.nadph.qnotified.util; import android.annotation.SuppressLint; import android.app.Activity; import android.app.Application; import android.content.Context; import android.content.DialogInterface; import android.content.pm.PackageInfo; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.view.View; import android.widget.Toast; import com.tencent.mobileqq.app.QQAppInterface; import dalvik.system.DexFile; import de.robv.android.xposed.XposedBridge; import mqq.app.AppRuntime; import nil.nadph.qnotified.BuildConfig; import nil.nadph.qnotified.SyncUtils; import nil.nadph.qnotified.config.ConfigItems; import nil.nadph.qnotified.config.ConfigManager; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.*; import java.text.SimpleDateFormat; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import static nil.nadph.qnotified.util.Initiator.load; @SuppressWarnings({"unchecked", "rawtypes"}) @SuppressLint("SimpleDateFormat") public class Utils { public static final String QN_VERSION_NAME = BuildConfig.VERSION_NAME; public static final int QN_VERSION_CODE = BuildConfig.VERSION_CODE; public static final boolean __REMOVE_PREVIOUS_CACHE = true; public static final String PACKAGE_NAME_QQ = "com.tencent.mobileqq"; public static final String PACKAGE_NAME_QQ_INTERNATIONAL = "com.tencent.mobileqqi"; public static final String PACKAGE_NAME_QQ_LITE = "com.tencent.qqlite"; public static final String PACKAGE_NAME_TIM = "com.tencent.tim"; public static final String PACKAGE_NAME_SELF = "nil.nadph.qnotified"; public static final String PACKAGE_NAME_XPOSED_INSTALLER = "de.robv.android.xposed.installer"; public static final int TOAST_TYPE_INFO = 0; public static final int TOAST_TYPE_ERROR = 1; public static final int TOAST_TYPE_SUCCESS = 2; public static boolean DEBUG = true; public static boolean ENABLE_DUMP_LOG = false; private static Handler mHandler; private Utils() { throw new AssertionError("No instance for you!"); } @SuppressWarnings("ResultOfMethodCallIgnored") @Nullable public static String getActiveModuleVersion() { Math.sqrt(1); Math.random(); Math.expm1(0.001); //Just make the function longer,so that it will get hooked by Epic return null; } public static void runOnUiThread(Runnable r) { if (mHandler == null) mHandler = new Handler(Looper.getMainLooper()); mHandler.post(r); } public static Application getApplication() { Field f; try { Class clz = load("com/tencent/common/app/BaseApplicationImpl"); f = hasField(clz, "sApplication"); if (f == null) return (Application) sget_object(clz, "a", clz); else return (Application) f.get(null); } catch (Exception e) { log(e); //noinspection UnnecessaryInitCause throw (AssertionError) new AssertionError("FATAL: Utils.getApplication() failure!").initCause(e); } } public static boolean isCallingFrom(String classname) { StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); for (StackTraceElement element : stackTraceElements) { if (element.toString().contains(classname)) { return true; } } return false; } public static boolean isCallingFromEither(String... classname) { StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); for (StackTraceElement element : stackTraceElements) { for (String name : classname) { if (element.toString().contains(name)) { return true; } } } return false; } public static Object getTroopManager() throws Exception { Object mTroopManager = invoke_virtual(getQQAppInterface(), "getManager", 51, int.class); if (!mTroopManager.getClass().getName().contains("TroopManager")) mTroopManager = invoke_virtual(getQQAppInterface(), "getManager", 52, int.class); return mTroopManager; } public static Object getQQMessageFacade() throws Exception { QQAppInterface app = getQQAppInterface(); return invoke_virtual_any(app, Initiator._QQMessageFacade()); } public static Object getManager(int index) throws Exception { return invoke_virtual(getQQAppInterface(), "getManager", index, int.class); } public static PackageInfo getHostInfo(Context context) { try { return context.getPackageManager().getPackageInfo(context.getPackageName(), 0); } catch (Throwable e) { Log.e("Utils", "Can not get PackageInfo!"); throw new AssertionError("Can not get PackageInfo!"); } } public static String paramsTypesToString(Class... c) { if (c == null) return null; if (c.length == 0) return "()"; StringBuilder sb = new StringBuilder("("); for (int i = 0; i < c.length; i++) { sb.append(c[i] == null ? "[null]" : c[i].getName()); if (i != c.length - 1) { sb.append(","); } } sb.append(")"); return sb.toString(); } /*public static Object invoke_virtual(Object obj,String method,Object...args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IllegalArgumentException{ Class clazz=obj.getClass(); Method m=findMethodByArgs(clazz,method,args); m.setAccessible(true); return m.invoke(obj,args); }*/ public static int getHostVersionCode() { PackageInfo pi = getHostInfo(getApplication()); return pi.versionCode; } public static long getLongAccountUin() { try { AppRuntime rt = getAppRuntime(); loge("getLongAccountUin/E getAppRuntime == null"); if (rt == null) return -1; return (long) invoke_virtual(rt, "getLongAccountUin"); } catch (Exception e) { log(e); } return -1; } public static void ref_setText(View obj, CharSequence str) { try { Method m = obj.getClass().getMethod("setText", CharSequence.class); m.setAccessible(true); m.invoke(obj, str); } catch (Exception e) { log(e); } } public static View.OnClickListener getOnClickListener(View v) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { return getOnClickListenerV14(v); } else { return getOnClickListenerV(v); } } @Deprecated public static Object invoke_virtual_any(Object obj, Object... argsTypesAndReturnType) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IllegalArgumentException { Class clazz = obj.getClass(); int argc = argsTypesAndReturnType.length / 2; Class[] argt = new Class[argc]; Object[] argv = new Object[argc]; Class returnType = null; if (argc * 2 + 1 == argsTypesAndReturnType.length) returnType = (Class) argsTypesAndReturnType[argsTypesAndReturnType.length - 1]; int i, ii; Method[] m; Method method = null; Class[] _argt; for (i = 0; i < argc; i++) { argt[i] = (Class) argsTypesAndReturnType[argc + i]; argv[i] = argsTypesAndReturnType[i]; } loop_main: do { m = clazz.getDeclaredMethods(); loop: for (i = 0; i < m.length; i++) { _argt = m[i].getParameterTypes(); if (_argt.length == argt.length) { for (ii = 0; ii < argt.length; ii++) { if (!argt[ii].equals(_argt[ii])) continue loop; } if (returnType != null && !returnType.equals(m[i].getReturnType())) continue; if (method == null) { method = m[i]; //here we go through this class } else { throw new NoSuchMethodException("Multiple methods found for __attribute__((any))" + paramsTypesToString(argt) + " in " + obj.getClass().getName()); } } } } while (method == null && !Object.class.equals(clazz = clazz.getSuperclass())); if (method == null) throw new NoSuchMethodException("__attribute__((a))" + paramsTypesToString(argt) + " in " + obj.getClass().getName()); method.setAccessible(true); return method.invoke(obj, argv); } @Deprecated public static Object invoke_static_any(Class<?> clazz, Object... argsTypesAndReturnType) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IllegalArgumentException { int argc = argsTypesAndReturnType.length / 2; Class[] argt = new Class[argc]; Object[] argv = new Object[argc]; Class returnType = null; if (argc * 2 + 1 == argsTypesAndReturnType.length) returnType = (Class) argsTypesAndReturnType[argsTypesAndReturnType.length - 1]; int i, ii; Method[] m; Method method = null; Class[] _argt; for (i = 0; i < argc; i++) { argt[i] = (Class) argsTypesAndReturnType[argc + i]; argv[i] = argsTypesAndReturnType[i]; } loop_main: do { m = clazz.getDeclaredMethods(); loop: for (i = 0; i < m.length; i++) { _argt = m[i].getParameterTypes(); if (_argt.length == argt.length) { for (ii = 0; ii < argt.length; ii++) { if (!argt[ii].equals(_argt[ii])) continue loop; } if (returnType != null && !returnType.equals(m[i].getReturnType())) continue; if (method == null) { method = m[i]; //here we go through this class } else { throw new NoSuchMethodException("Multiple methods found for __attribute__((any))" + paramsTypesToString(argt) + " in " + clazz.getName()); } } } } while (method == null && !Object.class.equals(clazz = clazz.getSuperclass())); if (method == null) throw new NoSuchMethodException("__attribute__((a))" + paramsTypesToString(argt) + " in " + clazz.getName()); method.setAccessible(true); return method.invoke(null, argv); } @Deprecated public static Object invoke_virtual_declared_modifier_any(Object obj, int requiredMask, int excludedMask, Object... argsTypesAndReturnType) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IllegalArgumentException { Class clazz = obj.getClass(); int argc = argsTypesAndReturnType.length / 2; Class[] argt = new Class[argc]; Object[] argv = new Object[argc]; Class returnType = null; if (argc * 2 + 1 == argsTypesAndReturnType.length) returnType = (Class) argsTypesAndReturnType[argsTypesAndReturnType.length - 1]; int i, ii; Method[] m; Method method = null; Class[] _argt; for (i = 0; i < argc; i++) { argt[i] = (Class) argsTypesAndReturnType[argc + i]; argv[i] = argsTypesAndReturnType[i]; } m = clazz.getDeclaredMethods(); loop: for (i = 0; i < m.length; i++) { _argt = m[i].getParameterTypes(); if (_argt.length == argt.length) { for (ii = 0; ii < argt.length; ii++) { if (!argt[ii].equals(_argt[ii])) continue loop; } if (returnType != null && !returnType.equals(m[i].getReturnType())) continue; if ((m[i].getModifiers() & requiredMask) != requiredMask) continue; if ((m[i].getModifiers() & excludedMask) != 0) continue; if (method == null) { method = m[i]; //here we go through this class } else { throw new NoSuchMethodException("Multiple methods found for __attribute__((any))" + paramsTypesToString(argt) + " in " + obj.getClass().getName()); } } } if (method == null) throw new NoSuchMethodException("__attribute__((a))" + paramsTypesToString(argt) + " in " + obj.getClass().getName()); method.setAccessible(true); return method.invoke(obj, argv); } /** * DO NOT USE, it's fragile * instance methods are counted, both public/private, * static methods are EXCLUDED, * count from 0 * * @param obj * @param ordinal the ordinal of instance method meeting the signature * @param expected how many instance methods are expected there * @param argsTypesAndReturnType * @return * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException * @throws IllegalArgumentException */ @Deprecated public static Object invoke_virtual_declared_ordinal(Object obj, int ordinal, int expected, boolean strict, Object... argsTypesAndReturnType) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IllegalArgumentException { Class clazz = obj.getClass(); int argc = argsTypesAndReturnType.length / 2; Class[] argt = new Class[argc]; Object[] argv = new Object[argc]; Class returnType = null; if (argc * 2 + 1 == argsTypesAndReturnType.length) returnType = (Class) argsTypesAndReturnType[argsTypesAndReturnType.length - 1]; int i, ii; Method[] m; Method[] candidates = new Method[expected]; int count = 0; Class[] _argt; for (i = 0; i < argc; i++) { argt[i] = (Class) argsTypesAndReturnType[argc + i]; argv[i] = argsTypesAndReturnType[i]; } m = clazz.getDeclaredMethods(); loop: for (i = 0; i < m.length; i++) { _argt = m[i].getParameterTypes(); if (_argt.length == argt.length) { for (ii = 0; ii < argt.length; ii++) { if (!argt[ii].equals(_argt[ii])) continue loop; } if (returnType != null && !returnType.equals(m[i].getReturnType())) continue; if (Modifier.isStatic(m[i].getModifiers())) continue; if (count < expected) { candidates[count++] = m[i]; } else { if (!strict) break; throw new NoSuchMethodException("More methods than expected(" + expected + ") at " + paramsTypesToString(argt) + " in " + obj.getClass().getName()); } } } if (strict && count != expected) { throw new NoSuchMethodException("Less methods(" + count + ") than expected(" + expected + ") at " + paramsTypesToString(argt) + " in " + obj.getClass().getName()); } Arrays.sort(candidates, new Comparator<Method>() { @Override public int compare(Method o1, Method o2) { if (o1 == null && o2 == null) return 0; if (o1 == null) return 1; if (o2 == null) return -1; return strcmp(o1.getName(), o2.getName()); } }); candidates[ordinal].setAccessible(true); return candidates[ordinal].invoke(obj, argv); } /** * DO NOT USE, it's fragile * instance methods are counted, both public/private, * static methods are EXCLUDED, * count from 0 * * @param obj * @param fixed which class * @param ordinal the ordinal of instance method meeting the signature * @param expected how many instance methods are expected there * @param argsTypesAndReturnType * @return * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException * @throws IllegalArgumentException */ @Deprecated public static Object invoke_virtual_declared_fixed_modifier_ordinal(Object obj, int requiredMask, int excludedMask, Class fixed, int ordinal, int expected, boolean strict, Object... argsTypesAndReturnType) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IllegalArgumentException { int argc = argsTypesAndReturnType.length / 2; Class[] argt = new Class[argc]; Object[] argv = new Object[argc]; Class returnType = null; if (argc * 2 + 1 == argsTypesAndReturnType.length) returnType = (Class) argsTypesAndReturnType[argsTypesAndReturnType.length - 1]; int i, ii; Method[] m; Method[] candidates = new Method[expected]; int count = 0; Class[] _argt; for (i = 0; i < argc; i++) { argt[i] = (Class) argsTypesAndReturnType[argc + i]; argv[i] = argsTypesAndReturnType[i]; } m = fixed.getDeclaredMethods(); loop: for (i = 0; i < m.length; i++) { _argt = m[i].getParameterTypes(); if (_argt.length == argt.length) { for (ii = 0; ii < argt.length; ii++) { if (!argt[ii].equals(_argt[ii])) continue loop; } if (returnType != null && !returnType.equals(m[i].getReturnType())) continue; if (Modifier.isStatic(m[i].getModifiers())) continue; if ((m[i].getModifiers() & requiredMask) != requiredMask) continue; if ((m[i].getModifiers() & excludedMask) != 0) continue; if (count < expected) { candidates[count++] = m[i]; } else { if (!strict) break; throw new NoSuchMethodException("More methods than expected(" + expected + ") at " + paramsTypesToString(argt) + " in " + obj.getClass().getName()); } } } if (strict && count != expected) { throw new NoSuchMethodException("Less methods(" + count + ") than expected(" + expected + ") at " + paramsTypesToString(argt) + " in " + obj.getClass().getName()); } Arrays.sort(candidates, new Comparator<Method>() { @Override public int compare(Method o1, Method o2) { if (o1 == null && o2 == null) return 0; if (o1 == null) return 1; if (o2 == null) return -1; return strcmp(o1.getName(), o2.getName()); } }); candidates[ordinal].setAccessible(true); return candidates[ordinal].invoke(obj, argv); } /** * DO NOT USE, it's fragile * instance methods are counted, both public/private, * static methods are EXCLUDED, * count from 0 * * @param obj * @param ordinal the ordinal of instance method meeting the signature * @param expected how many instance methods are expected there * @param argsTypesAndReturnType * @return * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException * @throws IllegalArgumentException */ @Deprecated public static Object invoke_virtual_declared_ordinal_modifier(Object obj, int ordinal, int expected, boolean strict, int requiredMask, int excludedMask, Object... argsTypesAndReturnType) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IllegalArgumentException { Class clazz = obj.getClass(); int argc = argsTypesAndReturnType.length / 2; Class[] argt = new Class[argc]; Object[] argv = new Object[argc]; Class returnType = null; if (argc * 2 + 1 == argsTypesAndReturnType.length) returnType = (Class) argsTypesAndReturnType[argsTypesAndReturnType.length - 1]; int i, ii; Method[] m; Method[] candidates = new Method[expected]; int count = 0; Class[] _argt; for (i = 0; i < argc; i++) { argt[i] = (Class) argsTypesAndReturnType[argc + i]; argv[i] = argsTypesAndReturnType[i]; } m = clazz.getDeclaredMethods(); loop: for (i = 0; i < m.length; i++) { _argt = m[i].getParameterTypes(); if (_argt.length == argt.length) { for (ii = 0; ii < argt.length; ii++) { if (!argt[ii].equals(_argt[ii])) continue loop; } if (returnType != null && !returnType.equals(m[i].getReturnType())) continue; if (Modifier.isStatic(m[i].getModifiers())) continue; if ((m[i].getModifiers() & requiredMask) != requiredMask) continue; if ((m[i].getModifiers() & excludedMask) != 0) continue; if (count < expected) { candidates[count++] = m[i]; } else { if (!strict) break; throw new NoSuchMethodException("More methods than expected(" + expected + ") at " + paramsTypesToString(argt) + " in " + obj.getClass().getName()); } } } if (strict && count != expected) { throw new NoSuchMethodException("Less methods(" + count + ") than expected(" + expected + ") at " + paramsTypesToString(argt) + " in " + obj.getClass().getName()); } Arrays.sort(candidates, new Comparator<Method>() { @Override public int compare(Method o1, Method o2) { if (o1 == null && o2 == null) return 0; if (o1 == null) return 1; if (o2 == null) return -1; return strcmp(o1.getName(), o2.getName()); } }); candidates[ordinal].setAccessible(true); return candidates[ordinal].invoke(obj, argv); } /** * DO NOT USE, it's fragile * static methods are counted, both public/private, * instance methods are EXCLUDED, * count from 0 * * @param clazz * @param ordinal the ordinal of instance method meeting the signature * @param expected how many instance methods are expected there * @param argsTypesAndReturnType * @return * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException * @throws IllegalArgumentException */ @Deprecated public static Object invoke_static_declared_ordinal_modifier(Class clazz, int ordinal, int expected, boolean strict, int requiredMask, int excludedMask, Object... argsTypesAndReturnType) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IllegalArgumentException { int argc = argsTypesAndReturnType.length / 2; Class[] argt = new Class[argc]; Object[] argv = new Object[argc]; Class returnType = null; if (argc * 2 + 1 == argsTypesAndReturnType.length) returnType = (Class) argsTypesAndReturnType[argsTypesAndReturnType.length - 1]; int i, ii; Method[] m; Method[] candidates = new Method[expected]; int count = 0; Class[] _argt; for (i = 0; i < argc; i++) { argt[i] = (Class) argsTypesAndReturnType[argc + i]; argv[i] = argsTypesAndReturnType[i]; } m = clazz.getDeclaredMethods(); loop: for (i = 0; i < m.length; i++) { _argt = m[i].getParameterTypes(); if (_argt.length == argt.length) { for (ii = 0; ii < argt.length; ii++) { if (!argt[ii].equals(_argt[ii])) continue loop; } if (returnType != null && !returnType.equals(m[i].getReturnType())) continue; if (!Modifier.isStatic(m[i].getModifiers())) continue; if ((m[i].getModifiers() & requiredMask) != requiredMask) continue; if ((m[i].getModifiers() & excludedMask) != 0) continue; if (count < expected) { candidates[count++] = m[i]; } else { if (!strict) break; throw new NoSuchMethodException("More methods than expected(" + expected + ") at " + paramsTypesToString(argt) + " in " + clazz.getName()); } } } if (strict && count != expected) { throw new NoSuchMethodException("Less methods(" + count + ") than expected(" + expected + ") at " + paramsTypesToString(argt) + " in " + clazz.getName()); } Arrays.sort(candidates, new Comparator<Method>() { @Override public int compare(Method o1, Method o2) { if (o1 == null && o2 == null) return 0; if (o1 == null) return 1; if (o2 == null) return -1; return strcmp(o1.getName(), o2.getName()); } }); candidates[ordinal].setAccessible(true); return candidates[ordinal].invoke(null, argv); } /** * DO NOT USE, it's fragile * static methods are counted, both public/private, * instance methods are EXCLUDED, * count from 0 * * @param clazz * @param ordinal the ordinal of instance method meeting the signature * @param expected how many instance methods are expected there * @param argsTypesAndReturnType * @return * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException * @throws IllegalArgumentException */ @Deprecated public static Object invoke_static_declared_ordinal(Class clazz, int ordinal, int expected, boolean strict, Object... argsTypesAndReturnType) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IllegalArgumentException { int argc = argsTypesAndReturnType.length / 2; Class[] argt = new Class[argc]; Object[] argv = new Object[argc]; Class returnType = null; if (argc * 2 + 1 == argsTypesAndReturnType.length) returnType = (Class) argsTypesAndReturnType[argsTypesAndReturnType.length - 1]; int i, ii; Method[] m; Method[] candidates = new Method[expected]; int count = 0; Class[] _argt; for (i = 0; i < argc; i++) { argt[i] = (Class) argsTypesAndReturnType[argc + i]; argv[i] = argsTypesAndReturnType[i]; } m = clazz.getDeclaredMethods(); loop: for (i = 0; i < m.length; i++) { _argt = m[i].getParameterTypes(); if (_argt.length == argt.length) { for (ii = 0; ii < argt.length; ii++) { if (!argt[ii].equals(_argt[ii])) continue loop; } if (returnType != null && !returnType.equals(m[i].getReturnType())) continue; if (!Modifier.isStatic(m[i].getModifiers())) continue; if (count < expected) { candidates[count++] = m[i]; } else { if (!strict) break; throw new NoSuchMethodException("More methods than expected(" + expected + ") at " + paramsTypesToString(argt) + " in " + clazz.getName()); } } } if (strict && count != expected) { throw new NoSuchMethodException("Less methods(" + count + ") than expected(" + expected + ") at " + paramsTypesToString(argt) + " in " + clazz.getName()); } Arrays.sort(candidates, new Comparator<Method>() { @Override public int compare(Method o1, Method o2) { if (o1 == null && o2 == null) return 0; if (o1 == null) return 1; if (o2 == null) return -1; return strcmp(o1.getName(), o2.getName()); } }); candidates[ordinal].setAccessible(true); return candidates[ordinal].invoke(null, argv); } public static Object invoke_virtual(Object obj, String name, Object... argsTypesAndReturnType) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IllegalArgumentException { Class clazz = obj.getClass(); int argc = argsTypesAndReturnType.length / 2; Class[] argt = new Class[argc]; Object[] argv = new Object[argc]; Class returnType = null; if (argc * 2 + 1 == argsTypesAndReturnType.length) returnType = (Class) argsTypesAndReturnType[argsTypesAndReturnType.length - 1]; int i, ii; Method[] m; Method method = null; Class[] _argt; for (i = 0; i < argc; i++) { argt[i] = (Class) argsTypesAndReturnType[argc + i]; argv[i] = argsTypesAndReturnType[i]; } loop_main: do { m = clazz.getDeclaredMethods(); loop: for (i = 0; i < m.length; i++) { if (m[i].getName().equals(name)) { _argt = m[i].getParameterTypes(); if (_argt.length == argt.length) { for (ii = 0; ii < argt.length; ii++) { if (!argt[ii].equals(_argt[ii])) continue loop; } if (returnType != null && !returnType.equals(m[i].getReturnType())) continue; method = m[i]; break loop_main; } } } } while (!Object.class.equals(clazz = clazz.getSuperclass())); if (method == null) throw new NoSuchMethodException(name + paramsTypesToString(argt) + " in " + obj.getClass().getName()); method.setAccessible(true); return method.invoke(obj, argv); } public static Method hasMethod(Object obj, String name, Object... argsTypesAndReturnType) throws IllegalArgumentException { Class clazz; if (obj == null) throw new NullPointerException("obj/clazz == null"); if (obj instanceof Class) clazz = (Class) obj; else clazz = obj.getClass(); int argc = argsTypesAndReturnType.length / 2; Class[] argt = new Class[argc]; Object[] argv = new Object[argc]; Class returnType = null; if (argc * 2 + 1 == argsTypesAndReturnType.length) returnType = (Class) argsTypesAndReturnType[argsTypesAndReturnType.length - 1]; int i, ii; Method[] m; Method method = null; Class[] _argt; for (i = 0; i < argc; i++) { argt[i] = (Class) argsTypesAndReturnType[argc + i]; argv[i] = argsTypesAndReturnType[i]; } loop_main: do { m = clazz.getDeclaredMethods(); loop: for (i = 0; i < m.length; i++) { if (m[i].getName().equals(name)) { _argt = m[i].getParameterTypes(); if (_argt.length == argt.length) { for (ii = 0; ii < argt.length; ii++) { if (!argt[ii].equals(_argt[ii])) continue loop; } if (returnType != null && !returnType.equals(m[i].getReturnType())) continue; method = m[i]; break loop_main; } } } } while (!Object.class.equals(clazz = clazz.getSuperclass())); if (method != null) method.setAccessible(true); return method; } public static Object invoke_virtual_original(Object obj, String name, Object... argsTypesAndReturnType) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IllegalArgumentException { Class clazz = obj.getClass(); int argc = argsTypesAndReturnType.length / 2; Class[] argt = new Class[argc]; Object[] argv = new Object[argc]; Class returnType = null; if (argc * 2 + 1 == argsTypesAndReturnType.length) returnType = (Class) argsTypesAndReturnType[argsTypesAndReturnType.length - 1]; int i, ii; Method[] m; Method method = null; Class[] _argt; for (i = 0; i < argc; i++) { argt[i] = (Class) argsTypesAndReturnType[argc + i]; argv[i] = argsTypesAndReturnType[i]; } loop_main: do { m = clazz.getDeclaredMethods(); loop: for (i = 0; i < m.length; i++) { if (m[i].getName().equals(name)) { _argt = m[i].getParameterTypes(); if (_argt.length == argt.length) { for (ii = 0; ii < argt.length; ii++) { if (!argt[ii].equals(_argt[ii])) continue loop; } if (returnType != null && !returnType.equals(m[i].getReturnType())) continue; method = m[i]; break loop_main; } } } } while (!Object.class.equals(clazz = clazz.getSuperclass())); if (method == null) throw new NoSuchMethodException(name + " in " + obj.getClass().getName()); method.setAccessible(true); Object ret; boolean needPatch = false; try { ret = XposedBridge.invokeOriginalMethod(method, obj, argv); return ret; } catch (IllegalStateException e) { //For SandHook-EdXp: Method not hooked. needPatch = true; } catch (InvocationTargetException e) { //For TaiChi Throwable cause = e.getCause(); if (cause instanceof NullPointerException) { String tr = android.util.Log.getStackTraceString(cause); if (tr.indexOf("ExposedBridge.invokeOriginalMethod") != 0 || tr.indexOf("ExposedBridge.invokeOriginalMethod") != 0) needPatch = true; } if (!needPatch) throw e; } //here needPatch is always true ret = method.invoke(obj, argv); return ret; } public static Object invoke_static(Class staticClass, String name, Object... argsTypesAndReturnType) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IllegalArgumentException { Class clazz = staticClass; int argc = argsTypesAndReturnType.length / 2; Class[] argt = new Class[argc]; Object[] argv = new Object[argc]; Class returnType = null; if (argc * 2 + 1 == argsTypesAndReturnType.length) returnType = (Class) argsTypesAndReturnType[argsTypesAndReturnType.length - 1]; int i, ii; Method[] m; Method method = null; Class[] _argt; for (i = 0; i < argc; i++) { argt[i] = (Class) argsTypesAndReturnType[argc + i]; argv[i] = argsTypesAndReturnType[i]; } loop_main: do { m = clazz.getDeclaredMethods(); loop: for (i = 0; i < m.length; i++) { if (m[i].getName().equals(name)) { _argt = m[i].getParameterTypes(); if (_argt.length == argt.length) { for (ii = 0; ii < argt.length; ii++) { if (!argt[ii].equals(_argt[ii])) continue loop; } if (returnType != null && !returnType.equals(m[i].getReturnType())) continue; method = m[i]; break loop_main; } } } } while (!Object.class.equals(clazz = clazz.getSuperclass())); if (method == null) throw new NoSuchMethodException(name + paramsTypesToString(argt)); method.setAccessible(true); return method.invoke(null, argv); } public static Object new_instance(Class clazz, Object... argsAndTypes) throws InvocationTargetException, InstantiationException, NoSuchMethodException { int argc = argsAndTypes.length / 2; Class[] argt = new Class[argc]; Object[] argv = new Object[argc]; int i; Constructor m; for (i = 0; i < argc; i++) { argt[i] = (Class) argsAndTypes[argc + i]; argv[i] = argsAndTypes[i]; } m = clazz.getDeclaredConstructor(argt); m.setAccessible(true); try { return m.newInstance(argv); } catch (IllegalAccessException e) { log(e); //should NOT happen throw new RuntimeException(e); } } public static QQAppInterface getQQAppInterface() { return (QQAppInterface) getAppRuntime(); } /* public static Method findMethodByArgs(Class mclazz,String name,Object...argv)throws NoSuchMethodException{ Method ret=null; Method[] m;? int i=0,ii=0; Class clazz=mclazz; Class argt[]; do{ m=clazz.getDeclaredMethods(); loop:for(i=0;i<m.length;i++){ if(m[i].getName().equals(name)){ argt=m[i].getParameterTypes(); if(argt.length==argv.length){ for(ii=0;ii<argt.length;ii++){ if( argv[ii]==null&&argt[ii].isPrimitive())continue loop; if( } } } } }while(!Object.class.equals(clazz=clazz.getSuperclass())); throw new NoSuchMethodException(name+"@"+mclazz); }*/ public static Object getMobileQQService() { return iget_object_or_null(getQQAppInterface(), "a", load("com/tencent/mobileqq/service/MobileQQService")); } public static String get_RGB(int color) { int r = 0xff & (color >> 16); int g = 0xff & (color >> 8); int b = 0xff & color; return "#" + byteStr(r) + byteStr(g) + byteStr(b); } public static String byteStr(int i) { String ret = Integer.toHexString(i); if (ret.length() == 1) return "0" + ret; else return ret; } //Used for APIs lower than ICS (API 14) private static View.OnClickListener getOnClickListenerV(View view) { View.OnClickListener retrievedListener = null; String viewStr = "android.view.View"; Field field; try { //noinspection JavaReflectionMemberAccess field = Class.forName(viewStr).getDeclaredField("mOnClickListener"); retrievedListener = (View.OnClickListener) field.get(view); } catch (NoSuchFieldException ex) { log("Reflection: No Such Field."); } catch (IllegalAccessException ex) { log("Reflection: Illegal Access."); } catch (ClassNotFoundException ex) { log("Reflection: Class Not Found."); } return retrievedListener; } //Used for new ListenerInfo class structure used beginning with API 14 (ICS) @SuppressWarnings("JavaReflectionMemberAccess") @SuppressLint("PrivateApi") private static View.OnClickListener getOnClickListenerV14(View view) { View.OnClickListener retrievedListener = null; String viewStr = "android.view.View"; String lInfoStr = "android.view.View$ListenerInfo"; try { Field listenerField = Class.forName(viewStr).getDeclaredField("mListenerInfo"); Object listenerInfo = null; if (listenerField != null) { listenerField.setAccessible(true); listenerInfo = listenerField.get(view); } Field clickListenerField = Class.forName(lInfoStr).getDeclaredField("mOnClickListener"); if (clickListenerField != null && listenerInfo != null) { retrievedListener = (View.OnClickListener) clickListenerField.get(listenerInfo); } } catch (NoSuchFieldException ex) { log("Reflection: No Such Field."); } catch (IllegalAccessException ex) { log("Reflection: Illegal Access."); } catch (ClassNotFoundException ex) { log("Reflection: Class Not Found."); } return retrievedListener; } public static <T> T _obj_clone(T obj) { try { Class<T> clazz = (Class<T>) obj.getClass(); T ret = clazz.newInstance(); Field[] f; int i; while (!Object.class.equals(clazz)) { f = clazz.getDeclaredFields(); for (i = 0; i < f.length; i++) { f[i].setAccessible(true); f[i].set(ret, f[i].get(obj)); } clazz = (Class<T>) clazz.getSuperclass(); } return ret; } catch (Throwable e) { log("CLONE : " + e.toString()); } return null; } public static <T extends View> T _view_clone(T obj) { try { Class<T> clazz = (Class<T>) obj.getClass(); T ret = clazz.getConstructor(Context.class).newInstance(obj.getContext()); Field[] f; int i; while (!Object.class.equals(clazz)) { f = clazz.getDeclaredFields(); for (i = 0; i < f.length; i++) { f[i].setAccessible(true); f[i].set(ret, f[i].get(obj)); } clazz = (Class<T>) clazz.getSuperclass(); } return ret; } catch (Throwable e) { log("CLONE : " + e.toString()); } return null; } public static Object sget_object(Class clazz, String name) { return sget_object(clazz, name, null); } public static Object sget_object(Class clazz, String name, Class type) { try { Field f = findField(clazz, type, name); f.setAccessible(true); return f.get(null); } catch (Exception e) { log(e); } return null; } public static Object iget_object_or_null(Object obj, String name) { return iget_object_or_null(obj, name, null); } public static <T> T iget_object_or_null(Object obj, String name, Class<T> type) { Class clazz = obj.getClass(); try { Field f = findField(clazz, type, name); f.setAccessible(true); return (T) f.get(obj); } catch (Exception e) { } return null; } public static void iput_object(Object obj, String name, Object value) { iput_object(obj, name, null, value); } public static void iput_object(Object obj, String name, Class type, Object value) { Class clazz = obj.getClass(); try { Field f = findField(clazz, type, name); f.setAccessible(true); f.set(obj, value); } catch (Exception e) { log(e); } } public static void sput_object(Class clz, String name, Object value) { sput_object(clz, name, null, value); } public static void sput_object(Class clazz, String name, Class type, Object value) { try { Field f = findField(clazz, type, name); f.setAccessible(true); f.set(null, value); } catch (Exception e) { log(e); } } public static String getCurrentNickname() { try { return (String) invoke_virtual(getQQAppInterface(), "getCurrentNickname"); } catch (Throwable e) { log(e); } return null; } public static AppRuntime getAppRuntime() { Object baseApplicationImpl = getApplication(); try { Method m; m = hasMethod(baseApplicationImpl, "getRuntime"); if (m == null) return (AppRuntime) invoke_virtual(baseApplicationImpl, "a", load("mqq/app/AppRuntime")); else return (AppRuntime) m.invoke(baseApplicationImpl); } catch (Exception e) { throw new AssertionError(e); } } public static String getAccount() { Object rt = getAppRuntime(); try { return (String) invoke_virtual(rt, "getAccount"); } catch (Exception e) { log(e); return null; } } public static Object getFriendListHandler() { return getBusinessHandler(1); } public static Object getBusinessHandler(int type) { try { Class cl_bh = load("com/tencent/mobileqq/app/BusinessHandler"); if (cl_bh == null) { Class cl_flh = load("com/tencent/mobileqq/app/FriendListHandler"); assert cl_flh != null; cl_bh = cl_flh.getSuperclass(); } //log("bh(" + type + ")=" + ret); Object appInterface = getQQAppInterface(); try { return invoke_virtual(appInterface, "a", type, int.class, cl_bh); } catch (NoSuchMethodException e) { try { Method m = appInterface.getClass().getMethod("getBusinessHandler", int.class); m.setAccessible(true); return m.invoke(appInterface, type); } catch (Exception e2) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { e.addSuppressed(e2); } } throw e; } } catch (Exception e) { log(e); return null; } } /** * NSF: Neither Static nor Final * * @param obj thisObj * @param type Field type * @return the FIRST(as declared seq in dex) field value meeting the type */ @Deprecated public static <T> T getFirstNSFByType(Object obj, Class<T> type) { if (obj == null) throw new NullPointerException("obj == null"); if (type == null) throw new NullPointerException("type == null"); Class clz = obj.getClass(); while (clz != null && !clz.equals(Object.class)) { for (Field f : clz.getDeclaredFields()) { if (!f.getType().equals(type)) continue; int m = f.getModifiers(); if (Modifier.isStatic(m) || Modifier.isFinal(m)) continue; f.setAccessible(true); try { return (T) f.get(obj); } catch (IllegalAccessException ignored) { //should not happen } } clz = clz.getSuperclass(); } return null; } /** * NSF: Neither Static nor Final * * @param clz Obj class * @param type Field type * @return the FIRST(as declared seq in dex) field value meeting the type */ @Deprecated public static Field getFirstNSFFieldByType(Class clz, Class type) { if (clz == null) throw new NullPointerException("clz == null"); if (type == null) throw new NullPointerException("type == null"); while (clz != null && !clz.equals(Object.class)) { for (Field f : clz.getDeclaredFields()) { if (!f.getType().equals(type)) continue; int m = f.getModifiers(); if (Modifier.isStatic(m) || Modifier.isFinal(m)) continue; f.setAccessible(true); return f; } clz = clz.getSuperclass(); } return null; } public static <T> T getObject(Class clazz, Class<?> type, String name, Object obj) { try { Field field = findField(clazz, type, name); return field == null ? null : (T) field.get(obj); } catch (Exception e) { return null; } } public static Field hasField(Object obj, String name) { return hasField(obj, name, null); } public static Field hasField(Object obj, String name, Class type) { if (obj == null) throw new NullPointerException("obj/class == null"); Class clazz; if (obj instanceof Class) clazz = (Class) obj; else clazz = obj.getClass(); return findField(clazz, type, name); } public static Field findField(Class<?> clazz, Class<?> type, String name) { if (clazz != null && name.length() > 0) { Class<?> clz = clazz; do { for (Field field : clz.getDeclaredFields()) { if ((type == null || field.getType().equals(type)) && field.getName() .equals(name)) { field.setAccessible(true); return field; } } } while ((clz = clz.getSuperclass()) != null); //log(String.format("Can't find the field of type: %s and name: %s in class: %s!",type==null?"[any]":type.getName(),name,clazz.getName())); } return null; } public static boolean isEmpty(String str) { return str == null || str.length() == 0; } @Deprecated public static void log(String str) { Log.i("QNdump", str); if (DEBUG) try { XposedBridge.log(str); } catch (NoClassDefFoundError e) { Log.i("Xposed", str); Log.i("EdXposed-Bridge", str); } if (ENABLE_DUMP_LOG) { String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/qn_log.txt"; File f = new File(path); try { if (!f.exists()) f.createNewFile(); appendToFile(path, "[" + System.currentTimeMillis() + "-" + android.os.Process.myPid() + "] " + str + "\n"); } catch (IOException e) { } } } public static void loge(String str) { Log.e("QNdump", str); try { XposedBridge.log(str); } catch (NoClassDefFoundError e) { Log.e("Xposed", str); Log.e("EdXposed-Bridge", str); } if (ENABLE_DUMP_LOG) { String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/qn_log.txt"; File f = new File(path); try { if (!f.exists()) f.createNewFile(); appendToFile(path, "[" + System.currentTimeMillis() + "-" + android.os.Process.myPid() + "] " + str + "\n"); } catch (IOException e) { } } } public static void logd(String str) { Log.d("QNdump", str); if (DEBUG) try { XposedBridge.log(str); } catch (NoClassDefFoundError e) { Log.d("Xposed", str); Log.d("EdXposed-Bridge", str); } if (ENABLE_DUMP_LOG) { String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/qn_log.txt"; File f = new File(path); try { if (!f.exists()) f.createNewFile(); appendToFile(path, "[" + System.currentTimeMillis() + "-" + android.os.Process.myPid() + "] " + str + "\n"); } catch (IOException e) { } } } public static void logi(String str) { Log.i("QNdump", str); try { XposedBridge.log(str); } catch (NoClassDefFoundError e) { Log.i("Xposed", str); Log.i("EdXposed-Bridge", str); } if (ENABLE_DUMP_LOG) { String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/qn_log.txt"; File f = new File(path); try { if (!f.exists()) f.createNewFile(); appendToFile(path, "[" + System.currentTimeMillis() + "-" + android.os.Process.myPid() + "] " + str + "\n"); } catch (IOException e) { } } } public static void log(Throwable th) { if (th == null) return; BugCollector.onThrowable(th); log(Log.getStackTraceString(th)); } public static void checkLogFlag() { try { File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/.qn_log_flag"); if (f.exists()) ENABLE_DUMP_LOG = true; } catch (Exception ignored) { } } /** * 追加文件:使用FileWriter * 不能{@link #log(Throwable)},防止死递归 * * @param fileName * @param content */ public static void appendToFile(String fileName, String content) { FileWriter writer = null; try { writer = new FileWriter(fileName, true); writer.write(content); } catch (IOException e) { e.printStackTrace(); } finally { try { if (writer != null) { writer.close(); } } catch (IOException e) { e.printStackTrace(); } } } public static String en(String str) { if (str == null) return "null"; return "\"" + str.replace("\\", "\\\\").replace("\"", "\\\"") .replace("\n", "\\n").replace("\r", "\\r") + "\""; } public static String de(String str) { if (str == null) return null; if (str.equals("null")) return null; if (str.startsWith("\"")) str = str.substring(1); if (str.endsWith("\"") && !str.endsWith("\\\"")) str = str.substring(0, str.length() - 1); return str.replace("\\\"", "\"").replace("\\\n", "\n") .replace("\\\r", "\r").replace("\\\\", "\\"); } public static String csvenc(String s) { if (!s.contains("\"") && !s.contains(" ") && !s.contains(",") && !s.contains("\r") && !s.contains("\n") && !s.contains("\t")) { return s; } return "\"" + s.replace("\"", "\"\"") + "\""; } private static Method method_Toast_show; private static Method method_Toast_makeText; private static Class clazz_QQToast; public static Toast showToast(Context ctx, int type, CharSequence str, int length) { try { if (clazz_QQToast == null) { clazz_QQToast = load("com/tencent/mobileqq/widget/QQToast"); } if (clazz_QQToast == null) { Class clz = load("com/tencent/mobileqq/activity/aio/doodle/DoodleLayout"); assert clz != null; Field[] fs = clz.getDeclaredFields(); for (Field f : fs) { if (View.class.isAssignableFrom(f.getType())) continue; if (f.getType().isPrimitive()) continue; if (f.getType().isInterface()) continue; clazz_QQToast = f.getType(); } } if (method_Toast_show == null) { Method[] ms = clazz_QQToast.getMethods(); for (Method m : ms) { if (Toast.class.equals(m.getReturnType()) && m.getParameterTypes().length == 0) { method_Toast_show = m; break; } } } if (method_Toast_makeText == null) { try { method_Toast_makeText = clazz_QQToast.getMethod("a", Context.class, int.class, CharSequence.class, int.class); } catch (NoSuchMethodException e) { try { method_Toast_makeText = clazz_QQToast.getMethod("b", Context.class, int.class, CharSequence.class, int.class); } catch (NoSuchMethodException e2) { throw e; } } } Object qqToast = method_Toast_makeText.invoke(null, ctx, type, str, length); return (Toast) method_Toast_show.invoke(qqToast); } catch (Exception e) { log(e); Toast t = Toast.makeText(ctx, str, length); t.show(); return t; } } public static Toast showToastShort(Context ctx, CharSequence str) { return showToast(ctx, 0, str, 0); } @Deprecated public static void showErrorToastAnywhere(final String text) { try { if (Looper.myLooper() == Looper.getMainLooper()) { Utils.showToast(getApplication(), TOAST_TYPE_ERROR, text, Toast.LENGTH_SHORT); } else { SyncUtils.post(new Runnable() { @Override public void run() { Utils.showToast(getApplication(), TOAST_TYPE_ERROR, text, Toast.LENGTH_SHORT); } }); } } catch (Exception e) { log(e); } } public static void dumpTrace() { Throwable t = new Throwable("Trace dump"); log(t); } public static int getLineNo() { return Thread.currentThread().getStackTrace()[3].getLineNumber(); } public static int getLineNo(int depth) { return Thread.currentThread().getStackTrace()[3 + depth].getLineNumber(); } public static String getRelTimeStrSec(long time_sec) { return getRelTimeStrMs(time_sec * 1000); } @SuppressWarnings("deprecation") public static String getRelTimeStrMs(long time_ms) { SimpleDateFormat format; long curr = System.currentTimeMillis(); Date now = new Date(curr); Date t = new Date(time_ms); if (t.getYear() != now.getYear()) { format = new SimpleDateFormat("yyyy/MM/dd HH:mm"); return format.format(t); } if (t.getMonth() == now.getMonth() && t.getDay() == now.getDay()) { format = new SimpleDateFormat("HH:mm:ss"); return format.format(t); } if ((curr - time_ms) / 1000f / 3600f / 24f < 6.0f) { format = new SimpleDateFormat(" HH:mm"); return "星期" + new String[]{"日", "一", "二", "三", "四", "五", "六"}[t.getDay()] + format.format(t); } format = new SimpleDateFormat("MM-dd HH:mm"); return format.format(t); } public static String getIntervalDspMs(long ms1, long ms2) { Date t1 = new Date(Math.min(ms1, ms2)); Date t2 = new Date(Math.max(ms1, ms2)); Date tn = new Date(); SimpleDateFormat format; String ret; switch (difTimeMs(t1, tn)) { case 4: case 3: case 2: format = new SimpleDateFormat("MM-dd HH:mm"); break; case 1: case 0: format = new SimpleDateFormat("HH:mm"); break; default: format = new SimpleDateFormat("yyyy-MM-dd HH:mm"); break; } ret = format.format(t1); switch (difTimeMs(t1, t2)) { case 4: case 3: case 2: format = new SimpleDateFormat("MM-dd HH:mm"); break; case 1: case 0: format = new SimpleDateFormat("HH:mm"); break; default: format = new SimpleDateFormat("yyyy-MM-dd HH:mm"); break; } ret = ret + " 至 " + format.format(t2); return ret; } public static String filterEmoji(String source) { if (source != null) { Pattern emoji = Pattern.compile("[\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]", Pattern.UNICODE_CASE | Pattern.CASE_INSENSITIVE); Matcher emojiMatcher = emoji.matcher(source); if (emojiMatcher.find()) { source = emojiMatcher.replaceAll("\u3000"); return source; } return source; } return null; } /** * same: t0 d1 w2 m3 y4 */ private static int difTimeMs(Date t1, Date t2) { if (t1.getYear() != t2.getYear()) return 5; if (t1.getMonth() != t2.getMonth()) return 4; if (t1.getDate() != t2.getDate()) return 3; if (t1.equals(t2)) return 0; return 1; } public static boolean isNiceUser() { if ((LicenseStatus.getCurrentUserWhiteFlags() & UserFlagConst.WF_NICE_USER) != 0) return true; try { ConfigManager cfg = ConfigManager.getDefaultConfig(); if (cfg.getBooleanOrDefault(ConfigItems.cfg_nice_user, false)) { return true; } if (doEvalNiceUser()) { try { if (SyncUtils.isMainProcess()) { cfg.getAllConfig().put(ConfigItems.cfg_nice_user, true); cfg.save(); } } catch (Throwable e1) { log(e1); } return true; } return false; } catch (Throwable e2) { log(e2); return true; } } private static boolean doEvalNiceUser() { if (!isExp()) return true; long uin = getLongAccountUin(); String nick = getCurrentNickname(); if (nick != null && nick.length() > 0 && isBadNick(nick)) return false; if (Utils.isTim(getApplication())) return true; if (uin > 2_0000_0000L && uin < 30_0000_0000L) { return true; } Class vip = DexKit.loadClassFromCache(DexKit.C_VIP_UTILS); try { if (vip != null) { return "0".equals(invoke_static(vip, "a", getQQAppInterface(), uin + "", load("com/tencent/common/app/AppInterface"), String.class, String.class)); } } catch (Exception e) { log(e); } return true; } private static boolean isSymbol(char c) { if (c == '\u3000') return true; if (c < '0') return true; if (c > '9' && c < 'A') return true; if (c > 'Z' && c < 'a') return true; return (c <= 0xD7FF); } /** * 特征 * A/a+sp/'/^ && lenth>2 * 丶ゞ * 中文.len()<5 && endsWith '.' * char[1] 'emoji' * char[1] 全半角单符号 * IDSP/3000" " */ private static boolean isBadNick(String nick) { if (nick == null) throw new NullPointerException("nick == null"); if (nick.length() == 0) throw new IllegalArgumentException("nick length == 0"); nick = filterEmoji(nick); if (nick.contains("\u4e36") || nick.contains("\u309e") || nick.contains("双封") || nick.contains("群发") || nick.contains("代发") || nick.contains("赚") || nick.contains("换群") || nick.contains("加我") || nick.contains("加盟") || nick.contains("中介") || nick.contains("兼职") || nick.contains("客服") || nick.contains("招聘") || nick.contains("换钱") || nick.contains("接单") || nick.contains("承接") || nick.contains("解封") || nick.contains("保号") || nick.contains("业务") || nick.contains("互拉") || nick.contains("刷单") || nick.contains("代打") || nick.contains("总创") || nick.contains("在线接") || nick.contains("引流") || nick.contains("mzmp") || nick.matches(".*[\u53f8\u6b7b][\u9a6c\u5417\u5988\u3000].*")) return true; if (nick.equalsIgnoreCase("A")) return true; if (nick.length() < 2) { return isSymbol(nick.charAt(0)); } if (nick.matches(".*[Aa][/'`. ,\u2018\u2019\u201a\u201b^_\u309e].*")) { return true; } if (nick.endsWith(".")) { char c = nick.charAt(nick.length() - 2); return c > 0xff; } return false; } /** * 仅仅为群发器而使用本模块的用户往往有两个鲜明的特征 * 1.使用某个虚拟框架 * 2.显而易见的昵称,见{@link #isBadNick(String)} * 仍然提供本模块的全部功能 * 只是隐藏我的联系方式 * 未必是完全正确的方法, but just do it. **/ private static boolean isExp() { try { Object pathList = iget_object_or_null(XposedBridge.class.getClassLoader(), "pathList"); Object[] dexElements = (Object[]) iget_object_or_null(pathList, "dexElements"); for (Object entry : dexElements) { DexFile dexFile = (DexFile) iget_object_or_null(entry, "dexFile"); Enumeration<String> entries = dexFile.entries(); while (entries.hasMoreElements()) { String className = entries.nextElement(); if (className.matches(".+?(epic|weishu).+")) { return true; } } } } catch (Throwable e) { if (!(e instanceof NullPointerException) && !(e instanceof NoClassDefFoundError)) { log(e); } } return false; } /** * 通过view暴力获取getContext()(Android不支持view.getContext()了) * * @param view 要获取context的view * @return 返回一个activity */ public static Context getContext(View view) { Context ctx = null; if (view.getContext().getClass().getName().contains("com.android.internal.policy.DecorContext")) { try { Field field = view.getContext().getClass().getDeclaredField("mPhoneWindow"); field.setAccessible(true); Object obj = field.get(view.getContext()); java.lang.reflect.Method m1 = obj.getClass().getMethod("getContext"); ctx = (Context) (m1.invoke(obj)); } catch (Exception e) { e.printStackTrace(); } } else { ctx = view.getContext(); } return ctx; } public static <T> T dump(T obj) { log("dump:" + obj); return obj; } public static String getShort$Name(Object obj) { String name; if (obj == null) return "null"; if (obj instanceof String) { name = ((String) obj).replace("/", "."); } else if (obj instanceof Class) { name = ((Class) obj).getName(); } else if (obj instanceof Field) { name = ((Field) obj).getType().getName(); } else name = obj.getClass().getName(); if (!name.contains(".")) return name; int p = name.lastIndexOf('.'); return name.substring(p + 1); } public static int sign(double d) { return Double.compare(d, 0d); } public static boolean isTim(Context ctx) { return ctx.getPackageName().equals(PACKAGE_NAME_TIM); } public static ContactDescriptor parseResultRec(Object a) { ContactDescriptor cd = new ContactDescriptor(); cd.uin = iget_object_or_null(a, "a", String.class); cd.nick = iget_object_or_null(a, "b", String.class); cd.uinType = iget_object_or_null(a, "b", int.class); return cd; } public static boolean isAlphaVersion() { return QN_VERSION_NAME.contains("-") || QN_VERSION_NAME.contains("es") || QN_VERSION_NAME.contains("a") || QN_VERSION_NAME.length() > 10; } //FIXME: this may not work properly after obfuscation public static boolean isRecursion() { StackTraceElement[] stacks = new Exception().getStackTrace(); int count = 0; String cname = stacks[1].getClassName(); String mname = stacks[1].getMethodName(); for (int i = 2; i < stacks.length; i++) { if (stacks[i].getClassName().equals(cname) && stacks[i].getMethodName().equals(mname)) { return true; } } return false; } /** * 根据手机的分辨率从 dip 的单位 转成为 px(像素) */ public static int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } public static int dip2sp(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density / context.getResources().getDisplayMetrics().scaledDensity; return (int) (dpValue * scale + 0.5f); } /** * 根据手机的分辨率从 px(像素) 的单位 转成为 dp */ public static int px2dip(Context context, float pxValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } /** * 将px值转换为sp值,保证文字大小不变 */ public static int px2sp(Context context, float pxValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (pxValue / fontScale + 0.5f); } /** * 将sp值转换为px值,保证文字大小不变 */ public static int sp2px(Context context, float spValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } public static Method findMethodByTypes_1(Class<?> clazz, Class returnType, Class... argt) throws NoSuchMethodException { Method method = null; Method[] m; Class[] _argt; loop_main: do { m = clazz.getDeclaredMethods(); loop: for (Method value : m) { _argt = value.getParameterTypes(); if (_argt.length == argt.length) { for (int ii = 0; ii < argt.length; ii++) { if (!argt[ii].equals(_argt[ii])) continue loop; } if (returnType != null && !returnType.equals(value.getReturnType())) continue; if (method == null) { method = value; //here we go through this class } else { throw new NoSuchMethodException("Multiple methods found for __attribute__((any))" + paramsTypesToString(argt) + " in " + clazz.getName()); } } } } while (method == null && !Object.class.equals(clazz = clazz.getSuperclass())); if (method == null) throw new NoSuchMethodException("__attribute__((a))" + paramsTypesToString(argt) + " in " + clazz.getName()); method.setAccessible(true); return method; } public static class DummyCallback implements DialogInterface.OnClickListener { public DummyCallback() { } @Override public void onClick(DialogInterface dialog, int which) { } } public static String en_toStr(Object obj) { if (obj == null) return null; String str; if (obj instanceof CharSequence) str = Utils.en(obj.toString()); else str = "" + obj; return str; } public static String nomorethan100(Object obj) { if (obj == null) return null; String str; if (obj instanceof CharSequence) str = "\"" + obj + "\""; else str = "" + obj; if (str.length() > 110) return str.substring(0, 100); return str; } public static Activity getCurrentActivity() { try { Class activityThreadClass = Class.forName("android.app.ActivityThread"); Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null); Field activitiesField = activityThreadClass.getDeclaredField("mActivities"); activitiesField.setAccessible(true); Map activities = (Map) activitiesField.get(activityThread); for (Object activityRecord : activities.values()) { Class activityRecordClass = activityRecord.getClass(); Field pausedField = activityRecordClass.getDeclaredField("paused"); pausedField.setAccessible(true); if (!pausedField.getBoolean(activityRecord)) { Field activityField = activityRecordClass.getDeclaredField("activity"); activityField.setAccessible(true); return (Activity) activityField.get(activityRecord); } } } catch (Exception e) { log(e); } return null; } public static class ContactDescriptor { public String uin; public int uinType; @Nullable public String nick; public String getId() { StringBuilder msg = new StringBuilder(); if (uin.length() < 10) { for (int i = 0; i < 10 - uin.length(); i++) { msg.append("0"); } } return msg + uin + uinType; } } public static void onStubClassInitialize() { Throwable th = new Throwable("WTF: stub class was initialized!!!"); log(th); } @Nullable public static Object defaultShadowClone(Object orig) { if (orig == null) return null; Class cl = orig.getClass(); Object clone; try { clone = cl.newInstance(); while (cl != null && !cl.equals(Object.class)) { for (Field f : cl.getDeclaredFields()) { f.setAccessible(true); f.set(clone, f.get(orig)); } cl = cl.getSuperclass(); } return clone; } catch (Exception e) { log(e); return null; } } @Deprecated public static int strcmp(String stra, String strb) { int len = Math.min(stra.length(), strb.length()); for (int i = 0; i < len; i++) { char a = stra.charAt(i); char b = strb.charAt(i); if (a != b) { return a - b; } } return stra.length() - strb.length(); } public static void nop() { if (Math.random() > 1) nop(); } public static int[] integerSetToArray(Set<Integer> is) { int[] ret = new int[is.size()]; Iterator<Integer> it = is.iterator(); for (int i = 0; i < ret.length; i++) { if (it.hasNext()) ret[i] = it.next(); } return ret; } }